From 04fc928a2c5f0262e85c09cced1ef20a9fd15f3d Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 29 Jul 2012 12:18:02 +0200 Subject: [PATCH] Implement 3PTY bridge of two 'join's. --- apppbx.cpp | 278 ++++++++++++++++++++++++++++++++++++++++++++++++-------- apppbx.h | 2 + joinpbx.cpp | 56 +++++++++--- joinpbx.h | 2 + lcradmin.c | 7 ++ lcrsocket.h | 1 + socket_server.c | 4 +- todo.txt | 5 + 8 files changed, 306 insertions(+), 49 deletions(-) diff --git a/apppbx.cpp b/apppbx.cpp index b53d5ce..0369bb9 100644 --- a/apppbx.cpp +++ b/apppbx.cpp @@ -2402,9 +2402,27 @@ void EndpointAppPBX::port_3pty(struct port_list *portlist, int message_type, uni struct lcr_msg *message; int rc; - rc = join_join(); +#if 0 + /* bridge for real */ + if (param->threepty.begin) + rc = join_join(); + else if (param->threepty.end) + rc = -ENOTSUP; + else + return; +#else + /* 3PTY bridge */ + if (param->threepty.begin) + rc = join_3pty(); + else if (param->threepty.end) + rc = split_3pty(); + else + return; +#endif + message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_3PTY); - message->param.threepty.begin = 1; + message->param.threepty.begin = param->threepty.begin; + message->param.threepty.end = param->threepty.end; if (rc < 0) message->param.threepty.error = 1; else @@ -3564,13 +3582,14 @@ int EndpointAppPBX::join_join(void) { #ifdef WITH_MISDN struct lcr_msg *message; - struct join_relation *our_relation, *other_relation; - struct join_relation **our_relation_pointer, **other_relation_pointer; - class Join *our_join, *other_join; - class JoinPBX *our_joinpbx, *other_joinpbx; - class EndpointAppPBX *other_eapp; class Endpoint *temp_epoint; + struct join_relation *add_relation, *remove_relation; + struct join_relation **add_relation_pointer, **remove_relation_pointer; + class Join *our_join, *other_join, *add_join, *remove_join; + class JoinPBX *our_joinpbx, *other_joinpbx, *add_joinpbx, *remove_joinpbx; + class EndpointAppPBX *other_eapp, *remove_eapp; class Port *our_port, *other_port; class Pdss1 *our_pdss1, *other_pdss1; + class Endpoint *temp_epoint; /* are we a candidate to join a join? */ our_join = find_join_id(ea_endpoint->ep_join_id); @@ -3602,7 +3621,7 @@ int EndpointAppPBX::join_join(void) } our_pdss1 = (class Pdss1 *)our_port; - /* find an endpoint that is on hold and has the same mISDNport that we are on */ + /* find an endpoint that has the same mISDNport/ces that we are on */ other_eapp = apppbx_first; while(other_eapp) { if (other_eapp == this) { @@ -3658,58 +3677,196 @@ int EndpointAppPBX::join_join(void) return -1; } + /* now find out which is ACTIVE-IDLE and which is ACTIVE-HELD */ + if (our_pdss1->p_m_hold && !other_pdss1->p_m_hold) { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) our relation is on hold and other is active, so we move our relations to other relations\n", ea_endpoint->ep_serial); + remove_eapp = this; + remove_join = our_join; + remove_joinpbx = our_joinpbx; + add_join = other_join; + add_joinpbx = other_joinpbx; + } else { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) our relation is active or other is on hold, so we move ohter relations to our relations\n", ea_endpoint->ep_serial); + remove_eapp = other_eapp; + remove_join = other_join; + remove_joinpbx = other_joinpbx; + add_join = our_join; + add_joinpbx = our_joinpbx; + } + /* remove relation to endpoint for join on hold */ - other_relation = other_joinpbx->j_relation; - other_relation_pointer = &other_joinpbx->j_relation; - while(other_relation) { - if (other_relation->epoint_id == other_eapp->ea_endpoint->ep_serial) { - /* detach other endpoint on hold */ - *other_relation_pointer = other_relation->next; - FREE(other_relation, sizeof(struct join_relation)); + remove_relation = remove_joinpbx->j_relation; + remove_relation_pointer = &remove_joinpbx->j_relation; + while(remove_relation) { + if (remove_relation->epoint_id == remove_eapp->ea_endpoint->ep_serial) { + /* detach other endpoint */ + *remove_relation_pointer = remove_relation->next; + FREE(remove_relation, sizeof(struct join_relation)); cmemuse--; - other_relation = *other_relation_pointer; - other_eapp->ea_endpoint->ep_join_id = 0; + remove_relation = *remove_relation_pointer; + remove_eapp->ea_endpoint->ep_join_id = 0; continue; } /* change join/hold pointer of endpoint to the new join */ - temp_epoint = find_epoint_id(other_relation->epoint_id); + temp_epoint = find_epoint_id(remove_relation->epoint_id); if (temp_epoint) { - if (temp_epoint->ep_join_id == other_join->j_serial) - temp_epoint->ep_join_id = our_join->j_serial; + if (temp_epoint->ep_join_id == remove_join->j_serial) + temp_epoint->ep_join_id = add_join->j_serial; } - other_relation_pointer = &other_relation->next; - other_relation = other_relation->next; + remove_relation_pointer = &remove_relation->next; + remove_relation = remove_relation->next; } - PDEBUG(DEBUG_EPOINT, "EPOINT(%d) endpoint on hold removed, other enpoints on join relinked (to our join).\n", ea_endpoint->ep_serial); + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) endpoint removed, other enpoints on join relinked.\n", ea_endpoint->ep_serial); /* join call relations */ - our_relation = our_joinpbx->j_relation; - our_relation_pointer = &our_joinpbx->j_relation; - while(our_relation) { - our_relation_pointer = &our_relation->next; - our_relation = our_relation->next; - } - *our_relation_pointer = other_joinpbx->j_relation; - other_joinpbx->j_relation = NULL; + add_relation = add_joinpbx->j_relation; + add_relation_pointer = &add_joinpbx->j_relation; + while(add_relation) { + add_relation_pointer = &add_relation->next; + add_relation = add_relation->next; + } + *add_relation_pointer = remove_joinpbx->j_relation; + remove_joinpbx->j_relation = NULL; PDEBUG(DEBUG_EPOINT, "EPOINT(%d) relations joined.\n", ea_endpoint->ep_serial); - /* release endpoint on hold */ - message = message_create(other_joinpbx->j_serial, other_eapp->ea_endpoint->ep_serial, JOIN_TO_EPOINT, MESSAGE_RELEASE); + /* release endpoint */ + message = message_create(remove_joinpbx->j_serial, remove_eapp->ea_endpoint->ep_serial, JOIN_TO_EPOINT, MESSAGE_RELEASE); message->param.disconnectinfo.cause = CAUSE_NORMAL; /* normal */ message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; message_put(message); /* if we are not a partyline, we get partyline state from other join */ - our_joinpbx->j_partyline += other_joinpbx->j_partyline; + add_joinpbx->j_partyline += remove_joinpbx->j_partyline; /* remove empty join */ - delete other_join; - PDEBUG(DEBUG_EPOINT, "EPOINT(%d)d-join completely removed!\n"); + delete remove_join; + PDEBUG(DEBUG_EPOINT, "EPOINT(%d)join completely removed!\n", ea_endpoint->ep_serial); + + /* mixer must update */ + trigger_work(&add_joinpbx->j_updatebridge); + + /* we send a retrieve to that endpoint */ + // mixer will update the hold-state of the join and send it to the endpoints is changes +#else + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: no mISDN support anyway.\n", ea_endpoint->ep_serial); +#endif + + return 0; +} + +int EndpointAppPBX::join_3pty(void) +{ +#ifdef WITH_MISDN + class Join *our_join, *other_join; + class JoinPBX *our_joinpbx, *other_joinpbx; + class EndpointAppPBX *other_eapp; + class Port *our_port, *other_port; + class Pdss1 *our_pdss1, *other_pdss1; + + /* are we a candidate to join a join? */ + our_join = find_join_id(ea_endpoint->ep_join_id); + if (!our_join) { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: our join doesn't exist anymore.\n", ea_endpoint->ep_serial); + return -1; + } + if (our_join->j_type != JOIN_TYPE_PBX) { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: join is not a pbx join.\n", ea_endpoint->ep_serial); + return -1; + } + our_joinpbx = (class JoinPBX *)our_join; + if (!ea_endpoint->ep_portlist) { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: we have no port.\n", ea_endpoint->ep_serial); + return -1; + } + if (!e_ext.number[0]) { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: we are not internal extension.\n", ea_endpoint->ep_serial); + return -1; + } + our_port = find_port_id(ea_endpoint->ep_portlist->port_id); + if (!our_port) { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: our port doesn't exist anymore.\n", ea_endpoint->ep_serial); + return -1; + } + if ((our_port->p_type & PORT_CLASS_mISDN_MASK) != PORT_CLASS_DSS1) { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: our port is not isdn.\n", ea_endpoint->ep_serial); + return -1; + } + our_pdss1 = (class Pdss1 *)our_port; + + /* find an endpoint that has the same mISDNport/ces that we are on */ + other_eapp = apppbx_first; + while(other_eapp) { + if (other_eapp == this) { + other_eapp = other_eapp->next; + continue; + } + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint candiate: (ep%d) terminal='%s' port=%s join=%d.\n", ea_endpoint->ep_serial, other_eapp->ea_endpoint->ep_serial, other_eapp->e_ext.number, (other_eapp->ea_endpoint->ep_portlist)?"YES":"NO", other_eapp->ea_endpoint->ep_join_id); + if (other_eapp->e_ext.number[0] /* has terminal */ + && other_eapp->ea_endpoint->ep_portlist /* has port */ + && other_eapp->ea_endpoint->ep_join_id) { /* has join */ + other_port = find_port_id(other_eapp->ea_endpoint->ep_portlist->port_id); + if (other_port) { /* port still exists */ + if (other_port->p_type==PORT_TYPE_DSS1_NT_OUT + || other_port->p_type==PORT_TYPE_DSS1_NT_IN) { /* port is isdn nt-mode */ + other_pdss1 = (class Pdss1 *)other_port; + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint's port is of type isdn! comparing our portnum=%d with other's portnum=%d hold=%s ces=%d\n", ea_endpoint->ep_serial, our_pdss1->p_m_mISDNport->portnum, other_pdss1->p_m_mISDNport->portnum, (other_pdss1->p_m_hold)?"YES":"NO", other_pdss1->p_m_d_ces); + if (1 //other_pdss1->p_m_hold /* port is on hold */ + && other_pdss1->p_m_mISDNport == our_pdss1->p_m_mISDNport /* same isdn interface */ + && other_pdss1->p_m_d_ces == our_pdss1->p_m_d_ces) /* same tei+sapi */ + break; + } else { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint's port is of other type!\n", ea_endpoint->ep_serial); + } + } else { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint's port doesn't exist enymore.\n", ea_endpoint->ep_serial); + } + } + other_eapp = other_eapp->next; + } + if (!other_eapp) { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: no other endpoint on same isdn terminal.\n", ea_endpoint->ep_serial); + return -1; + } + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) port with same terminal found.\n", ea_endpoint->ep_serial); + + /* if we have the same join */ + if (other_eapp->ea_endpoint->ep_join_id == ea_endpoint->ep_join_id) { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: we and the other have the same join.\n", ea_endpoint->ep_serial); + return -1; + } + other_join = find_join_id(other_eapp->ea_endpoint->ep_join_id); + if (!other_join) { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: other join doesn't exist anymore.\n", ea_endpoint->ep_serial); + return -1; + } + if (other_join->j_type != JOIN_TYPE_PBX) { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: other join is not a pbx join.\n", ea_endpoint->ep_serial); + return -1; + } + other_joinpbx = (class JoinPBX *)other_join; + if (our_joinpbx->j_partyline && other_joinpbx->j_partyline) { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: both joins are partylines.\n", ea_endpoint->ep_serial); + return -1; + } + + if (our_joinpbx->j_3pty) { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: our join already doing 3PTY.\n", ea_endpoint->ep_serial); + return -1; + } + if (other_joinpbx->j_3pty) { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: other join already doing 3PTY.\n", ea_endpoint->ep_serial); + return -1; + } + + /* set 3PTY bridge */ + other_joinpbx->j_3pty = our_joinpbx->j_serial; + our_joinpbx->j_3pty = other_joinpbx->j_serial; /* mixer must update */ trigger_work(&our_joinpbx->j_updatebridge); + trigger_work(&other_joinpbx->j_updatebridge); /* we send a retrieve to that endpoint */ // mixer will update the hold-state of the join and send it to the endpoints is changes @@ -3720,6 +3877,55 @@ int EndpointAppPBX::join_join(void) return 0; } +int EndpointAppPBX::split_3pty(void) +{ +#ifdef WITH_MISDN + class Join *our_join, *other_join; + class JoinPBX *our_joinpbx, *other_joinpbx; + + /* are we a candidate to join a join? */ + our_join = find_join_id(ea_endpoint->ep_join_id); + if (!our_join) { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot split: our join doesn't exist anymore.\n", ea_endpoint->ep_serial); + return -1; + } + if (our_join->j_type != JOIN_TYPE_PBX) { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot split: join is not a pbx join.\n", ea_endpoint->ep_serial); + return -1; + } + our_joinpbx = (class JoinPBX *)our_join; + + if (!our_joinpbx->j_3pty) { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot split: we don't have a 3PTY.\n", ea_endpoint->ep_serial); + return -1; + } + + other_join = find_join_id(our_joinpbx->j_3pty); + if (!other_join) { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot split: other join doesn't exist anymore.\n", ea_endpoint->ep_serial); + return -1; + } + if (other_join->j_type != JOIN_TYPE_PBX) { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot split: join is not a pbx join.\n", ea_endpoint->ep_serial); + return -1; + } + other_joinpbx = (class JoinPBX *)other_join; + + our_joinpbx->j_3pty = 0; + other_joinpbx->j_3pty = 0; + + /* mixer must update */ + trigger_work(&our_joinpbx->j_updatebridge); + trigger_work(&other_joinpbx->j_updatebridge); + + /* we send a retrieve to that endpoint */ + // mixer will update the hold-state of the join and send it to the endpoints is changes +#else + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot split: no mISDN support anyway.\n", ea_endpoint->ep_serial); +#endif + + return 0; +} /* check if we have an external call * this is used to check for encryption ability diff --git a/apppbx.h b/apppbx.h index 6e4e12d..1bbe8ba 100644 --- a/apppbx.h +++ b/apppbx.h @@ -309,6 +309,8 @@ class EndpointAppPBX : public EndpointApp /* facility function */ void pick_join(char *extension); int join_join(void); + int join_3pty(void); + int split_3pty(void); void encrypt_shared(void); void encrypt_keyex(void); void encrypt_off(void); diff --git a/joinpbx.cpp b/joinpbx.cpp index 988ba24..6e995f3 100644 --- a/joinpbx.cpp +++ b/joinpbx.cpp @@ -226,6 +226,7 @@ JoinPBX::JoinPBX(class Endpoint *epoint) : Join() j_pid = getpid(); j_partyline = 0; j_partyline_jingle = 0; + j_3pty = 0; j_multicause = 0; j_multilocation = 0; memset(&j_updatebridge, 0, sizeof(j_updatebridge)); @@ -261,6 +262,19 @@ JoinPBX::~JoinPBX() 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); } @@ -285,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 */ @@ -348,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 @@ -355,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 { @@ -366,28 +391,35 @@ void JoinPBX::bridge(void) message_put(message); } - /* - * Bridge between port instances if: - * - two or more relations - * - one or all are not mISDN - */ - message = message_create(j_serial, relation->epoint_id, JOIN_TO_EPOINT, MESSAGE_BRIDGE); - message->param.bridge_id = (relations>=2 && !allmISDN) ? j_serial : 0; - PDEBUG(DEBUG_JOIN, "join%u EP%u requests bridge=%u\n", j_serial, relation->epoint_id, message->param.bridge_id); - 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) { diff --git a/joinpbx.h b/joinpbx.h index bf42c9a..cda5419 100644 --- a/joinpbx.h +++ b/joinpbx.h @@ -65,6 +65,8 @@ class JoinPBX : public Join int j_partyline; /* if set, join is conference room */ int j_partyline_jingle; /* also play jingle on join/leave */ + unsigned int j_3pty; /* other join if a 3pty-bridge is requested */ + void bridge(void); void remove_relation(struct join_relation *relation); struct join_relation *add_relation(void); diff --git a/lcradmin.c b/lcradmin.c index bd029e8..59553fd 100644 --- a/lcradmin.c +++ b/lcradmin.c @@ -387,6 +387,13 @@ int debug_join(struct admin_message *msg, struct admin_message *m, int line, int color(white); SPRINT(buffer,"JOIN(%d)", join); addstr(buffer); + if (m[i].u.j.threepty) { + color(cyan); + addstr(" 3PTY->"); + color(white); + SPRINT(buffer, "%d\n", m[i].u.j.threepty); + addstr(buffer); + } if (m[i].u.j.partyline) { color(cyan); addstr(" partyline="); diff --git a/lcrsocket.h b/lcrsocket.h index 931afb8..f52dd7e 100644 --- a/lcrsocket.h +++ b/lcrsocket.h @@ -95,6 +95,7 @@ struct admin_response_join { unsigned int serial; /* join serial number */ char remote[32]; /* remote application name */ unsigned int partyline; + unsigned int threepty; }; struct admin_response_epoint { diff --git a/socket_server.c b/socket_server.c index 0f7f6d0..7bb69fc 100644 --- a/socket_server.c +++ b/socket_server.c @@ -957,8 +957,10 @@ int admin_state(struct admin_queue **responsep) /* serial */ response->am[num].u.j.serial = join->j_serial; /* partyline */ - if (join->j_type == JOIN_TYPE_PBX) + if (join->j_type == JOIN_TYPE_PBX) { response->am[num].u.j.partyline = ((class JoinPBX *)join)->j_partyline; + response->am[num].u.j.threepty = ((class JoinPBX *)join)->j_3pty; + } /* */ join = join->next; num++; diff --git a/todo.txt b/todo.txt index 896a0d0..caceec8 100644 --- a/todo.txt +++ b/todo.txt @@ -1,3 +1,8 @@ +wimpys interfaces patch +atuls rtp socket patch + + +-----old: call transfer and other functional protocols -- 2.13.6