summaryrefslogtreecommitdiffstats
path: root/callpbx.cpp
diff options
context:
space:
mode:
authorSuper User2007-05-06 15:54:52 +0200
committerSuper User2007-05-06 15:54:52 +0200
commit2ed0fee489c37a6e2d4473f6185ebbe3e746ac11 (patch)
treefcf232bc282c083404cfde0ce5b04236fe202c3e /callpbx.cpp
parentfirst commit (diff)
downloadlcr-2ed0fee489c37a6e2d4473f6185ebbe3e746ac11.tar.gz
lcr-2ed0fee489c37a6e2d4473f6185ebbe3e746ac11.tar.xz
lcr-2ed0fee489c37a6e2d4473f6185ebbe3e746ac11.zip
only for backup, still in coding state - no compile!!!
Diffstat (limited to 'callpbx.cpp')
-rw-r--r--callpbx.cpp993
1 files changed, 993 insertions, 0 deletions
diff --git a/callpbx.cpp b/callpbx.cpp
new file mode 100644
index 0000000..bb644d0
--- /dev/null
+++ b/callpbx.cpp
@@ -0,0 +1,993 @@
+/*****************************************************************************\
+** **
+** PBX4Linux **
+** **
+**---------------------------------------------------------------------------**
+** Copyright: Andreas Eversberg **
+** **
+** call functions **
+** **
+\*****************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <poll.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "main.h"
+#define __u8 unsigned char
+#define __u16 unsigned short
+#define __u32 unsigned long
+#include "linux/isdnif.h"
+
+
+/* notify endpoint about state change (if any) */
+static int notify_state_change(int call_id, int epoint_id, int old_state, int new_state)
+{
+ int notify_off = 0, notify_on = 0;
+ struct message *message;
+
+ if (old_state == new_state)
+ return(old_state);
+
+ switch(old_state)
+ {
+ case NOTIFY_STATE_ACTIVE:
+ switch(new_state)
+ {
+ case NOTIFY_STATE_HOLD:
+ notify_on = INFO_NOTIFY_REMOTE_HOLD;
+ break;
+ case NOTIFY_STATE_SUSPEND:
+ notify_on = INFO_NOTIFY_USER_SUSPENDED;
+ break;
+ case NOTIFY_STATE_CONFERENCE:
+ notify_on = INFO_NOTIFY_CONFERENCE_ESTABLISHED;
+ break;
+ }
+ break;
+
+ case NOTIFY_STATE_HOLD:
+ switch(new_state)
+ {
+ case NOTIFY_STATE_ACTIVE:
+ notify_off = INFO_NOTIFY_REMOTE_RETRIEVAL;
+ break;
+ case NOTIFY_STATE_SUSPEND:
+ notify_off = INFO_NOTIFY_REMOTE_RETRIEVAL;
+ notify_on = INFO_NOTIFY_USER_SUSPENDED;
+ break;
+ case NOTIFY_STATE_CONFERENCE:
+ notify_off = INFO_NOTIFY_REMOTE_RETRIEVAL;
+ notify_on = INFO_NOTIFY_CONFERENCE_ESTABLISHED;
+ break;
+ }
+ break;
+
+ case NOTIFY_STATE_SUSPEND:
+ switch(new_state)
+ {
+ case NOTIFY_STATE_ACTIVE:
+ notify_off = INFO_NOTIFY_USER_RESUMED;
+ break;
+ case NOTIFY_STATE_HOLD:
+ notify_off = INFO_NOTIFY_USER_RESUMED;
+ notify_on = INFO_NOTIFY_REMOTE_HOLD;
+ break;
+ case NOTIFY_STATE_CONFERENCE:
+ notify_off = INFO_NOTIFY_USER_RESUMED;
+ notify_on = INFO_NOTIFY_CONFERENCE_ESTABLISHED;
+ break;
+ }
+ break;
+
+ case NOTIFY_STATE_CONFERENCE:
+ switch(new_state)
+ {
+ case NOTIFY_STATE_ACTIVE:
+ notify_off = INFO_NOTIFY_CONFERENCE_DISCONNECTED;
+ break;
+ case NOTIFY_STATE_HOLD:
+ notify_off = INFO_NOTIFY_CONFERENCE_DISCONNECTED;
+ notify_on = INFO_NOTIFY_REMOTE_HOLD;
+ break;
+ case NOTIFY_STATE_SUSPEND:
+ notify_off = INFO_NOTIFY_CONFERENCE_DISCONNECTED;
+ notify_on = INFO_NOTIFY_USER_SUSPENDED;
+ break;
+ }
+ break;
+ }
+
+ if (call_id && notify_off)
+ {
+ message = message_create(call_id, epoint_id, CALL_TO_EPOINT, MESSAGE_NOTIFY);
+ message->param.notifyinfo.notify = notify_off;
+ message_put(message);
+ }
+
+ if (call_id && notify_on)
+ {
+ message = message_create(call_id, epoint_id, CALL_TO_EPOINT, MESSAGE_NOTIFY);
+ message->param.notifyinfo.notify = notify_on;
+ message_put(message);
+ }
+
+ return(new_state);
+}
+
+
+/* debug function for call */
+void callpbx_debug(class CallPBX *callpbx, char *function)
+{
+ struct call_relation *relation;
+ struct port_list *portlist;
+ class Endpoint *epoint;
+ class Port *port;
+ char buffer[512];
+
+ if (!(options.deb & DEBUG_CALL))
+ return;
+
+ PDEBUG(DEBUG_CALL, "CALL(%d) start (called from %s)\n", callpbx->c_serial, function);
+
+ relation = callpbx->c_relation;
+
+ if (!relation)
+ PDEBUG(DEBUG_CALL, "call has no relations\n");
+ while(relation)
+ {
+ epoint = find_epoint_id(relation->epoint_id);
+ if (!epoint)
+ {
+ PDEBUG(DEBUG_CALL, "warning: relations epoint id=%ld doesn't exists!\n", relation->epoint_id);
+ relation = relation->next;
+ continue;
+ }
+ buffer[0] = '\0';
+ UPRINT(strchr(buffer,0), "*** ep%ld", relation->epoint_id);
+ UPRINT(strchr(buffer,0), " ifs=");
+ portlist = epoint->ep_portlist;
+ while(portlist)
+ {
+ port = find_port_id(portlist->port_id);
+ if (port)
+ UPRINT(strchr(buffer,0), "%s,", port->p_name);
+ else
+ UPRINT(strchr(buffer,0), "<port %ld doesn't exist>,", portlist->port_id);
+ portlist = portlist->next;
+ }
+// UPRINT(strchr(buffer,0), " endpoint=%d on=%s hold=%s", epoint->ep_serial, (epoint->ep_call_id==callpbx->c_serial)?"yes":"no", (epoint->get_hold_id()==callpbx->c_serial)?"yes":"no");
+ UPRINT(strchr(buffer,0), " endpoint=%d on=%s", epoint->ep_serial, (epoint->ep_call_id==callpbx->c_serial)?"yes":"no");
+ switch(relation->type)
+ {
+ case RELATION_TYPE_CALLING:
+ UPRINT(strchr(buffer,0), " type=CALLING");
+ break;
+ case RELATION_TYPE_SETUP:
+ UPRINT(strchr(buffer,0), " type=SETUP");
+ break;
+ case RELATION_TYPE_CONNECT:
+ UPRINT(strchr(buffer,0), " type=CONNECT");
+ break;
+ default:
+ UPRINT(strchr(buffer,0), " type=unknown");
+ break;
+ }
+ switch(relation->channel_state)
+ {
+ case CHANNEL_STATE_CONNECT:
+ UPRINT(strchr(buffer,0), " channel=CONNECT");
+ break;
+ case CHANNEL_STATE_HOLD:
+ UPRINT(strchr(buffer,0), " channel=HOLD");
+ break;
+ default:
+ UPRINT(strchr(buffer,0), " channel=unknown");
+ break;
+ }
+ switch(relation->tx_state)
+ {
+ case NOTIFY_STATE_ACTIVE:
+ UPRINT(strchr(buffer,0), " tx_state=ACTIVE");
+ break;
+ case NOTIFY_STATE_HOLD:
+ UPRINT(strchr(buffer,0), " tx_state=HOLD");
+ break;
+ case NOTIFY_STATE_SUSPEND:
+ UPRINT(strchr(buffer,0), " tx_state=SUSPEND");
+ break;
+ case NOTIFY_STATE_CONFERENCE:
+ UPRINT(strchr(buffer,0), " tx_state=CONFERENCE");
+ break;
+ default:
+ UPRINT(strchr(buffer,0), " tx_state=unknown");
+ break;
+ }
+ switch(relation->rx_state)
+ {
+ case NOTIFY_STATE_ACTIVE:
+ UPRINT(strchr(buffer,0), " rx_state=ACTIVE");
+ break;
+ case NOTIFY_STATE_HOLD:
+ UPRINT(strchr(buffer,0), " rx_state=HOLD");
+ break;
+ case NOTIFY_STATE_SUSPEND:
+ UPRINT(strchr(buffer,0), " rx_state=SUSPEND");
+ break;
+ case NOTIFY_STATE_CONFERENCE:
+ UPRINT(strchr(buffer,0), " rx_state=CONFERENCE");
+ break;
+ default:
+ UPRINT(strchr(buffer,0), " rx_state=unknown");
+ break;
+ }
+ PDEBUG(DEBUG_CALL, "%s\n", buffer);
+ relation = relation->next;
+ }
+
+ PDEBUG(DEBUG_CALL, "end\n");
+}
+
+
+/*
+ * constructor for a new call
+ * the call will have a relation to the calling endpoint
+ */
+CallPBX::CallPBX(class Endpoint *epoint) : Call(epoint)
+{
+ struct call_relation *relation;
+// char filename[256];
+
+ if (!epoint)
+ {
+ PERROR("software error, epoint is NULL.\n");
+ exit(-1);
+ }
+
+ PDEBUG(DEBUG_CALL, "creating new call and connecting it to the endpoint.\n");
+
+ c_type = CALL_TYPE_PBX;
+ c_caller[0] = '\0';
+ c_caller_id[0] = '\0';
+ c_dialed[0] = '\0';
+ c_todial[0] = '\0';
+ c_mixer = 0;
+ c_partyline = 0;
+
+ /* initialize a relation only to the calling interface */
+ relation = c_relation = (struct call_relation *)calloc(1, sizeof(struct call_relation));
+ if (!relation)
+ {
+ PERROR("no memory, exitting..\n");
+ exit(-1);
+ }
+ cmemuse++;
+ memset(relation, 0, sizeof(struct call_relation));
+ relation->type = RELATION_TYPE_CALLING;
+ relation->channel_state = CHANNEL_STATE_HOLD; /* audio is assumed on a new call */
+ relation->tx_state = NOTIFY_STATE_ACTIVE; /* new calls always assumed to be active */
+ relation->rx_state = NOTIFY_STATE_ACTIVE; /* new calls always assumed to be active */
+ relation->epoint_id = epoint->ep_serial;
+
+
+ if (options.deb & DEBUG_CALL)
+ callpbx_debug(this, "CallPBX::Constructor(new call)");
+}
+
+
+/*
+ * call descructor
+ */
+CallPBX::~CallPBX()
+{
+ struct call_relation *relation, *rtemp;
+
+ relation = c_relation;
+ while(relation)
+ {
+ rtemp = relation->next;
+ memset(relation, 0, sizeof(struct call_relation));
+ free(relation);
+ cmemuse--;
+ relation = rtemp;
+ }
+}
+
+
+/* mixer sets the mixer of hisax bchannels
+ * the mixer() will set the mixer for the hisax ports which is done
+ * at kernel space.
+ */
+void CallPBX::mixer(void)
+{
+ struct call_relation *relation;
+ struct message *message;
+ int numconnect, relations;
+ class Endpoint *epoint;
+ struct port_list *portlist;
+ class Port *port;
+ int nodata = 1;
+
+ relation = c_relation;
+ while(relation)
+ {
+ epoint = find_epoint_id(relation->epoint_id);
+ if (!epoint)
+ {
+ PERROR("software error: relation without existing endpoints.\n");
+ relation = relation->next;
+ continue;
+ }
+ portlist = epoint->ep_portlist;
+ if (!portlist)
+ {
+ PDEBUG((DEBUG_CALL|DEBUG_PORT), "ignoring relation without interfaces.\n");
+//#warning testing: keep on hold until single audio stream available
+ relation->channel_state = CHANNEL_STATE_HOLD;
+ relation = relation->next;
+ continue;
+ }
+ if (portlist->next)
+ {
+ PDEBUG((DEBUG_CALL|DEBUG_PORT), "ignoring relation with ep%d due to port_list.\n", epoint->ep_serial);
+//#warning testing: keep on hold until single audio stream available
+ relation->channel_state = CHANNEL_STATE_HOLD;
+ relation = relation->next;
+ continue;
+ }
+ port = find_port_id(portlist->port_id);
+ if (!port)
+ {
+ PDEBUG((DEBUG_CALL|DEBUG_PORT), "software error: relation without existing port.\n");
+ relation = relation->next;
+ continue;
+ }
+ if (port->p_record)
+ {
+ PDEBUG(DEBUG_CALL|DEBUG_PORT, "mixer(): relation ep%d does recording, so we must get data from all members.\n", epoint->ep_serial);
+ if (nodata)
+ {
+ PDEBUG(DEBUG_CALL|DEBUG_PORT, "mixer(): at least one endpoint wants data.\n");
+ nodata = 0;
+ }
+ }
+ if ((port->p_type&PORT_CLASS_MASK)!=PORT_CLASS_mISDN)
+ {
+ PDEBUG((DEBUG_CALL|DEBUG_PORT), "ignoring relation ep%d because it is not mISDN.\n", epoint->ep_serial);
+ if (nodata)
+ {
+ PDEBUG((DEBUG_CALL|DEBUG_PORT), "not all endpoints are mISDN.\n");
+ nodata = 0;
+ }
+ relation = relation->next;
+ continue;
+ }
+// remove unconnected parties from conference, also remove remotely disconnected parties so conference will not be disturbed.
+ if (relation->channel_state == CHANNEL_STATE_CONNECT
+ && relation->rx_state != NOTIFY_STATE_HOLD
+ && relation->rx_state != NOTIFY_STATE_SUSPEND)
+ {
+ message = message_create(c_serial, relation->epoint_id, CALL_TO_EPOINT, MESSAGE_mISDNSIGNAL);
+ message->param.mISDNsignal.message = mISDNSIGNAL_CONF;
+ message->param.mISDNsignal.conf = (c_serial<<1) + 1;
+ PDEBUG(DEBUG_CALL, "%s +on+ id: 0x%08x\n", port->p_name, message->param.mISDNsignal.conf);
+ message_put(message);
+ } else
+ {
+ message = message_create(c_serial, relation->epoint_id, CALL_TO_EPOINT, MESSAGE_mISDNSIGNAL);
+ message->param.mISDNsignal.message = mISDNSIGNAL_CONF;
+ message->param.mISDNsignal.conf = 0;
+ PDEBUG(DEBUG_CALL, "%s +off+ id: 0x%08x\n", port->p_name, message->param.mISDNsignal.conf);
+ message_put(message);
+ }
+ relation = relation->next;
+ }
+ /* we notify all relations about rxdata. */
+ relation = c_relation;
+ while(relation)
+ {
+ message = message_create(c_serial, relation->epoint_id, CALL_TO_EPOINT, MESSAGE_mISDNSIGNAL);
+ message->param.mISDNsignal.message = mISDNSIGNAL_NODATA;
+ message->param.mISDNsignal.nodata = nodata;
+ PDEBUG(DEBUG_CALL, "call %d sets alldata on port %s to %d\n", c_serial, port->p_name, nodata);
+ message_put(message);
+ relation = relation->next;
+ }
+
+ /* count relations and states */
+ relation = c_relation;
+ numconnect = 0;
+ relations = 0;
+ while(relation) /* count audio-connected and active relations */
+ {
+ relations ++;
+ if ((relation->channel_state == CHANNEL_STATE_CONNECT)
+ && (relation->rx_state != NOTIFY_STATE_SUSPEND)
+ && (relation->rx_state != NOTIFY_STATE_HOLD))
+ numconnect ++;
+ relation = relation->next;
+ }
+
+ if (relations==2 && !c_partyline) /* two people just exchange their states */
+ {
+ relation = c_relation;
+ relation->tx_state = notify_state_change(c_serial, relation->epoint_id, relation->tx_state, relation->next->rx_state);
+ relation->next->tx_state = notify_state_change(c_serial, relation->next->epoint_id, relation->next->tx_state, relation->rx_state);
+ } else
+ if ((relations==1 || numconnect==1) /*&& !c_partyline*/) /* one member in a call, so we put her on hold */
+ {
+ relation = c_relation;
+ while(relation)
+ {
+ if ((relation->channel_state == CHANNEL_STATE_CONNECT)
+ && (relation->rx_state != NOTIFY_STATE_SUSPEND)
+ && (relation->rx_state != NOTIFY_STATE_HOLD))
+ relation->tx_state = notify_state_change(c_serial, relation->epoint_id, relation->tx_state, NOTIFY_STATE_HOLD);
+ relation = relation->next;
+ }
+ } else
+ /* if conference/partyline or (more than two members and more than one is connected), so we set conference state */
+ {
+ relation = c_relation;
+ while(relation)
+ {
+ if ((relation->channel_state == CHANNEL_STATE_CONNECT)
+ && (relation->rx_state != NOTIFY_STATE_SUSPEND)
+ && (relation->rx_state != NOTIFY_STATE_HOLD))
+ relation->tx_state = notify_state_change(c_serial, relation->epoint_id, relation->tx_state, NOTIFY_STATE_CONFERENCE);
+ relation = relation->next;
+ }
+ }
+}
+
+
+/* send audio data to endpoints which do not come from an endpoint connected
+ * to an isdn port and do not go to an endpoint which is connected to an
+ * isdn port. in this case the mixing cannot be done with kernel space
+ */
+void CallPBX::call_mixer(unsigned long epoint_from, struct call_relation *relation_from, union parameter *param)
+{
+ struct call_relation *relation_to;
+ struct message *message;
+
+ /* skip if source endpoint has NOT audio mode CONNECT */
+ if (relation_from->channel_state != CHANNEL_STATE_CONNECT)
+ {
+ return;
+ }
+
+ /* loop all endpoints and skip the endpoint where the audio is from
+ * so we do not get a loop (echo)
+ */
+ relation_to = c_relation;
+ while(relation_to)
+ {
+ /* skip source endpoint */
+ if (relation_to->epoint_id == epoint_from)
+ {
+ relation_to = relation_to->next;
+ continue;
+ }
+
+ /* skip if destination endpoint has audio mode HOLD */
+ if (relation_to->channel_state != CHANNEL_STATE_CONNECT)
+ {
+ relation_to = relation_to->next;
+ continue;
+ }
+
+ /* now we may send our data to the endpoint where it
+ * will be delivered to the port
+ */
+//PDEBUG(DEBUG_CALL, "mixing from %d to %d\n", epoint_from, relation_to->epoint_id);
+ message = message_create(c_serial, relation_to->epoint_id, CALL_TO_EPOINT, MESSAGE_DATA);
+ memcpy(&message->param, param, sizeof(union parameter));
+ message_put(message);
+
+ relation_to = relation_to->next;
+ }
+}
+
+
+/* release call from endpoint
+ * if the call has two relations, all relations are freed and the call will be
+ * destroyed
+ */
+void CallPBX::release(unsigned long epoint_id, int hold, int location, int cause)
+{
+ struct call_relation *relation, **relationpointer;
+ struct message *message;
+
+ class Call *call;
+
+ if (!epoint_id)
+ {
+ PERROR("software error, epoint is NULL.\n");
+ return;
+ }
+
+ if (options.deb & DEBUG_CALL)
+ callpbx_debug(this, "call_release{before}");
+
+ /* find relation */
+ relation = c_relation;
+ while(relation)
+ {
+ if (relation->epoint_id == epoint_id)
+ break;
+ relation = relation->next;
+ }
+ if (!relation)
+ {
+ PERROR("software error, epoint has a call with no relation to the epoint.\n");
+ return;
+ }
+
+ /* remove from mixer */
+ if (relation->channel_state != CHANNEL_STATE_HOLD)
+ {
+ relation->channel_state = CHANNEL_STATE_HOLD;
+ c_mixer = 1; /* update mixer flag */
+ }
+
+ /* detach given interface */
+ relation = c_relation;
+ relationpointer = &c_relation;
+ while(relation)
+ {
+ /* endpoint of function call */
+ if (relation->epoint_id == epoint_id)
+ {
+ *relationpointer = relation->next;
+ memset(relation, 0, sizeof(struct call_relation));
+ free(relation);
+ cmemuse--;
+ relation = *relationpointer;
+ continue;
+ }
+ relationpointer = &relation->next;
+ relation = relation->next;
+ }
+
+ /* if no more relation */
+ if (!c_relation)
+ {
+ PDEBUG(DEBUG_CALL, "call is completely removed.\n");
+ /* there is no more endpoint related to the call */
+ delete this;
+ // end of call object!
+ PDEBUG(DEBUG_CALL, "call completely removed!\n");
+ } else
+ /* if call is a party line */
+ if (c_partyline)
+ {
+ PDEBUG(DEBUG_CALL, "call is a conference room, so we keep it alive until the last party left.\n");
+ } else
+ /* if only one relation left */
+ if (!c_relation->next)
+ {
+ PDEBUG(DEBUG_CALL, "call has one relation left, so we send it a release with the given cause %d.\n", cause);
+ message = message_create(c_serial, c_relation->epoint_id, CALL_TO_EPOINT, MESSAGE_RELEASE);
+ message->param.disconnectinfo.cause = cause;
+ message->param.disconnectinfo.location = location;
+ message_put(message);
+ delete this;
+ // end of call object!
+ PDEBUG(DEBUG_CALL, "call completely removed!\n");
+ }
+
+ call = call_first;
+ while(call)
+ {
+ if (options.deb & DEBUG_CALL && call->c_type==CALL_TYPE_PBX)
+ callpbx_debug((class CallPBX *)call, "call_release{all calls left}");
+ call = call->next;
+ }
+ PDEBUG(DEBUG_CALL, "call_release(): ended.\n");
+}
+
+/* count number of relations in a call
+ */
+int callpbx_countrelations(unsigned long call_id)
+{
+ struct call_relation *relation;
+ int i;
+ class Call *call;
+ class CallPBX *callpbx;
+
+ call = find_call_id(call_id);
+
+ if (!call)
+ return(0);
+
+ if (call->c_type != CALL_TYPE_PBX)
+ return(0);
+ callpbx = (class CallPBX *)call;
+
+ i = 0;
+ relation = callpbx->c_relation;
+ while(relation)
+ {
+ i++;
+ relation = relation->next;
+ }
+
+ return(i);
+}
+
+void CallPBX::remove_relation(struct call_relation *relation)
+{
+ struct call_relation *temp, **tempp;
+
+ if (!relation)
+ return;
+
+ temp = c_relation;
+ tempp = &c_relation;
+ while(temp)
+ {
+ if (temp == relation)
+ break;
+ tempp = &temp->next;
+ temp = temp->next;
+ }
+ if (!temp)
+ {
+ PERROR("relation not in call.\n");
+ return;
+ }
+
+ PDEBUG(DEBUG_CALL, "removing relation.\n");
+ *tempp = relation->next;
+ memset(temp, 0, sizeof(struct call_relation));
+ free(temp);
+ cmemuse--;
+}
+
+
+struct call_relation *CallPBX::add_relation(void)
+{
+ struct call_relation *relation;
+
+ if (!c_relation)
+ {
+ PERROR("there is no first relation to this call\n");
+ return(NULL);
+ }
+ relation = c_relation;
+ while(relation->next)
+ relation = relation->next;
+
+ relation->next = (struct call_relation *)calloc(1, sizeof(struct call_relation));
+ if (!relation->next)
+ {
+ PERROR("no memory\n");
+ return(NULL);
+ }
+ cmemuse++;
+ memset(relation->next, 0, sizeof(struct call_relation));
+ /* the record pointer is set at the first time the data is received for the relation */
+
+// if (options.deb & DEBUG_CALL)
+// callpbx_debug(call, "add_relation");
+ return(relation->next);
+}
+
+/* epoint sends a message to a call
+ *
+ */
+void CallPBX::message_epoint(unsigned long epoint_id, int message_type, union parameter *param)
+{
+ class Call *cl;
+ struct call_relation *relation, *rel;
+ int num;
+ int new_state;
+ struct message *message;
+// int size, writesize, oldpointer;
+ class Endpoint *epoint;
+ char *number;
+
+ if (!epoint_id)
+ {
+ PERROR("software error, epoint == NULL\n");
+ return;
+ }
+
+// if (options.deb & DEBUG_CALL)
+// {
+// PDEBUG(DEBUG_CALL, "message %d received from ep%d.\n", message, epoint->ep_serial);
+// callpbx_debug(call,"Call::message_epoint");
+// }
+ if (options.deb & DEBUG_CALL)
+ {
+ if (message_type != MESSAGE_DATA)
+ {
+ cl = call_first;
+ while(cl)
+ {
+ if (cl->c_type == CALL_TYPE_PBX)
+ callpbx_debug((class CallPBX *)cl, "Call::message_epoint{all calls before processing}");
+ cl = cl->next;
+ }
+ }
+ }
+
+ /* check relation */
+ relation = c_relation;
+ while(relation)
+ {
+ if (relation->epoint_id == epoint_id)
+ break;
+ relation = relation->next;
+ }
+ if (!relation)
+ {
+// PERROR("no relation back to the endpoint found, ignoring (call=%d, endpoint=%d\n", c_serial, epoint_id);
+ return;
+ }
+
+ switch(message_type)
+ {
+ /* process channel message */
+ case MESSAGE_CHANNEL:
+ PDEBUG(DEBUG_CALL, "call received channel message: %d.\n", param->channel);
+ if (relation->channel_state != param->channel)
+ {
+ relation->channel_state = param->channel;
+ c_mixer = 1; /* update mixer flag */
+ if (options.deb & DEBUG_CALL)
+ callpbx_debug(this, "Call::message_epoint{after setting new channel state}");
+ }
+ return;
+
+ /* track notify */
+ case MESSAGE_NOTIFY:
+ switch(param->notifyinfo.notify)
+ {
+ case INFO_NOTIFY_USER_SUSPENDED:
+ case INFO_NOTIFY_USER_RESUMED:
+ case INFO_NOTIFY_REMOTE_HOLD:
+ case INFO_NOTIFY_REMOTE_RETRIEVAL:
+ case INFO_NOTIFY_CONFERENCE_ESTABLISHED:
+ case INFO_NOTIFY_CONFERENCE_DISCONNECTED:
+ new_state = track_notify(relation->rx_state, param->notifyinfo.notify);
+ if (new_state != relation->rx_state)
+ {
+ relation->rx_state = new_state;
+ c_mixer = 1;
+ if (options.deb & DEBUG_CALL)
+ callpbx_debug(this, "Call::message_epoint{after setting new rx state}");
+ }
+ break;
+
+ default:
+ /* send notification to all other endpoints */
+ rel = c_relation;
+ while(rel)
+ {
+ if (rel->epoint_id!=epoint_id && rel->epoint_id)
+ {
+ message = message_create(c_serial, rel->epoint_id, CALL_TO_EPOINT, MESSAGE_NOTIFY);
+ memcpy(&message->param, param, sizeof(union parameter));
+ message_put(message);
+ }
+ rel = rel->next;
+ }
+ }
+ return;
+
+ /* audio data */
+ case MESSAGE_DATA:
+ /* now send audio data to all endpoints connected */
+ call_mixer(epoint_id, relation, param);
+ return;
+ }
+
+ /* process party line */
+ if (message_type == MESSAGE_SETUP) if (param->setup.partyline)
+ {
+ PDEBUG(DEBUG_CALL, "respsone with connect in partyline mode.\n");
+ c_partyline = param->setup.partyline;
+ message = message_create(c_serial, epoint_id, CALL_TO_EPOINT, MESSAGE_CONNECT);
+ message->param.setup.partyline = c_partyline;
+ message_put(message);
+ c_mixer = 1; /* update mixer flag */
+ }
+ if (c_partyline)
+ {
+ if (message_type == MESSAGE_DISCONNECT)
+ {
+ PDEBUG(DEBUG_CALL, "releasing after receiving disconnect, because call in partyline mode.\n");
+// remove_relation(relation);
+ message = message_create(c_serial, epoint_id, CALL_TO_EPOINT, MESSAGE_RELEASE);
+ message->param.disconnectinfo.cause = CAUSE_NORMAL;
+ message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
+ message_put(message);
+// c_mixer = 1; /* update mixer flag */
+ return;
+ }
+ }
+ if (c_partyline)
+ {
+ PDEBUG(DEBUG_CALL, "ignoring message, because call in partyline mode.\n");
+ return;
+ }
+
+ /* count relations */
+ num=callpbx_countrelations(c_serial);
+
+ /* check number of relations */
+ if (num > 2)
+ {
+ PDEBUG(DEBUG_CALL, "call has more than two relations so there is no need to send a message.\n");
+ return;
+ }
+
+ /* find interfaces not related to calling epoint */
+ relation = c_relation;
+ while(relation)
+ {
+ if (relation->epoint_id != epoint_id)
+ break;
+ relation = relation->next;
+ }
+ if (!relation)
+ {
+ switch(message_type)
+ {
+ case MESSAGE_SETUP:
+ if (param->dialinginfo.itype == INFO_ITYPE_ISDN_EXTENSION)
+ {
+ while(number = strsep(&param->dialinginfo.number, ','))
+ {
+ if (out_setup(epoint_id, message_type, param, number))
+ return; // call destroyed
+ }
+ break;
+ }
+ if (out_setup(epoint_id, message_type, param, NULL))
+ return; // call destroyed
+ break;
+
+ default:
+ PDEBUG(DEBUG_CALL, "no need to send a message because there is no other endpoint than the calling one.\n");
+ }
+ } else
+ {
+ PDEBUG(DEBUG_CALL, "sending message ep%ld -> ep%ld.\n", epoint_id, relation->epoint_id);
+ message = message_create(c_serial, relation->epoint_id, CALL_TO_EPOINT, message_type);
+ memcpy(&message->param, param, sizeof(union parameter));
+ message_put(message);
+ PDEBUG(DEBUG_CALL, "message sent.\n");
+ }
+}
+
+
+/* call process is called from the main loop
+ * it processes the current calling state.
+ * returns 0 if call nothing was done
+ */
+int CallPBX::handler(void)
+{
+// struct call_relation *relation;
+// char dialing[32][32];
+// int port[32];
+// int found;
+// int i, j;
+// char *p;
+
+ /* the mixer must be updated */
+ if (c_mixer)
+ {
+ mixer();
+ c_mixer = 0;
+ return(1);
+ }
+
+ return(0);
+}
+
+
+int track_notify(int oldstate, int notify)
+{
+ int newstate = oldstate;
+
+ switch(notify)
+ {
+ case INFO_NOTIFY_USER_RESUMED:
+ case INFO_NOTIFY_REMOTE_RETRIEVAL:
+ case INFO_NOTIFY_CONFERENCE_DISCONNECTED:
+ case INFO_NOTIFY_RESERVED_CT_1:
+ case INFO_NOTIFY_RESERVED_CT_2:
+ case INFO_NOTIFY_CALL_IS_DIVERTING:
+ newstate = NOTIFY_STATE_ACTIVE;
+ break;
+
+ case INFO_NOTIFY_USER_SUSPENDED:
+ newstate = NOTIFY_STATE_SUSPEND;
+ break;
+
+ case INFO_NOTIFY_REMOTE_HOLD:
+ newstate = NOTIFY_STATE_HOLD;
+ break;
+
+ case INFO_NOTIFY_CONFERENCE_ESTABLISHED:
+ newstate = NOTIFY_STATE_CONFERENCE;
+ break;
+ }
+
+ return(newstate);
+}
+
+
+/*
+ * setup to exactly one endpoint
+ * if it fails, the calling endpoint is released.
+ * if other outgoing endpoints already exists, they are release as well.
+ * note: if this functions fails, it will destroy its own call object!
+ */
+int CallPBX::out_setup(unsigned long epoint_id, int message_type, union parameter *param, char *newnumber)
+{
+ struct call_relation *relation;
+ struct message *message;
+ class Endpoint *epoint;
+
+ PDEBUG(DEBUG_CALL, "no endpoint found, so we will create an endpoint and send the setup message we have.\n");
+ /* create a new relation */
+ if (!(relation=add_relation()))
+ {
+ /* release due to error */
+ ressource_error:
+ relation = c_relation;
+ while(relation)
+ {
+ message = message_create(c_serial, releation->epoint_id, CALL_TO_EPOINT, MESSAGE_RELEASE);
+ message->param.disconnectinfo.cause = (relation->epoint_id==epoint_id)CAUSE_RESSOURCEUNAVAIL?:CAUSE_NORMAL;
+ message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
+ message_put(message);
+ relation = relation->next;
+ }
+ delete this;
+ return(-ENOMEM);
+ }
+ relation->type = RELATION_TYPE_SETUP;
+ relation->channel_state = CHANNEL_STATE_HOLD; /* audio is assumed on a new call */
+ relation->tx_state = NOTIFY_STATE_ACTIVE; /* new calls always assumed to be active */
+ relation->rx_state = NOTIFY_STATE_ACTIVE; /* new calls always assumed to be active */
+ /* create a new endpoint */
+ epoint = new Endpoint(0, c_serial);
+ if (!epoint)
+ {
+ remove_relation(relation);
+ goto ressource_error;
+ }
+ if (!(epoint->ep_app = new DEFAULT_ENDPOINT_APP(epoint)))
+ {
+ PERROR("ERROR:No endpoint's app.\n");
+ delete epoint;
+ remove_relation(relation);
+ goto ressource_error;
+ }
+ relation->epoint_id = epoint->ep_serial;
+ /* send setup message to new endpoint */
+//printf("JOLLY DEBUG: %d\n",call_countrelations(c_serial));
+//i if (options.deb & DEBUG_CALL)
+// callpbx_debug(call, "Call::message_epoint");
+ message = message_create(c_serial, relation->epoint_id, CALL_TO_EPOINT, message_type);
+ memcpy(&message->param, param, sizeof(union parameter));
+ if (newnumber)
+ SCPY(message->param.setup.dialinginfo.number, newnumber);
+ PDEBUG(DEBUG_CALL, "setup message sent to ep %d with number='%s'.\n", relation->epoint_id, message->param.setup.dialinginfo.number);
+ message_put(message);
+ return(0);
+}
+
+todo: beim release von einem relation_type_setup muss der cause gesammelt werden, bis keine weitere setup-relation mehr existiert
+beim letzten den collected cause senden
+mixer kann ruhig loslegen, das aber dokumentieren
+mixer überdenken: wer sendet, welche töne verfügbar sind, u.s.w
+