Implement 3PTY bridge of two 'join's.
[lcr.git] / joinpbx.cpp
index af070b1..6e995f3 100644 (file)
@@ -202,6 +202,7 @@ void joinpbx_debug(class JoinPBX *joinpbx, const char *function)
        PDEBUG(DEBUG_JOIN, "end\n");
 }
 
+int update_bridge(struct lcr_work *work, void *instance, int index);
 
 /*
  * constructor for a new join 
@@ -223,11 +224,13 @@ JoinPBX::JoinPBX(class Endpoint *epoint) : Join()
        j_dialed[0] = '\0';
        j_todial[0] = '\0';
        j_pid = getpid();
-       j_updatebridge = 0;
        j_partyline = 0;
        j_partyline_jingle = 0;
+       j_3pty = 0;
        j_multicause = 0;
        j_multilocation = 0;
+       memset(&j_updatebridge, 0, sizeof(j_updatebridge));
+       add_work(&j_updatebridge, update_bridge, this, 0);
 
        /* initialize a relation only to the calling interface */
        relation = j_relation = (struct join_relation *)MALLOC(sizeof(struct join_relation));
@@ -258,12 +261,36 @@ JoinPBX::~JoinPBX()
                cmemuse--;
                relation = rtemp;
        }
+
+       /* remove 3PTY from other join */
+       if (j_3pty) {
+               class Join *join;
+               class JoinPBX *joinpbx;
+
+               join = find_join_id(j_3pty);
+               if (join && join->j_type == JOIN_TYPE_PBX) {
+                       joinpbx = (class JoinPBX *)join;
+                       joinpbx->j_3pty = 0;
+                       trigger_work(&joinpbx->j_updatebridge);
+               }
+       }
+
+       del_work(&j_updatebridge);
 }
 
 
 /* bridge sets the audio flow of all bchannels assiociated to 'this' join
  * also it changes and notifies active/hold/conference states
  */
+int update_bridge(struct lcr_work *work, void *instance, int index)
+{
+        class JoinPBX *joinpbx = (class JoinPBX *)instance;
+
+        joinpbx->bridge();
+
+        return 0;
+}
+
 void JoinPBX::bridge(void)
 {
        struct join_relation *relation;
@@ -272,12 +299,21 @@ void JoinPBX::bridge(void)
        class Endpoint *epoint;
        struct port_list *portlist;
        class Port *port;
+       unsigned int bridge_id;
 #ifdef DEBUG_COREBRIDGE
        int allmISDN = 0; // never set for debug purpose
 #else
        int allmISDN = 1; // set until a non-mISDN relation is found
 #endif
 
+       /* bridge id is the serial of join
+        * if we have a 3pty with another join, we always use the lowest brigde id.
+        * this way we use common ids, so both joins share same bridge */
+       if (j_3pty && j_3pty < j_serial)
+               bridge_id = j_3pty;
+       else
+               bridge_id = j_serial;
+
        relation = j_relation;
        while(relation) {
                /* count all relations */
@@ -335,6 +371,8 @@ void JoinPBX::bridge(void)
                        numconnect ++;
 
                /* remove unconnected parties from conference, also remove remotely disconnected parties so conference will not be disturbed. */
+
+               /* mISDN */
                if (relation->channel_state == 1
                 && relation->rx_state != NOTIFY_STATE_HOLD
                 && relation->rx_state != NOTIFY_STATE_SUSPEND
@@ -342,7 +380,7 @@ void JoinPBX::bridge(void)
                 && allmISDN) { // no conf if any member is not mISDN
                        message = message_create(j_serial, relation->epoint_id, JOIN_TO_EPOINT, MESSAGE_mISDNSIGNAL);
                        message->param.mISDNsignal.message = mISDNSIGNAL_CONF;
-                       message->param.mISDNsignal.conf = j_serial<<16 | j_pid;
+                       message->param.mISDNsignal.conf = (bridge_id << 16) | j_pid;
                        PDEBUG(DEBUG_JOIN, "join%d EP%d +on+ id: 0x%08x\n", j_serial, relation->epoint_id, message->param.mISDNsignal.conf);
                        message_put(message);
                } else {
@@ -353,30 +391,35 @@ void JoinPBX::bridge(void)
                        message_put(message);
                }
 
-               /*
-                * request data from endpoint/port if:
-                * - two relations
-                * - any without mISDN
-                * in this case we bridge
-                */
-               message = message_create(j_serial, relation->epoint_id, JOIN_TO_EPOINT, MESSAGE_mISDNSIGNAL);
-               message->param.mISDNsignal.message = mISDNSIGNAL_JOINDATA;
-               message->param.mISDNsignal.joindata = (relations==2 && !allmISDN);
-               PDEBUG(DEBUG_JOIN, "join%d EP%d set joindata=%d\n", j_serial, relation->epoint_id, message->param.mISDNsignal.joindata);
-               message_put(message);
+               /* core bridge */
+               if (relation->channel_state == 1
+                && relation->rx_state != NOTIFY_STATE_HOLD
+                && relation->rx_state != NOTIFY_STATE_SUSPEND
+                && relations>1 // no bridge with one member
+                && !allmISDN) { // no bridge if all members are mISDN
+                       message = message_create(j_serial, relation->epoint_id, JOIN_TO_EPOINT, MESSAGE_BRIDGE);
+                       message->param.bridge_id = bridge_id;
+                       PDEBUG(DEBUG_JOIN, "join%u EP%u requests bridge=%u\n", j_serial, relation->epoint_id, bridge_id);
+                       message_put(message);
+               } else {
+                       message = message_create(j_serial, relation->epoint_id, JOIN_TO_EPOINT, MESSAGE_BRIDGE);
+                       message->param.bridge_id = 0;
+                       PDEBUG(DEBUG_JOIN, "join%u EP%u drop bridge=%u\n", j_serial, relation->epoint_id, bridge_id);
+                       message_put(message);
+               }
 
                relation = relation->next;
        }
 
        /* two people just exchange their states */
-       if (relations==2 && !j_partyline) {
+       if (!j_3pty && relations==2 && !j_partyline) {
                PDEBUG(DEBUG_JOIN, "join%d 2 relations / no partyline\n", j_serial);
                relation = j_relation;
                relation->tx_state = notify_state_change(j_serial, relation->epoint_id, relation->tx_state, relation->next->rx_state);
                relation->next->tx_state = notify_state_change(j_serial, relation->next->epoint_id, relation->next->tx_state, relation->rx_state);
        } else
        /* one member in a join, so we put her on hold */
-       if ((relations==1 || numconnect==1)/* && !j_partyline_jingle*/) {
+       if (!j_3pty && (relations==1 || numconnect==1)/* && !j_partyline_jingle*/) {
                PDEBUG(DEBUG_JOIN, "join%d 1 member or only 1 connected, put on hold\n", j_serial);
                relation = j_relation;
                while(relation) {
@@ -400,43 +443,6 @@ void JoinPBX::bridge(void)
        }
 }
 
-/*
- * bridging is only possible with two connected endpoints
- */
-void JoinPBX::bridge_data(unsigned int epoint_from, struct join_relation *relation_from, union parameter *param)
-{
-       struct join_relation *relation_to;
-
-       /* if we are alone */
-       if (!j_relation->next)
-               return;
-
-       /* if we are more than two */
-       if (j_relation->next->next)
-               return;
-
-       /* skip if source endpoint has NOT audio mode CONNECT */
-       if (relation_from->channel_state != 1)
-               return;
-
-       /* get destination relation */
-       relation_to = j_relation;
-       if (relation_to == relation_from) {
-               /* oops, we are the first, so destination is: */
-               relation_to = relation_to->next;
-       }
-
-       /* skip if destination endpoint has NOT audio mode CONNECT */
-       if (relation_to->channel_state != 1)
-               return;
-
-       /* now we may send our data to the endpoint where it
-        * will be delivered to the port
-        */
-//printf("from %d, to %d\n", relation_from->epoint_id, relation_to->epoint_id);
-       message_forward(j_serial, relation_to->epoint_id, JOIN_TO_EPOINT, param);
-}
-
 /* release join from endpoint
  * if the join has two relations, all relations are freed and the join will be
  * destroyed
@@ -453,7 +459,7 @@ int JoinPBX::release(struct join_relation *relation, int location, int cause)
        /* remove from bridge */
        if (relation->channel_state != 0) {
                relation->channel_state = 0;
-               j_updatebridge = 1; /* update bridge flag */
+               trigger_work(&j_updatebridge);
                // note: if join is not released, bridge must be updated
        }
 
@@ -525,9 +531,6 @@ int joinpbx_countrelations(unsigned int join_id)
        if (!join)
                return(0);
 
-       if (join->j_type == JOIN_TYPE_REMOTE)
-               return(2);
-
        if (join->j_type != JOIN_TYPE_PBX)
                return(0);
        joinpbx = (class JoinPBX *)join;
@@ -638,7 +641,7 @@ void JoinPBX::message_epoint(unsigned int epoint_id, int message_type, union par
 //             joinpbx_debug(join,"Join::message_epoint");
 //     }
        if (options.deb & DEBUG_JOIN) {
-               if (message_type != MESSAGE_DATA) {
+               if (message_type) {
                        cl = join_first;
                        while(cl) {
                                if (cl->j_type == JOIN_TYPE_PBX)
@@ -677,7 +680,7 @@ void JoinPBX::message_epoint(unsigned int epoint_id, int message_type, union par
                        SPRINT(message->param.connectinfo.id, "%d", j_partyline);
                        message->param.connectinfo.ntype = INFO_NTYPE_UNKNOWN;
                        message_put(message);
-                       j_updatebridge = 1; /* update bridge flag */
+                       trigger_work(&j_updatebridge);
                        if (j_partyline_jingle)
                               play_jingle(1);
                        break;
@@ -686,7 +689,7 @@ void JoinPBX::message_epoint(unsigned int epoint_id, int message_type, union par
                        PDEBUG(DEBUG_JOIN, "join received channel message: %d.\n", param->audiopath);
                        if (relation->channel_state != param->audiopath) {
                                relation->channel_state = param->audiopath;
-                               j_updatebridge = 1; /* update bridge flag */
+                               trigger_work(&j_updatebridge);
                                if (options.deb & DEBUG_JOIN)
                                        joinpbx_debug(this, "Join::message_epoint{after setting new channel state}");
                        }
@@ -718,10 +721,10 @@ void JoinPBX::message_epoint(unsigned int epoint_id, int message_type, union par
        switch(message_type) {
                /* process audio path message */
                case MESSAGE_AUDIOPATH:
-               PDEBUG(DEBUG_JOIN, "join received channel message: %d.\n", param->audiopath);
+               PDEBUG(DEBUG_JOIN, "join received channel message: audiopath=%d, current relation's channel_state=%d\n", param->audiopath, relation->channel_state);
                if (relation->channel_state != param->audiopath) {
                        relation->channel_state = param->audiopath;
-                       j_updatebridge = 1; /* update bridge flag */
+                       trigger_work(&j_updatebridge);
                        if (options.deb & DEBUG_JOIN)
                                joinpbx_debug(this, "Join::message_epoint{after setting new channel state}");
                }
@@ -739,7 +742,7 @@ void JoinPBX::message_epoint(unsigned int epoint_id, int message_type, union par
                        new_state = track_notify(relation->rx_state, param->notifyinfo.notify);
                        if (new_state != relation->rx_state) {
                                relation->rx_state = new_state;
-                               j_updatebridge = 1;
+                               trigger_work(&j_updatebridge);
                                if (options.deb & DEBUG_JOIN)
                                        joinpbx_debug(this, "Join::message_epoint{after setting new rx state}");
                        }
@@ -759,12 +762,6 @@ void JoinPBX::message_epoint(unsigned int epoint_id, int message_type, union par
                }
                return;
 
-               /* audio data */
-               case MESSAGE_DATA:
-               /* now send audio data to the other endpoint */
-               bridge_data(epoint_id, relation, param);
-               return;
-
                /* relations sends a connect */
                case MESSAGE_CONNECT:
                /* outgoing setup type becomes connected */
@@ -853,14 +850,25 @@ void JoinPBX::message_epoint(unsigned int epoint_id, int message_type, union par
                switch(message_type) {
                        case MESSAGE_SETUP:
                        if (param->setup.dialinginfo.itype == INFO_ITYPE_ISDN_EXTENSION) {
+                               /* in case of keypad */
+                               numbers = param->setup.dialinginfo.keypad;
+                               if (numbers[0]) {
+                                       while((number = strsep(&numbers, ","))) {
+                                               if (out_setup(epoint_id, message_type, param, NULL, number))
+                                                       return; // join destroyed
+                                       }
+                                       /* after keypad finish dialing */
+                                       break;
+                               }
+                               /* dialed number */
                                numbers = param->setup.dialinginfo.id;
                                while((number = strsep(&numbers, ","))) {
-                                       if (out_setup(epoint_id, message_type, param, number))
+                                       if (out_setup(epoint_id, message_type, param, number, NULL))
                                                return; // join destroyed
                                }
                                break;
                        }
-                       if (out_setup(epoint_id, message_type, param, NULL))
+                       if (out_setup(epoint_id, message_type, param, param->setup.dialinginfo.id, param->setup.dialinginfo.keypad))
                                return; // join destroyed
                        break;
 
@@ -884,30 +892,6 @@ void JoinPBX::message_epoint(unsigned int epoint_id, int message_type, union par
 }
 
 
-/* join process is called from the main loop
- * it processes the current calling state.
- * returns 0 if join nothing was done
- */
-int JoinPBX::handler(void)
-{
-//     struct join_relation *relation;
-//     char dialing[32][32];
-//     int port[32];
-//     int found;
-//     int i, j;
-//     char *p;
-
-       /* the bridge must be updated */
-       if (j_updatebridge) {
-               bridge();
-               j_updatebridge = 0;
-               return(1);
-       }
-
-       return(0);
-}
-
-
 int track_notify(int oldstate, int notify)
 {
        int newstate = oldstate;
@@ -945,7 +929,7 @@ int track_notify(int oldstate, int notify)
  * if other outgoing endpoints already exists, they are release as well.
  * note: if this functions fails, it will destroy its own join object!
  */
-int JoinPBX::out_setup(unsigned int epoint_id, int message_type, union parameter *param, char *newnumber)
+int JoinPBX::out_setup(unsigned int epoint_id, int message_type, union parameter *param, char *newnumber, char *newkeypad)
 {
        struct join_relation *relation;
        struct lcr_msg *message;
@@ -963,8 +947,7 @@ int JoinPBX::out_setup(unsigned int epoint_id, int message_type, union parameter
        epoint = new Endpoint(0, j_serial);
        if (!epoint)
                FATAL("No memory for Endpoint instance\n");
-       if (!(epoint->ep_app = new DEFAULT_ENDPOINT_APP(epoint, 1))) // outgoing
-               FATAL("No memory for Endpoint Application instance\n");
+       epoint->ep_app = new_endpointapp(epoint, 1, EAPP_TYPE_PBX); // outgoing
        relation->epoint_id = epoint->ep_serial;
        /* send setup message to new endpoint */
 //printf("JOLLY DEBUG: %d\n",join_countrelations(j_serial));
@@ -974,7 +957,13 @@ int JoinPBX::out_setup(unsigned int epoint_id, int message_type, union parameter
        memcpy(&message->param, param, sizeof(union parameter));
        if (newnumber)
                SCPY(message->param.setup.dialinginfo.id, newnumber);
-       PDEBUG(DEBUG_JOIN, "setup message sent to ep %d with number='%s'.\n", relation->epoint_id, message->param.setup.dialinginfo.id);
+       else
+               message->param.setup.dialinginfo.id[0] = '\0';
+       if (newkeypad)
+               SCPY(message->param.setup.dialinginfo.keypad, newkeypad);
+       else
+               message->param.setup.dialinginfo.keypad[0] = '\0';
+       PDEBUG(DEBUG_JOIN, "setup message sent to ep %d with number='%s' keypad='%s'.\n", relation->epoint_id, message->param.setup.dialinginfo.id, message->param.setup.dialinginfo.keypad);
        message_put(message);
        return(0);
 }