Add -lncurses to LDD flags
[lcr.git] / apppbx.cpp
index a61d108..9752b87 100644 (file)
@@ -500,6 +500,7 @@ void EndpointAppPBX::notify_active(void)
  */
 void EndpointAppPBX::keypad_function(char digit)
 {
+       class Port *port;
 
        /* we must be in a call, in order to send messages to the call */
        if (e_ext.number[0] == '\0') {
@@ -515,7 +516,25 @@ void EndpointAppPBX::keypad_function(char digit)
                        break;
                }
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) join call with call on hold\n", ea_endpoint->ep_serial);
-               join_join();
+               port = find_port_id(ea_endpoint->ep_portlist->port_id);
+               if (!port)
+                       break;
+               if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS)
+                       join_join_fxs();
+               else if ((port->p_type & PORT_CLASS_mISDN_MASK) == PORT_CLASS_DSS1)
+                       join_join_dss1();
+               break;
+
+               /* VOOTP on */
+               case '1':
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) enable VoOTP.\n", ea_endpoint->ep_serial);
+               vootp_on(1);
+               break;
+
+               /* VOOTP off */
+               case '2':
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) disable VoOTP.\n", ea_endpoint->ep_serial);
+               vootp_on(0);
                break;
 
 #ifdef WITH_CRYPT
@@ -537,7 +556,6 @@ void EndpointAppPBX::keypad_function(char digit)
                encrypt_off();
                break;
 #endif
-
                default:        
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) unsupported keypad digit '%c'.\n", ea_endpoint->ep_serial, digit);
        }
@@ -584,306 +602,6 @@ void EndpointAppPBX::set_tone(struct port_list *portlist, const char *tone)
        }
 }
 
-/* hunts for the given interface
- * it does not need to have an mISDNport instance */
-struct interface *EndpointAppPBX::hunt_interface(char *ifname)
-{
-       struct interface *interface;
-       int there_is_an_external = 0;
-
-       interface = interface_first;
-
-       /* first find the given interface or, if not given, one with no extension */
-       checknext:
-       if (!interface) {
-               if (!there_is_an_external && !(ifname && ifname[0])) {
-                       trace_header("CHANNEL SELECTION (no external interface specified)", DIRECTION_NONE);
-                       add_trace("info", NULL, "Add 'extern' parameter to interface.conf.");
-                       end_trace();
-               }
-               return(NULL);
-       }
-
-       /* check for given interface */
-       if (ifname && ifname[0]) {
-               if (!strcasecmp(interface->name, ifname)) {
-                       /* found explicit interface */
-                       trace_header("CHANNEL SELECTION (found given interface)", DIRECTION_NONE);
-                       add_trace("interface", NULL, "%s", ifname);
-                       end_trace();
-                       goto foundif;
-               }
-       } else {
-               if (interface->external) {
-                       there_is_an_external = 1;
-                       /* found non extension */
-                       trace_header("CHANNEL SELECTION (found external interface)", DIRECTION_NONE);
-                       add_trace("interface", NULL, "%s", interface->name);
-                       end_trace();
-                       goto foundif;
-               }
-       }
-
-       interface = interface->next;
-       goto checknext;
-foundif:
-
-       return interface;
-}
-
-
-#ifdef WITH_MISDN
-/*
- * hunts an mISDNport that is available for an outgoing call
- * if no ifname was given, any interface that is not an extension
- * will be searched.
- */
-struct mISDNport *EndpointAppPBX::hunt_port(char *ifname, int *channel)
-{
-       struct interface *interface;
-       struct interface_port *ifport, *ifport_start;
-       struct select_channel *selchannel; 
-       struct mISDNport *mISDNport;
-       int index, i;
-       int there_is_an_external = 0;
-
-       interface = interface_first;
-
-       /* first find the given interface or, if not given, one with no extension */
-       checknext:
-       if (!interface) {
-               if (!there_is_an_external && !(ifname && ifname[0])) {
-                       trace_header("CHANNEL SELECTION (no external interface specified)", DIRECTION_NONE);
-                       add_trace("info", NULL, "Add 'extern' parameter to interface.conf.");
-                       end_trace();
-               }
-               return(NULL);
-       }
-
-       /* check for given interface */
-       if (ifname && ifname[0]) {
-               if (!strcasecmp(interface->name, ifname)) {
-                       /* found explicit interface */
-                       trace_header("CHANNEL SELECTION (found given interface)", DIRECTION_NONE);
-                       add_trace("interface", NULL, "%s", ifname);
-                       end_trace();
-                       goto foundif;
-               }
-
-       } else {
-               if (interface->external) {
-                       there_is_an_external = 1;
-                       /* found non extension */
-                       trace_header("CHANNEL SELECTION (found external interface)", DIRECTION_NONE);
-                       add_trace("interface", NULL, "%s", interface->name);
-                       end_trace();
-                       goto foundif;
-               }
-       }
-
-       interface = interface->next;
-       goto checknext;
-foundif:
-
-       /* see if interface has ports */
-       if (!interface->ifport) {
-               /* no ports */
-               trace_header("CHANNEL SELECTION (active ports, skipping)", DIRECTION_NONE);
-               add_trace("interface", NULL, "%s", interface->name);
-               end_trace();
-               interface = interface->next;
-               goto checknext;
-       }
-
-       /* select port by algorithm */
-       ifport_start = interface->ifport;
-       index = 0;
-       if (interface->hunt == HUNT_ROUNDROBIN) {
-               while(ifport_start->next && index<interface->hunt_next) {
-                       ifport_start = ifport_start->next;
-                       index++;
-               }
-               trace_header("CHANNEL SELECTION (starting round-robin)", DIRECTION_NONE);
-               add_trace("port", NULL, "%d", ifport_start->portnum);
-               add_trace("position", NULL, "%d", index);
-               end_trace();
-       }
-
-       /* loop ports */
-       ifport = ifport_start;
-       nextport:
-
-       /* see if port is available */
-       if (!ifport->mISDNport) {
-               trace_header("CHANNEL SELECTION (port not available, skipping)", DIRECTION_NONE);
-               add_trace("port", NULL, "%d", ifport->portnum);
-               add_trace("position", NULL, "%d", index);
-               end_trace();
-               goto portbusy;
-       }
-       mISDNport = ifport->mISDNport;
-
-       /* see if port is administratively blocked */
-       if (ifport->block) {
-               trace_header("CHANNEL SELECTION (port blocked by admin, skipping)", DIRECTION_NONE);
-               add_trace("port", NULL, "%d", ifport->portnum);
-               add_trace("position", NULL, "%d", index);
-               end_trace();
-               goto portbusy;
-       }
-
-       /* see if link is up on PTP*/
-       if (mISDNport->l2hold && mISDNport->l2link<1) {
-               trace_header("CHANNEL SELECTION (port's layer 2 is down, skipping)", DIRECTION_NONE);
-               add_trace("port", NULL, "%d", ifport->portnum);
-               add_trace("position", NULL, "%d", index);
-               end_trace();
-               goto portbusy;
-       }
-
-       /* check for channel form selection list */
-       *channel = 0;
-#ifdef WITH_SS5
-       if (mISDNport->ss5) {
-               class Pss5 *port;
-               port = ss5_hunt_line(mISDNport);
-               if (port) {
-                       *channel = port->p_m_b_channel;
-                       trace_header("CHANNEL SELECTION (selecting SS5 channel)", DIRECTION_NONE);
-                       add_trace("port", NULL, "%d", ifport->portnum);
-                       add_trace("position", NULL, "%d", index);
-                       add_trace("channel", NULL, "%d", *channel);
-                       end_trace();
-               }
-       } else
-#endif
-       {
-               selchannel = ifport->out_channel;
-               while(selchannel) {
-                       switch(selchannel->channel) {
-                               case CHANNEL_FREE: /* free channel */
-                               if (mISDNport->b_reserved >= mISDNport->b_num)
-                                       break; /* all channel in use or reserverd */
-                               /* find channel */
-                               i = 0;
-                               while(i < mISDNport->b_num) {
-                                       if (mISDNport->b_port[i] == NULL) {
-                                               *channel = i+1+(i>=15);
-                                               trace_header("CHANNEL SELECTION (selecting free channel)", DIRECTION_NONE);
-                                               add_trace("port", NULL, "%d", ifport->portnum);
-                                               add_trace("position", NULL, "%d", index);
-                                               add_trace("channel", NULL, "%d", *channel);
-                                               end_trace();
-                                               break;
-                                       }
-                                       i++;
-                               }
-                               if (*channel)
-                                       break;
-                               trace_header("CHANNEL SELECTION (no channel is 'free')", DIRECTION_NONE);
-                               add_trace("port", NULL, "%d", ifport->portnum);
-                               add_trace("position", NULL, "%d", index);
-                               end_trace();
-                               break;
-
-                               case CHANNEL_ANY: /* don't ask for channel */
-                               if (mISDNport->b_reserved >= mISDNport->b_num) {
-                                       trace_header("CHANNEL SELECTION (cannot ask for 'any' channel, all reserved)", DIRECTION_NONE);
-                                       add_trace("port", NULL, "%d", ifport->portnum);
-                                       add_trace("position", NULL, "%d", index);
-                                       add_trace("total", NULL, "%d", mISDNport->b_num);
-                                       add_trace("reserved", NULL, "%d", mISDNport->b_reserved);
-                                       end_trace();
-                                       break; /* all channel in use or reserverd */
-                               }
-                               trace_header("CHANNEL SELECTION (using 'any' channel)", DIRECTION_NONE);
-                               add_trace("port", NULL, "%d", ifport->portnum);
-                               add_trace("position", NULL, "%d", index);
-                               end_trace();
-                               *channel = CHANNEL_ANY;
-                               break;
-
-                               case CHANNEL_NO: /* call waiting */
-                               trace_header("CHANNEL SELECTION (using 'no' channel, call-waiting)", DIRECTION_NONE);
-                               add_trace("port", NULL, "%d", ifport->portnum);
-                               add_trace("position", NULL, "%d", index);
-                               end_trace();
-                               *channel = CHANNEL_NO;
-                               break;
-
-                               default:
-                               if (selchannel->channel<1 || selchannel->channel==16) {
-                                       trace_header("CHANNEL SELECTION (channel out of range)", DIRECTION_NONE);
-                                       add_trace("port", NULL, "%d", ifport->portnum);
-                                       add_trace("position", NULL, "%d", index);
-                                       add_trace("channel", NULL, "%d", selchannel->channel);
-                                       end_trace();
-                                       break; /* invalid channels */
-                               }
-                               i = selchannel->channel-1-(selchannel->channel>=17);
-                               if (i >= mISDNport->b_num) {
-                                       trace_header("CHANNEL SELECTION (channel out of range)", DIRECTION_NONE);
-                                       add_trace("port", NULL, "%d", ifport->portnum);
-                                       add_trace("position", NULL, "%d", index);
-                                       add_trace("channel", NULL, "%d", selchannel->channel);
-                                       add_trace("channels", NULL, "%d", mISDNport->b_num);
-                                       end_trace();
-                                       break; /* channel not in port */
-                               }
-                               if (mISDNport->b_port[i] == NULL) {
-                                       *channel = selchannel->channel;
-                                       trace_header("CHANNEL SELECTION (selecting given channel)", DIRECTION_NONE);
-                                       add_trace("port", NULL, "%d", ifport->portnum);
-                                       add_trace("position", NULL, "%d", index);
-                                       add_trace("channel", NULL, "%d", *channel);
-                                       end_trace();
-                                       break;
-                               }
-                               break;
-                       }
-                       if (*channel)
-                               break; /* found channel */
-                       selchannel = selchannel->next;
-               }
-       }
-
-       /* if channel was found, return mISDNport and channel */
-       if (*channel) {
-               /* setting next port to start next time */
-               if (interface->hunt == HUNT_ROUNDROBIN) {
-                       index++;
-                       if (!ifport->next)
-                               index = 0;
-                       interface->hunt_next = index;
-               }
-               
-               return(mISDNport);
-       }
-
-       trace_header("CHANNEL SELECTION (skipping, no channel found)", DIRECTION_NONE);
-       add_trace("port", NULL, "%d", ifport->portnum);
-       add_trace("position", NULL, "%d", index);
-       end_trace();
-
-       portbusy:
-       /* go next port, until all ports are checked */
-       index++;
-       ifport = ifport->next;
-       if (!ifport) {
-               index = 0;
-               ifport = interface->ifport;
-       }
-       if (ifport != ifport_start)
-               goto nextport;
-
-       if (!ifname) {
-               interface = interface->next;
-               goto checknext;
-       }
-
-       return(NULL); /* no port found */
-}
-#endif
 
 /* outgoing setup to port(s)
  * ports will be created and a setup is sent if everything is ok. otherwhise
@@ -909,12 +627,14 @@ void EndpointAppPBX::out_setup(int cfnr)
 //     char                    allowed_ports[256];
 //     char                    exten[256];
        char                    ifname[sizeof(e_ext.interfaces)],
-                               number[256];
+                               *ifname_p,
+                               number[256],
+                               *number_p;
        struct port_settings    port_settings;
 #ifdef WITH_MISDN
        int                     channel = 0;
-       struct admin_list       *admin;
 #endif
+       struct admin_list       *admin;
        int                     earlyb;
        int                     mode = B_MODE_TRANSPARENT;
 
@@ -1039,6 +759,23 @@ void EndpointAppPBX::out_setup(int cfnr)
                        }
                        /* found interface */
                        PDEBUG(DEBUG_EPOINT, "EPOINT(%d) calling to interface %s\n", ea_endpoint->ep_serial, ifname);
+                       if (interface->remote) {
+                               admin = admin_first;
+                               while(admin) {
+                                       if (admin->remote_name[0] && !strcmp(admin->remote_name, interface->remote_app))
+                                               break;
+                                       admin = admin->next;
+                               }
+                               if (!admin) {
+                                       trace_header("INTERFACE (remote not connected)", DIRECTION_NONE);
+                                       add_trace("application", NULL, "%s", interface->remote_app);
+                                       end_trace();
+                                       continue;
+                               }
+                               SPRINT(portname, "%s-%d-out", interface->name, 0);
+                               port = new Premote(PORT_TYPE_REMOTE_OUT, portname, &port_settings, interface, admin->sock);
+                               earlyb = (interface->is_earlyb == IS_YES);
+                       } else
 #ifdef WITH_GSM_BS
                        if (interface->gsm_bs) {
                                SPRINT(portname, "%s-%d-out", interface->name, 0);
@@ -1053,7 +790,7 @@ void EndpointAppPBX::out_setup(int cfnr)
                                earlyb = (interface->is_earlyb == IS_YES);
                        } else
 #endif
-#ifdef WITH_GSM_MS
+#ifdef WITH_SIP
                        if (interface->sip) {
                                SPRINT(portname, "%s-%d-out", interface->name, 0);
                                port = new Psip(PORT_TYPE_SIP_OUT, portname, &port_settings, interface);
@@ -1077,22 +814,12 @@ void EndpointAppPBX::out_setup(int cfnr)
                                        port = ss5_hunt_line(mISDNport);
                                else
 #endif
-                               if (mISDNport->ifport->remote) {
-                                       admin = admin_first;
-                                       while(admin) {
-                                               if (admin->remote_name[0] && !strcmp(admin->remote_name, mISDNport->ifport->remote_app))
-                                                       break;
-                                               admin = admin->next;
-                                       }
-                                       if (!admin) {
-                                               trace_header("INTERFACE (remote not connected)", DIRECTION_NONE);
-                                               add_trace("application", NULL, "%s", mISDNport->ifport->remote_app);
-                                               end_trace();
-                                               continue;
-                                       }
-                                       port = new Premote(PORT_TYPE_REMOTE_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode, admin->sock);
-                               } else
-                                       port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode);
+#ifdef ISDN_P_FXS_POTS
+                               if (mISDNport->pots)
+                                       port = new Pfxs(PORT_TYPE_POTS_FXS_OUT, mISDNport, portname, &port_settings, mISDNport->ifport->interface, mode);
+                               else
+#endif
+                                       port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, mISDNport->ifport->interface, channel, mISDNport->ifport->channel_force, mode);
                                earlyb = mISDNport->earlyb;
 #else
                        trace_header("INTERFACE (has no function)", DIRECTION_NONE);
@@ -1213,7 +940,12 @@ void EndpointAppPBX::out_setup(int cfnr)
                                                port = ss5_hunt_line(mISDNport);
                                        else
 #endif
-                                               port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode);
+#ifdef ISDN_P_FXS_POTS
+                                       if (mISDNport->pots)
+                                               port = new Pfxs(PORT_TYPE_POTS_FXS_OUT, mISDNport, portname, &port_settings, mISDNport->ifport->interface, mode);
+                                       else
+#endif
+                                               port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, mISDNport->ifport->interface, channel, mISDNport->ifport->channel_force, mode);
                                        if (!port)
                                                FATAL("No memory for Port instance\n");
                                        earlyb = mISDNport->earlyb;
@@ -1286,128 +1018,153 @@ void EndpointAppPBX::out_setup(int cfnr)
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) dialing external: called='%s' keypad='%s'\n", ea_endpoint->ep_serial, e_dialinginfo.id, e_dialinginfo.keypad);
                /* call to extenal interfaces */
                if (e_dialinginfo.keypad[0])
-                       p = e_dialinginfo.keypad;
+                       number_p = e_dialinginfo.keypad;
                else
-                       p = e_dialinginfo.id;
+                       number_p = e_dialinginfo.id;
                do {
-                       earlyb = 0;
                        number[0] = '\0';
-                       while(*p!=',' && *p!='\0')
-                               SCCAT(number, *p++);
-                       if (*p == ',')
-                               p++;
+                       while(*number_p!=',' && *number_p!='\0')
+                               SCCAT(number, *number_p++);
+                       if (*number_p == ',')
+                               number_p++;
                        /* found number */
-                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) calling to number '%s' interface '%s'\n", ea_endpoint->ep_serial, number, e_dialinginfo.interfaces[0]?e_dialinginfo.interfaces:"any interface");
-                       /* search interface */
-                       interface = hunt_interface(e_dialinginfo.interfaces[0]?e_dialinginfo.interfaces:NULL);
-                       if (!interface) {
-                               trace_header("INTERFACE (not found)", DIRECTION_NONE);
-                               add_trace("interface", NULL, "%s", ifname);
-                               end_trace();
-                               goto check_anycall_extern;
-                       }
-                       /* found interface */
-#ifdef WITH_GSM_BS
-                       if (interface->gsm_bs) {
-                               SPRINT(portname, "%s-%d-out", interface->name, 0);
-                               port = new Pgsm_bs(PORT_TYPE_GSM_BS_OUT, portname, &port_settings, interface);
-                               earlyb = (interface->is_earlyb == IS_YES);
-                       } else
-#endif
-#ifdef WITH_GSM_MS
-                       if (interface->gsm_ms) {
-                               SPRINT(portname, "%s-%d-out", interface->name, 0);
-                               port = new Pgsm_ms(PORT_TYPE_GSM_MS_OUT, portname, &port_settings, interface);
-                               earlyb = (interface->is_earlyb == IS_YES);
-                       } else
-#endif
-#ifdef WITH_GSM_MS
-                       if (interface->sip) {
-                               SPRINT(portname, "%s-%d-out", interface->name, 0);
-                               port = new Psip(PORT_TYPE_SIP_OUT, portname, &port_settings, interface);
-                               earlyb = (interface->is_earlyb == IS_YES);
-                       } else
-#endif
-                       {
-#ifdef WITH_MISDN
-                               /* hunt for mISDNport and create Port */
-                               mISDNport = hunt_port(e_dialinginfo.interfaces[0]?e_dialinginfo.interfaces:NULL, &channel);
-                               if (!mISDNport) {
-                                       trace_header("INTERFACE (too busy)", DIRECTION_NONE);
-                                       add_trace("interface", NULL, "%s", e_dialinginfo.interfaces[0]?e_dialinginfo.interfaces:"any interface");
+
+                       ifname_p = e_dialinginfo.interfaces;
+                       if (*ifname_p == '+')
+                               ifname_p++;
+                       do {
+                               earlyb = 0;
+                               ifname[0] = '\0';
+                               while(*ifname_p!=',' && *ifname_p!='\0')
+                                       SCCAT(ifname, *ifname_p++);
+                               if (*ifname_p == ',')
+                                       ifname_p++;
+                               /* found interface name */
+
+                               /* search interface */
+                               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) calling to number '%s' interface '%s'\n", ea_endpoint->ep_serial, number, ifname[0]?ifname:"any interface");
+                               interface = hunt_interface(ifname[0]?ifname:NULL);
+                               if (!interface) {
+                                       trace_header("INTERFACE (not found)", DIRECTION_NONE);
+                                       add_trace("interface", NULL, "%s", ifname);
                                        end_trace();
-                                       goto check_anycall_extern;
+                                       continue;
                                }
-                               /* creating EXTERNAL port*/
-                               SPRINT(portname, "%s-%d-out", mISDNport->ifport->interface->name, mISDNport->portnum);
-#ifdef WITH_SS5
-                               if (mISDNport->ss5)
-                                       port = ss5_hunt_line(mISDNport);
-                               else
-#endif
-                               if (mISDNport->ifport->remote) {
+                               /* found interface */
+                               if (interface->remote) {
                                        admin = admin_first;
                                        while(admin) {
-                                               if (admin->remote_name[0] && !strcmp(admin->remote_name, mISDNport->ifport->remote_app))
+                                               if (admin->remote_name[0] && !strcmp(admin->remote_name, interface->remote_app))
                                                        break;
                                                admin = admin->next;
                                        }
                                        if (!admin) {
                                                trace_header("INTERFACE (remote not connected)", DIRECTION_NONE);
-                                               add_trace("application", NULL, "%s", mISDNport->ifport->remote_app);
+                                               add_trace("application", NULL, "%s", interface->remote_app);
                                                end_trace();
                                                continue;
                                        }
-                                       port = new Premote(PORT_TYPE_REMOTE_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode, admin->sock);
+                                       SPRINT(portname, "%s-%d-out", interface->name, 0);
+                                       port = new Premote(PORT_TYPE_REMOTE_OUT, portname, &port_settings, interface, admin->sock);
+                                       earlyb = (interface->is_earlyb == IS_YES);
                                } else
-                                       port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode);
-                               earlyb = mISDNport->earlyb;
+#ifdef WITH_GSM_BS
+                               if (interface->gsm_bs) {
+                                       SPRINT(portname, "%s-%d-out", interface->name, 0);
+                                       port = new Pgsm_bs(PORT_TYPE_GSM_BS_OUT, portname, &port_settings, interface);
+                                       earlyb = (interface->is_earlyb == IS_YES);
+                               } else
+#endif
+#ifdef WITH_GSM_MS
+                               if (interface->gsm_ms) {
+                                       SPRINT(portname, "%s-%d-out", interface->name, 0);
+                                       port = new Pgsm_ms(PORT_TYPE_GSM_MS_OUT, portname, &port_settings, interface);
+                                       earlyb = (interface->is_earlyb == IS_YES);
+                               } else
+#endif
+#ifdef WITH_SIP
+                               if (interface->sip) {
+                                       SPRINT(portname, "%s-%d-out", interface->name, 0);
+                                       port = new Psip(PORT_TYPE_SIP_OUT, portname, &port_settings, interface);
+                                       earlyb = (interface->is_earlyb == IS_YES);
+                               } else
+#endif
+                               {
+#ifdef WITH_MISDN
+                                       /* hunt for mISDNport and create Port */
+                                       mISDNport = hunt_port(ifname[0]?ifname:NULL, &channel);
+                                       if (!mISDNport) {
+                                               trace_header("INTERFACE (too busy)", DIRECTION_NONE);
+                                               add_trace("interface", NULL, "%s", ifname[0]?ifname:"any interface");
+                                               end_trace();
+                                               continue;
+                                       }
+                                       /* creating EXTERNAL port*/
+                                       SPRINT(portname, "%s-%d-out", mISDNport->ifport->interface->name, mISDNport->portnum);
+
+#ifdef WITH_SS5
+                                       if (mISDNport->ss5)
+                                               port = ss5_hunt_line(mISDNport);
+                                       else
+#endif
+#ifdef ISDN_P_FXS_POTS
+                                       if (mISDNport->pots)
+                                               port = new Pfxs(PORT_TYPE_POTS_FXS_OUT, mISDNport, portname, &port_settings, mISDNport->ifport->interface, mode);
+                                       else
+#endif
+                                               port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, mISDNport->ifport->interface, channel, mISDNport->ifport->channel_force, mode);
+                                       earlyb = mISDNport->earlyb;
 #else
-                               trace_header("INTERFACE (has no function)", DIRECTION_NONE);
-                               add_trace("interface", NULL, "%s", ifname);
-                               end_trace();
-                               continue;
+                                       trace_header("INTERFACE (has no function)", DIRECTION_NONE);
+                                       add_trace("interface", NULL, "%s", ifname);
+                                       end_trace();
+                                       continue;
 #endif
-                       }
-                       if (!port)
-                               FATAL("No memory for Port instance\n");
-                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) created port %s\n", ea_endpoint->ep_serial, port->p_name);
-                       memset(&dialinginfo, 0, sizeof(dialinginfo));
-                       if (e_dialinginfo.keypad[0])
-                               SCPY(dialinginfo.keypad, number);
-                       else
-                               SCPY(dialinginfo.id, number);
-                       dialinginfo.itype = INFO_ITYPE_ISDN;
-                       dialinginfo.ntype = e_dialinginfo.ntype;
-                       dialinginfo.sending_complete = e_dialinginfo.sending_complete;
-                       portlist = ea_endpoint->portlist_new(port->p_serial, port->p_type, earlyb);
-                       if (!portlist) {
-                               PERROR("EPOINT(%d) cannot allocate port_list relation\n", ea_endpoint->ep_serial);
-                               delete port;
-                               goto check_anycall_extern;
-                       }
-//printf("EXTERNAL caller=%s,id=%s,dial=%s\n", param.setup.networkid, param.setup.callerinfo.id, param.setup.dialinginfo.id);
-                       message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_SETUP);
-                       memcpy(&message->param.setup.dialinginfo, &dialinginfo, sizeof(struct dialing_info));
-                       memcpy(&message->param.setup.redirinfo, &e_redirinfo, sizeof(struct redir_info));
-                       memcpy(&message->param.setup.callerinfo, &e_callerinfo, sizeof(struct caller_info));
-                       memcpy(&message->param.setup.capainfo, &e_capainfo, sizeof(struct capa_info));
-                       memcpy(&message->param.setup.rtpinfo, &e_rtpinfo, sizeof(struct rtp_info));
-//terminal                     SCPY(message->param.setup.from_terminal, e_ext.number);
-//terminal                     if (e_dialinginfo.id)
-//terminal                             SCPY(message->param.setup.to_terminal, e_dialinginfo.id);
-                               /* handle restricted caller ids */
-                       apply_callerid_restriction(&e_ext, message->param.setup.callerinfo.id, &message->param.setup.callerinfo.ntype, &message->param.setup.callerinfo.present, &message->param.setup.callerinfo.screen, message->param.setup.callerinfo.extension, message->param.setup.callerinfo.name);
-                       apply_callerid_restriction(&e_ext, message->param.setup.callerinfo.id2, &message->param.setup.callerinfo.ntype2, &message->param.setup.callerinfo.present2, &message->param.setup.callerinfo.screen2, message->param.setup.callerinfo.extension, message->param.setup.callerinfo.name);
-                       apply_callerid_restriction(&e_ext, message->param.setup.redirinfo.id, &message->param.setup.redirinfo.ntype, &message->param.setup.redirinfo.present, 0, message->param.setup.redirinfo.extension, NULL);
-                       /* display callerid if desired for extension */
-                       SCPY(message->param.setup.callerinfo.display, apply_callerid_display(message->param.setup.callerinfo.id, message->param.setup.callerinfo.itype, message->param.setup.callerinfo.ntype, message->param.setup.callerinfo.present, message->param.setup.callerinfo.screen, message->param.setup.callerinfo.extension, message->param.setup.callerinfo.name));
-                       message_put(message);
-                       logmessage(message->type, &message->param, portlist->port_id, DIRECTION_OUT);
-                       anycall = 1;
-               } while(*p);
+                               }
+                               if (!port)
+                                       FATAL("No memory for Port instance\n");
+                               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) created port %s\n", ea_endpoint->ep_serial, port->p_name);
+                               memset(&dialinginfo, 0, sizeof(dialinginfo));
+                               if (e_dialinginfo.keypad[0])
+                                       SCPY(dialinginfo.keypad, number);
+                               else
+                                       SCPY(dialinginfo.id, number);
+                               dialinginfo.itype = INFO_ITYPE_ISDN;
+                               dialinginfo.ntype = e_dialinginfo.ntype;
+                               dialinginfo.sending_complete = e_dialinginfo.sending_complete;
+                               portlist = ea_endpoint->portlist_new(port->p_serial, port->p_type, earlyb);
+                               if (!portlist) {
+                                       PERROR("EPOINT(%d) cannot allocate port_list relation\n", ea_endpoint->ep_serial);
+                                       delete port;
+                                       continue;
+                               }
+       //printf("EXTERNAL caller=%s,id=%s,dial=%s\n", param.setup.networkid, param.setup.callerinfo.id, param.setup.dialinginfo.id);
+                               message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_SETUP);
+                               memcpy(&message->param.setup.dialinginfo, &dialinginfo, sizeof(struct dialing_info));
+                               memcpy(&message->param.setup.redirinfo, &e_redirinfo, sizeof(struct redir_info));
+                               memcpy(&message->param.setup.callerinfo, &e_callerinfo, sizeof(struct caller_info));
+                               memcpy(&message->param.setup.capainfo, &e_capainfo, sizeof(struct capa_info));
+                               memcpy(&message->param.setup.rtpinfo, &e_rtpinfo, sizeof(struct rtp_info));
+       //terminal                      SCPY(message->param.setup.from_terminal, e_ext.number);
+       //terminal                      if (e_dialinginfo.id)
+       //terminal                              SCPY(message->param.setup.to_terminal, e_dialinginfo.id);
+                                       /* handle restricted caller ids */
+                               apply_callerid_restriction(&e_ext, message->param.setup.callerinfo.id, &message->param.setup.callerinfo.ntype, &message->param.setup.callerinfo.present, &message->param.setup.callerinfo.screen, message->param.setup.callerinfo.extension, message->param.setup.callerinfo.name);
+                               apply_callerid_restriction(&e_ext, message->param.setup.callerinfo.id2, &message->param.setup.callerinfo.ntype2, &message->param.setup.callerinfo.present2, &message->param.setup.callerinfo.screen2, message->param.setup.callerinfo.extension, message->param.setup.callerinfo.name);
+                               apply_callerid_restriction(&e_ext, message->param.setup.redirinfo.id, &message->param.setup.redirinfo.ntype, &message->param.setup.redirinfo.present, 0, message->param.setup.redirinfo.extension, NULL);
+                               /* display callerid if desired for extension */
+                               SCPY(message->param.setup.callerinfo.display, apply_callerid_display(message->param.setup.callerinfo.id, message->param.setup.callerinfo.itype, message->param.setup.callerinfo.ntype, message->param.setup.callerinfo.present, message->param.setup.callerinfo.screen, message->param.setup.callerinfo.extension, message->param.setup.callerinfo.name));
+                               message_put(message);
+                               logmessage(message->type, &message->param, portlist->port_id, DIRECTION_OUT);
+                               anycall = 1;
+
+                               /* found an interface
+                                * continue only if + is given, so every interface is calles parallel */
+                               if (e_dialinginfo.interfaces[0] != '+')
+                                       break;
+                       } while (*ifname_p);
+               } while(*number_p);
 
-               check_anycall_extern:
                /* now we have all ports created */
                if (!anycall) {
                        trace_header("INTERFACE (no free ports found)", DIRECTION_NONE);
@@ -1643,7 +1400,8 @@ void EndpointAppPBX::port_setup(struct port_list *portlist, int message_type, un
        /* screen incoming caller id */
        if (e_callerinfo.interface[0]) {
                do_screen(0, e_callerinfo.id, sizeof(e_callerinfo.id), &e_callerinfo.ntype, &e_callerinfo.present, e_callerinfo.interface);
-               do_screen(0, e_callerinfo.id2, sizeof(e_callerinfo.id2), &e_callerinfo.ntype2, &e_callerinfo.present2, e_callerinfo.interface);
+               if (e_callerinfo.id2[0]) do_screen(0, e_callerinfo.id2, sizeof(e_callerinfo.id2), &e_callerinfo.ntype2, &e_callerinfo.present2, e_callerinfo.interface);
+               if (e_redirinfo.id[0]) do_screen(0, e_redirinfo.id, sizeof(e_redirinfo.id), &e_redirinfo.ntype, &e_redirinfo.present, e_callerinfo.interface);
        }
 
        /* process extension */
@@ -1847,8 +1605,7 @@ void EndpointAppPBX::port_information(struct port_list *portlist, int message_ty
        }
        if (e_action)
        if (e_action->index==ACTION_OUTDIAL
-        || e_action->index==ACTION_EXTERNAL
-        || e_action->index==ACTION_REMOTE) {
+        || e_action->index==ACTION_EXTERNAL) {
                if (!e_extdialing)
                        set_tone(portlist, "dialing");
                else if (!e_extdialing[0])
@@ -2213,6 +1970,16 @@ void EndpointAppPBX::port_connect(struct port_list *portlist, int message_type,
                message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_join_id, EPOINT_TO_JOIN, MESSAGE_AUDIOPATH);
                message->param.audiopath = 1;
                message_put(message);
+               if (e_ext.dov_ident[0]) {
+                       message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_join_id, EPOINT_TO_JOIN, MESSAGE_DOV_REQUEST);
+                       SPRINT(message->param.dov.data, "%08x ", lcr_random);
+                       SCAT(message->param.dov.data, e_ext.dov_ident);
+                       message->param.dov.length = strlen((char *)message->param.dov.data);
+                       message->param.dov.type = e_ext.dov_type;
+                       message->param.dov.level = e_ext.dov_level;
+                       dov_msg_write(&message->param, 1);
+                       message_put(message);
+               }
        } else if (!e_adminid) {
                /* callback */
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) we have a callback, so we create a call with cbcaller: \"%s\".\n", ea_endpoint->ep_serial, e_cbcaller);
@@ -2505,8 +2272,6 @@ void EndpointAppPBX::port_notify(struct port_list *portlist, int message_type, u
        logmessage(message_type, param, portlist->port_id, DIRECTION_IN);
 
        struct lcr_msg *message;
-       const char *logtext = "";
-       char buffer[64];
 
        /* signal to call tool */
        admin_call_response(e_adminid, ADMIN_CALL_NOTIFY, numberrize_callerinfo(param->notifyinfo.id,param->notifyinfo.ntype, options.national, options.international), 0, 0, param->notifyinfo.notify);
@@ -2550,86 +2315,6 @@ void EndpointAppPBX::port_notify(struct port_list *portlist, int message_type, u
                break;
        }
 
-       /* get name of notify */
-       switch(param->notifyinfo.notify) {
-               case 0x00:
-               logtext = "NULL";
-               break;
-               case 0x80:
-               logtext = "USER_SUSPENDED";
-               break;
-               case 0x82:
-               logtext = "BEARER_SERVICE_CHANGED";
-               break;
-               case 0x81:
-               logtext = "USER_RESUMED";
-               break;
-               case 0xc2:
-               logtext = "CONFERENCE_ESTABLISHED";
-               break;
-               case 0xc3:
-               logtext = "CONFERENCE_DISCONNECTED";
-               break;
-               case 0xc4:
-               logtext = "OTHER_PARTY_ADDED";
-               break;
-               case 0xc5:
-               logtext = "ISOLATED";
-               break;
-               case 0xc6:
-               logtext = "REATTACHED";
-               break;
-               case 0xc7:
-               logtext = "OTHER_PARTY_ISOLATED";
-               break;
-               case 0xc8:
-               logtext = "OTHER_PARTY_REATTACHED";
-               break;
-               case 0xc9:
-               logtext = "OTHER_PARTY_SPLIT";
-               break;
-               case 0xca:
-               logtext = "OTHER_PARTY_DISCONNECTED";
-               break;
-               case 0xcb:
-               logtext = "CONFERENCE_FLOATING";
-               break;
-               case 0xcc:
-               logtext = "CONFERENCE_DISCONNECTED_PREEMTED";
-               break;
-               case 0xcf:
-               logtext = "CONFERENCE_FLOATING_SERVED_USER_PREEMTED";
-               break;
-               case 0xe0:
-               logtext = "CALL_IS_A_WAITING_CALL";
-               break;
-               case 0xe8:
-               logtext = "DIVERSION_ACTIVATED";
-               break;
-               case 0xe9:
-               logtext = "RESERVED_CT_1";
-               break;
-               case 0xea:
-               logtext = "RESERVED_CT_2";
-               break;
-               case 0xee:
-               logtext = "REVERSE_CHARGING";
-               break;
-               case 0xf9:
-               logtext = "REMOTE_HOLD";
-               break;
-               case 0xfa:
-               logtext = "REMOTE_RETRIEVAL";
-               break;
-               case 0xfb:
-               logtext = "CALL_IS_DIVERTING";
-               break;
-               default:
-               SPRINT(buffer, "%d", param->notifyinfo.notify - 0x80);
-               logtext = buffer;
-
-       }
-
        /* notify call if available */
        if (ea_endpoint->ep_join_id) {
                message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_join_id, EPOINT_TO_JOIN, MESSAGE_NOTIFY);
@@ -2670,6 +2355,50 @@ void EndpointAppPBX::port_facility(struct port_list *portlist, int message_type,
        message_put(message);
 }
 
+/* port MESSAGE_3PTY */
+void EndpointAppPBX::port_3pty(struct port_list *portlist, int message_type, union parameter *param)
+{
+       logmessage(message_type, param, portlist->port_id, DIRECTION_IN);
+
+       struct lcr_msg *message;
+       int rc;
+
+       /* 3PTY bridge */
+       if (param->threepty.begin)
+               rc = join_3pty_dss1();
+       else if (param->threepty.end)
+               rc = split_3pty();
+       else
+               return;
+
+       message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_3PTY);
+       message->param.threepty.begin = param->threepty.begin;
+       message->param.threepty.end = param->threepty.end;
+       if (rc < 0)
+               message->param.threepty.error = 1;
+       else
+               message->param.threepty.result = 1;
+       message->param.threepty.invoke_id = param->threepty.invoke_id;
+       logmessage(message->type, &message->param, portlist->port_id, DIRECTION_OUT);
+       message_put(message);
+}
+
+/* port MESSAGE_TRANSFER */
+void EndpointAppPBX::port_transfer(struct port_list *portlist, int message_type, union parameter *param)
+{
+       logmessage(message_type, param, portlist->port_id, DIRECTION_IN);
+
+       class Port *port;
+
+       /* bridge for real */
+       if (!(port = find_port_id(portlist->port_id)))
+               return;
+       if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS)
+               join_join_fxs();
+       else if ((port->p_type & PORT_CLASS_mISDN_MASK) == PORT_CLASS_DSS1)
+               join_join_dss1();
+}
+
 /* port MESSAGE_SUSPEND */
 /* NOTE: before supending, the inactive-notification must be done in order to set call mixer */
 void EndpointAppPBX::port_suspend(struct port_list *portlist, int message_type, union parameter *param)
@@ -2709,6 +2438,49 @@ void EndpointAppPBX::port_enablekeypad(struct port_list *portlist, int message_t
 }
 
 
+/* port MESSAGE_DISABLE_DEJITTER */
+void EndpointAppPBX::port_disable_dejitter(struct port_list *portlist, int message_type, union parameter *param)
+{
+       struct lcr_msg *message;
+
+       logmessage(message_type, param, portlist->port_id, DIRECTION_IN);
+
+       message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_join_id, EPOINT_TO_JOIN, MESSAGE_DISABLE_DEJITTER);
+       memcpy(&message->param, param, sizeof(union parameter));
+       message_put(message);
+}
+
+/* port MESSAGE_DOV_INDICATION */
+void EndpointAppPBX::port_dov_indication(struct port_list *portlist, int message_type, union parameter *param)
+{
+       struct lcr_msg *message;
+
+       logmessage(message_type, param, portlist->port_id, DIRECTION_IN);
+
+       message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_join_id, EPOINT_TO_JOIN, MESSAGE_DOV_INDICATION);
+       memcpy(&message->param, param, sizeof(union parameter));
+       message_put(message);
+}
+
+
+/* port MESSAGE_UPDATEBRIDGE  */
+void EndpointAppPBX::port_updatebridge(struct port_list *portlist, int message_type, union parameter *param)
+{
+       struct lcr_msg *message;
+
+       message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_join_id, EPOINT_TO_JOIN, MESSAGE_UPDATEBRIDGE);
+       message_put(message);
+}
+
+
+/* port MESSAGE_VOOTP  */
+void EndpointAppPBX::port_vootp(struct port_list *portlist, int message_type, union parameter *param)
+{
+       if (param->vootp.failed)
+               set_tone(ea_endpoint->ep_portlist, "crypt_off");
+}
+
+
 /* port sends message to the endpoint
  */
 void EndpointAppPBX::ea_message_port(unsigned int port_id, int message_type, union parameter *param)
@@ -2772,6 +2544,16 @@ void EndpointAppPBX::ea_message_port(unsigned int port_id, int message_type, uni
                port_facility(portlist, message_type, param);
                break;
 
+               case MESSAGE_3PTY:
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) incoming 3PTY facility (terminal '%s', caller id '%s')\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id);
+               port_3pty(portlist, message_type, param);
+               break;
+
+               case MESSAGE_TRANSFER:
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) incoming TRANSFER request (terminal '%s', caller id '%s')\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id);
+               port_transfer(portlist, message_type, param);
+               break;
+
                /* PORT sends DTMF message */
                case MESSAGE_DTMF: /* dtmf digits received */
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) dtmf digit=%c (terminal '%s', caller id '%s')\n", ea_endpoint->ep_serial, param->dtmf, e_ext.number, e_callerinfo.id);
@@ -2890,6 +2672,26 @@ void EndpointAppPBX::ea_message_port(unsigned int port_id, int message_type, uni
                port_enablekeypad(portlist, message_type, param);
                break;
 
+               case MESSAGE_DISABLE_DEJITTER:
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) incoming disable dejitter message (terminal '%s', caller id '%s')\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id);
+               port_disable_dejitter(portlist, message_type, param);
+               break;
+
+               case MESSAGE_UPDATEBRIDGE:
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) incoming updatebridge message (terminal '%s', caller id '%s')\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id);
+               port_updatebridge(portlist, message_type, param);
+               break;
+
+               case MESSAGE_VOOTP:
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) incoming vootp message (terminal '%s', caller id '%s')\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id);
+               port_vootp(portlist, message_type, param);
+               break;
+
+               /* PORT indivated Data-Over-Voice */
+               case MESSAGE_DOV_INDICATION:
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) epoint with terminal '%s' (caller id '%s') indicates Data-Over-Voice.\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id);
+               port_dov_indication(portlist, message_type, param);
+               break;
 
                default:
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) epoint with terminal '%s' (caller id '%s') received a wrong message: %d\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id, message_type);
@@ -3109,6 +2911,13 @@ void EndpointAppPBX::join_connect(struct port_list *portlist, int message_type,
        message_put(message);
        time(&now);
        e_start = now;
+
+       /* if the remote answered, we listen to DOV message */
+       if (e_ext.dov_log[0]) {
+               message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_join_id, EPOINT_TO_JOIN, MESSAGE_DOV_LISTEN);
+               message->param.dov.type = e_ext.dov_type;
+               message_put(message);
+       }
 }
 
 /* join MESSAGE_DISCONNECT MESSAGE_RELEASE */
@@ -3414,6 +3223,56 @@ void EndpointAppPBX::join_dtmf(struct port_list *portlist, int message_type, uni
        }
 }
 
+/* join MESSAGE_DISABLE_DEJITTER */
+void EndpointAppPBX::join_disable_dejitter(struct port_list *portlist, int message_type, union parameter *param)
+{
+       struct lcr_msg *message;
+
+       while(portlist) {
+               message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_DISABLE_DEJITTER);
+               memcpy(&message->param, param, sizeof(union parameter));
+               message_put(message);
+               portlist = portlist->next;
+       }
+}
+
+/* join MESSAGE_DOV_INDICATION */
+void EndpointAppPBX::join_dov_indication(struct port_list *portlist, int message_type, union parameter *param)
+{
+       dov_msg_write(param, 0);
+}
+
+/* join MESSAGE_DOV_REQUEST */
+void EndpointAppPBX::join_dov_request(struct port_list *portlist, int message_type, union parameter *param)
+{
+       struct lcr_msg *message;
+
+       /* don't send DOV from estension to extension */
+       if (e_ext.number[0])
+               return;
+
+       while(portlist) {
+               message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_DOV_REQUEST);
+               memcpy(&message->param, param, sizeof(union parameter));
+               message_put(message);
+               logmessage(message_type, param, portlist->port_id, DIRECTION_OUT);
+               portlist = portlist->next;
+       }
+}
+
+/* join MESSAGE_DOV_LISTEN */
+void EndpointAppPBX::join_dov_listen(struct port_list *portlist, int message_type, union parameter *param)
+{
+       struct lcr_msg *message;
+
+       while(portlist) {
+               message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_DOV_LISTEN);
+               memcpy(&message->param, param, sizeof(union parameter));
+               message_put(message);
+               portlist = portlist->next;
+       }
+}
+
 /* JOIN sends messages to the endpoint
  */
 void EndpointAppPBX::ea_message_join(unsigned int join_id, int message_type, union parameter *param)
@@ -3585,6 +3444,30 @@ void EndpointAppPBX::ea_message_join(unsigned int join_id, int message_type, uni
                join_dtmf(portlist, message_type, param);
                break;
 
+               /* JOIN sends a DISABLE_DEJITTER message */
+               case MESSAGE_DISABLE_DEJITTER:
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) epoint with terminal '%s' (caller id '%s') received disable dejitter.\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id);
+               join_disable_dejitter(portlist, message_type, param);
+               break;
+
+               /* JOIN sends a Data-Over-Voice message indication */
+               case MESSAGE_DOV_INDICATION:
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) epoint with terminal '%s' (caller id '%s') received Data-Over-Voice indication.\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id);
+               join_dov_indication(portlist, message_type, param);
+               break;
+
+               /* JOIN sends a Data-Over-Voice message request */
+               case MESSAGE_DOV_REQUEST:
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) epoint with terminal '%s' (caller id '%s') received Data-Over-Voice request.\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id);
+               join_dov_request(portlist, message_type, param);
+               break;
+
+               /* JOIN sends a Data-Over-Voice listen order */
+               case MESSAGE_DOV_LISTEN:
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) epoint with terminal '%s' (caller id '%s') received Data-Over-Voice listen order.\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id);
+               join_dov_listen(portlist, message_type, param);
+               break;
+
                default:
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) epoint with terminal '%s' (caller id '%s') received a wrong message: #%d\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id, message_type);
        }
@@ -3636,10 +3519,8 @@ void EndpointAppPBX::pick_join(char *extensions)
        class Join *join;
        class JoinPBX *joinpbx;
        struct join_relation *relation;
-       int vbox;
 
        /* find an endpoint that is ringing internally or vbox with higher priority */
-       vbox = 0;
        found = NULL;
        eapp = apppbx_first;
        while(eapp) {
@@ -3650,7 +3531,6 @@ void EndpointAppPBX::pick_join(char *extensions)
                                        if (port->p_type == PORT_TYPE_VBOX_OUT) {
                                                if (match_list(extensions, eapp->e_ext.number)) {
                                                        found = eapp;
-                                                       vbox = 1;
                                                        break;
                                                }
                                        }
@@ -3811,49 +3691,50 @@ reject:
 
 /* join calls (look for a join that is on hold (same isdn interface/terminal))
  */
-void EndpointAppPBX::join_join(void)
+int EndpointAppPBX::join_join_dss1(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);
        if (!our_join) {
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: our join doesn't exist anymore.\n", ea_endpoint->ep_serial);
-               return;
+               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;
+               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;
+               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;
+               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;
+               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;
+               return -1;
        }
        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) {
@@ -3870,7 +3751,7 @@ void EndpointAppPBX::join_join(void)
                                 || 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 (other_pdss1->p_m_hold /* port is on hold */
+                                       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;
@@ -3885,155 +3766,643 @@ void EndpointAppPBX::join_join(void)
        }
        if (!other_eapp) {
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: no other endpoint on same isdn terminal.\n", ea_endpoint->ep_serial);
-               return;
+               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;
+               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;
+               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;
+               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;
+               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(&our_joinpbx->j_updatebridge);
+       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;
+}
 
-/* check if we have an external call
- * this is used to check for encryption ability
+/* join calls (look for a join that is on hold (same fxs interface/terminal))
  */
-int EndpointAppPBX::check_external(const char **errstr, class Port **port)
+int EndpointAppPBX::join_join_fxs(void)
 {
-       struct join_relation *relation;
-       class Join *join;
-       class JoinPBX *joinpbx;
-       class Endpoint *epoint;
+#ifdef WITH_MISDN
+       struct lcr_msg *message;
+       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 Pfxs *our_fxs, *other_fxs;
+       class Endpoint *temp_epoint;
 
-       /* some paranoia check */
+       /* 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) error: we have no port.\n", ea_endpoint->ep_serial);
-               *errstr = "No Call";
-               return(1);
+               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) error: we are not internal extension.\n", ea_endpoint->ep_serial);
-               *errstr = "No Call";
-               return(1);
+               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_POTS_MASK) != PORT_CLASS_POTS_FXS) {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: our port is not fxs.\n", ea_endpoint->ep_serial);
+               return -1;
        }
+       our_fxs = (class Pfxs *)our_port;
 
-       /* check if we have a join with 2 parties */
-       join = find_join_id(ea_endpoint->ep_join_id);
-       if (!join) {
-               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) we have currently no join.\n", ea_endpoint->ep_serial);
-               *errstr = "No Call";
-               return(1);
+       /* find an endpoint that has the same mISDNport 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_POTS_FXS_OUT
+                                || other_port->p_type==PORT_TYPE_POTS_FXS_IN) { /* port is FXS */
+                                       other_fxs = (class Pfxs *)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 state=%d\n", ea_endpoint->ep_serial, our_fxs->p_m_mISDNport->portnum, other_fxs->p_m_mISDNport->portnum, (other_fxs->p_m_hold)?"YES":"NO", other_fxs->p_state);
+                                       if (1 //other_fxs->p_m_hold /* port is on hold */
+                                        && other_fxs->p_m_mISDNport == our_fxs->p_m_mISDNport) /* same isdn interface */
+                                               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 (join->j_type != JOIN_TYPE_PBX) {
-               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) join is not a pbx join.\n", ea_endpoint->ep_serial);
-               *errstr = "No PBX Call";
-               return(1);
+       if (!other_eapp) {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: no other endpoint on same FXS terminal.\n", ea_endpoint->ep_serial);
+               return -1;
        }
-       joinpbx = (class JoinPBX *)join;
-       relation = joinpbx->j_relation;
-       if (!relation) {
-               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) join has no relation.\n", ea_endpoint->ep_serial);
-               *errstr = "No Call";
-               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;
        }
-       if (!relation->next) {
-               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) join has no 2nd relation.\n", ea_endpoint->ep_serial);
-               *errstr = "No Call";
-               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 (relation->next->next) {
-               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) join has more than two relations.\n", ea_endpoint->ep_serial);
-               *errstr = "Err: Conference";
-               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;
        }
-       if (relation->epoint_id == ea_endpoint->ep_serial) {
-               relation = relation->next;
-               if (relation->epoint_id == ea_endpoint->ep_serial) {
-                       PERROR("EPOINT(%d) SOFTWARE ERROR: both join relations are related to our endpoint.\n", ea_endpoint->ep_serial);
-                       *errstr = "Software Error";
-                       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;
        }
 
-       /* check remote port for external call */
-       epoint = find_epoint_id(relation->epoint_id);
-       if (!epoint) {
-               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) join has no 2nd endpoint.\n", ea_endpoint->ep_serial);
+       /* now find out which is ACTIVE-IDLE and which is ACTIVE-HELD */
+       if (our_fxs->p_m_hold && !other_fxs->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 */
+       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--;
+                       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(remove_relation->epoint_id);
+               if (temp_epoint) {
+                       if (temp_epoint->ep_join_id == remove_join->j_serial)
+                               temp_epoint->ep_join_id = add_join->j_serial;
+               }
+
+               remove_relation_pointer = &remove_relation->next;
+               remove_relation = remove_relation->next;
+       }
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) endpoint removed, other enpoints on join relinked.\n", ea_endpoint->ep_serial);
+
+       /* join call relations */
+       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 */
+       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 */
+       add_joinpbx->j_partyline += remove_joinpbx->j_partyline; 
+
+       /* remove empty join */
+       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_dss1(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
+#else
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: no mISDN support anyway.\n", ea_endpoint->ep_serial);
+#endif
+
+       return 0;
+}
+
+int EndpointAppPBX::join_3pty_fxs(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 Pfxs *our_fxs, *other_fxs;
+
+       /* 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_POTS_MASK) != PORT_CLASS_POTS_FXS) {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: our port is not FXS pots.\n", ea_endpoint->ep_serial);
+               return -1;
+       }
+       our_fxs = (class Pfxs *)our_port;
+
+       /* find an endpoint that has the same mISDNport 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_POTS_FXS_OUT
+                                || other_port->p_type==PORT_TYPE_POTS_FXS_IN) { /* port is isdn nt-mode */
+                                       other_fxs = (class Pfxs *)other_port;
+                                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint's port is of type FXS! comparing our portnum=%d with other's portnum=%d hold=%s state=%d\n", ea_endpoint->ep_serial, our_fxs->p_m_mISDNport->portnum, other_fxs->p_m_mISDNport->portnum, (other_fxs->p_m_hold)?"YES":"NO", other_fxs->p_state);
+                                       if (1 //other_fxs->p_m_hold /* port is on hold */
+                                        && other_fxs->p_m_mISDNport == our_fxs->p_m_mISDNport) /* same pots interface */
+                                               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 FXS 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
+#else
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: no mISDN support anyway.\n", ea_endpoint->ep_serial);
+#endif
+
+       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
+ */
+int EndpointAppPBX::check_external(const char **errstr, class Port **port)
+{
+       struct join_relation *relation;
+       class Join *join;
+       class JoinPBX *joinpbx;
+       class Endpoint *epoint;
+
+       /* some paranoia check */
+       if (!ea_endpoint->ep_portlist) {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) error: we have no port.\n", ea_endpoint->ep_serial);
+               *errstr = "No Call";
+               return(1);
+       }
+       if (!e_ext.number[0]) {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) error: we are not internal extension.\n", ea_endpoint->ep_serial);
+               *errstr = "No Call";
+               return(1);
+       }
+
+       /* check if we have a join with 2 parties */
+       join = find_join_id(ea_endpoint->ep_join_id);
+       if (!join) {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) we have currently no join.\n", ea_endpoint->ep_serial);
+               *errstr = "No Call";
+               return(1);
+       }
+       if (join->j_type != JOIN_TYPE_PBX) {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) join is not a pbx join.\n", ea_endpoint->ep_serial);
+               *errstr = "No PBX Call";
+               return(1);
+       }
+       joinpbx = (class JoinPBX *)join;
+       relation = joinpbx->j_relation;
+       if (!relation) {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) join has no relation.\n", ea_endpoint->ep_serial);
+               *errstr = "No Call";
+               return(1);
+       }
+       if (!relation->next) {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) join has no 2nd relation.\n", ea_endpoint->ep_serial);
+               *errstr = "No Call";
+               return(1);
+       }
+       if (relation->next->next) {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) join has more than two relations.\n", ea_endpoint->ep_serial);
+               *errstr = "Err: Conference";
+               return(1);
+       }
+       if (relation->epoint_id == ea_endpoint->ep_serial) {
+               relation = relation->next;
+               if (relation->epoint_id == ea_endpoint->ep_serial) {
+                       PERROR("EPOINT(%d) SOFTWARE ERROR: both join relations are related to our endpoint.\n", ea_endpoint->ep_serial);
+                       *errstr = "Software Error";
+                       return(1);
+               }
+       }
+
+       /* check remote port for external call */
+       epoint = find_epoint_id(relation->epoint_id);
+       if (!epoint) {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) join has no 2nd endpoint.\n", ea_endpoint->ep_serial);
                *errstr = "No Call";
                return(1);
        }
@@ -4061,6 +4430,30 @@ int EndpointAppPBX::check_external(const char **errstr, class Port **port)
        return(0);
 }
 
+int EndpointAppPBX::vootp_on(int on)
+{
+#ifndef WITH_VOOTP
+       set_tone(ea_endpoint->ep_portlist, "crypt_off");
+#else
+       if (!e_ext.otp_ident[0]) {
+               set_tone(ea_endpoint->ep_portlist, "crypt_off");
+               return -EINVAL;
+       }
+       if(ea_endpoint->ep_portlist) {
+               struct lcr_msg *message;
+
+               message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_VOOTP);
+               message->param.vootp.enable = on;
+               SCPY(message->param.vootp.id, e_ext.otp_ident);
+               message_put(message);
+       }
+       if (!on)
+               set_tone(ea_endpoint->ep_portlist, "crypt_off");
+#endif
+
+       return 0;
+}
+
 void EndpointAppPBX::logmessage(int message_type, union parameter *param, unsigned int port_id, int dir)
 {
        const char *logtext = "unknown";
@@ -4075,6 +4468,10 @@ void EndpointAppPBX::logmessage(int message_type, union parameter *param, unsign
                        add_trace("from", NULL, "CH(%lu)", port_id);
                if (param->setup.callerinfo.extension[0])
                        add_trace("extension", NULL, "%s", param->setup.callerinfo.extension);
+               if (param->setup.callerinfo.interface[0])
+                       add_trace("interface", "from", "%s", param->setup.callerinfo.interface);
+               if (param->setup.dialinginfo.interfaces[0])
+                       add_trace("interface", "to", "%s", param->setup.dialinginfo.interfaces);
                add_trace("caller id", "number", "%s", numberrize_callerinfo(param->setup.callerinfo.id, param->setup.callerinfo.ntype, options.national, options.international));
                switch(param->setup.callerinfo.present) {
                        case INFO_PRESENT_RESTRICTED:
@@ -4460,6 +4857,50 @@ void EndpointAppPBX::logmessage(int message_type, union parameter *param, unsign
                break;
 #endif
 
+               case MESSAGE_3PTY:
+               if (param->threepty.begin)
+                       trace_header("Begin3PTY", dir);
+               if (param->threepty.end)
+                       trace_header("End3PTY", dir);
+               if (param->threepty.invoke)
+                       add_trace("action", NULL, "invoke");
+               if (param->threepty.result)
+                       add_trace("action", NULL, "result");
+               if (param->threepty.error)
+                       add_trace("action", NULL, "error");
+               add_trace("invoke-id", NULL, "%d", param->threepty.invoke_id);
+               end_trace();
+               break;
+
+               case MESSAGE_TRANSFER:
+               trace_header("TRANSFER", dir);
+               end_trace();
+               break;
+
+               case MESSAGE_DISABLE_DEJITTER:
+               trace_header("DISBALE_DEJITTER", dir);
+               if (param->queue)
+                       add_trace("queue", NULL, "%d", param->queue);
+               end_trace();
+               break;
+
+               case MESSAGE_DOV_INDICATION:
+               case MESSAGE_DOV_REQUEST:
+               trace_header("Data-Over-Voice", dir);
+               if (dir == DIRECTION_OUT)
+                       add_trace("to", NULL, "CH(%lu)", port_id);
+               if (dir == DIRECTION_IN)
+                       add_trace("from", NULL, "CH(%lu)", port_id);
+               {
+                       char dov_str[param->dov.length + 1];
+                       memcpy(dov_str, param->dov.data, param->dov.length);
+                       dov_str[param->dov.length] = '\0';
+                       add_trace("string", NULL, "%s", dov_str);
+               }
+               add_trace("type", NULL, "%d", param->dov.type);
+               end_trace();
+               break;
+
                default:
                PERROR("EPOINT(%d) message not of correct type (%d)\n", ea_endpoint->ep_serial, message_type);
        }
@@ -4493,3 +4934,38 @@ void EndpointAppPBX::message_disconnect_port(struct port_list *portlist, int cau
        logmessage(message->type, &message->param, portlist->port_id, DIRECTION_OUT);
 }
 
+void EndpointAppPBX::dov_msg_write(union parameter *param, int sent)
+{
+       FILE *fp;
+       struct tm *tm;
+       time_t ti;
+       int __attribute__((__unused__)) rc;
+
+       /* no write, if no log file given */
+       if (!e_ext.dov_log[0])
+               return;
+
+       fp = fopen(e_ext.dov_log, "a");
+       if (!fp) {
+               PERROR("EPOINT(%d) failed to open Data-Over-Voice log file '%s'\n", ea_endpoint->ep_serial, e_ext.dov_log);
+               return;
+       }
+
+       ti = time(NULL);
+       tm = localtime(&ti);
+       fprintf(fp, "%02d.%02d.%02d %02d:%02d:%02d ", tm->tm_mday, tm->tm_mon+1, tm->tm_year%100, tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+
+       if (sent) {
+               fprintf(fp, "sent [%s] ", numberrize_callerinfo(e_callerinfo.id, e_callerinfo.ntype, options.national, options.international));
+       } else {
+               fprintf(fp, "received [%s] ", e_dialinginfo.id);
+       }
+
+       rc = fwrite(param->dov.data, param->dov.length, 1, fp);
+
+       fprintf(fp, "\n");
+
+       fclose(fp);
+}
+