SIP: Fix incoming re-invite
[lcr.git] / endpointapp.cpp
index 9479033..a3ad7dd 100644 (file)
@@ -9,16 +9,15 @@
 **                                                                           **
 \*****************************************************************************/ 
 
-
-#include <stdio.h>
 #include "main.h"
 
 /*
  * EndpointApp constructor
  */
-EndpointApp::EndpointApp(class Endpoint *epoint, int origin)
+EndpointApp::EndpointApp(class Endpoint *epoint, int origin, int type)
 {
        ea_endpoint = epoint;
+       ea_type = type;
        classuse++;
 }
 
@@ -30,20 +29,343 @@ EndpointApp::~EndpointApp(void)
        classuse--;
 }
 
-int EndpointApp::handler(void)
-{
-       return(0);
-}
-
 /* mini application for test purpose only */
 
-void EndpointApp::ea_message_port(unsigned long port_id, int message_type, union parameter *param)
+void EndpointApp::ea_message_port(unsigned int port_id, int message_type, union parameter *param)
 {
        PDEBUG(DEBUG_EPOINT, "%s: Spare function.\n", __FUNCTION__);
 }
 
-void EndpointApp::ea_message_join(unsigned long join_id, int message_type, union parameter *param)
+void EndpointApp::ea_message_join(unsigned int join_id, int message_type, union parameter *param)
 {
        PDEBUG(DEBUG_EPOINT, "%s: Spare function.\n", __FUNCTION__);
 }
 
+
+/* create endpoint app */
+class EndpointApp *new_endpointapp(class Endpoint *epoint, int origin, int type)
+{
+       class EndpointApp *app = NULL;
+
+       switch (type) {
+       case EAPP_TYPE_PBX:
+               app = new EndpointAppPBX(epoint, origin);
+               break;
+       case EAPP_TYPE_BRIDGE:
+               app = new EndpointAppBridge(epoint, origin);
+               break;
+       }
+
+       if (!app)
+               FATAL("Failed to create endpoint APP (type %d)\n", type);
+
+       epoint->ep_app_type = type;
+       epoint->ep_app = app;
+
+       return app;
+}
+
+#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 *EndpointApp::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
+
+/* hunts for the given interface
+ * it does not need to have an mISDNport instance */
+struct interface *EndpointApp::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;
+}
+
+/* must be overloaded by specific app */
+void EndpointApp::trace_header(const char *name, int direction)
+{
+}