Fix: Make action.cpp compile without mISDN/FXS support
[lcr.git] / action.cpp
index 8c06137..db8e101 100644 (file)
@@ -15,55 +15,26 @@ extern char **environ;
 
 
 /*
- * process init 'internal' / 'external' / 'remote' / 'vbox-record' / 'partyline'...
+ * process init 'internal' / 'external' / 'vbox-record' / 'partyline'...
  */
-int EndpointAppPBX::_action_init_call(char *remote)
+void EndpointAppPBX::action_init_call(void)
 {
        class Join              *join;
-       struct port_list        *portlist = ea_endpoint->ep_portlist;
-       struct admin_list       *admin;
 
        /* a created call, this should never happen */
        if (ea_endpoint->ep_join_id) {
                if (options.deb & DEBUG_EPOINT)
                        PERROR("EPOINT(%d): We already have a call instance, this should never happen!\n", ea_endpoint->ep_serial);
-               return(0);
+               return;
        }
 
        /* create join */
        PDEBUG(DEBUG_EPOINT, "EPOINT(%d): Creating new join instance.\n", ea_endpoint->ep_serial);
-       if (remote) {
-               admin = admin_first;
-               while(admin) {
-                       if (admin->remote_name[0] && !strcmp(admin->remote_name, remote))
-                               break;
-                       admin = admin->next;
-               }
-               if (!admin) {
-                       /* resource not available */
-                       trace_header("ACTION remote (not available)", DIRECTION_NONE);
-                       add_trace("application", NULL, "%s", remote);
-                       end_trace();
-                       message_disconnect_port(portlist, CAUSE_OUTOFORDER, LOCATION_PRIVATE_LOCAL, "");
-                       new_state(EPOINT_STATE_OUT_DISCONNECT);
-                       set_tone(portlist,"cause_1b");
-                       return(0);
-               }
-               join = new JoinRemote(ea_endpoint->ep_serial, remote, admin->sock);
-       }
-       else
-               join = new JoinPBX(ea_endpoint);
+       join = new JoinPBX(ea_endpoint);
        if (!join)
                FATAL("No memoy for Join instance.\n");
        ea_endpoint->ep_join_id = join->j_serial;
-       return(1);
-}
-void EndpointAppPBX::action_init_call(void)
-{
-       _action_init_call(NULL);
-}
-void EndpointAppPBX::action_init_remote(void)
-{
+       return;
 }
 
 /*
@@ -74,6 +45,7 @@ void EndpointAppPBX::action_dialing_internal(void)
        struct capa_info        capainfo;
        struct caller_info      callerinfo;
        struct redir_info       redirinfo;
+       struct rtp_info         rtpinfo;
        struct dialing_info     dialinginfo;
        struct port_list        *portlist = ea_endpoint->ep_portlist;
        struct lcr_msg          *message;
@@ -91,6 +63,7 @@ void EndpointAppPBX::action_dialing_internal(void)
        memcpy(&capainfo, &e_capainfo, sizeof(capainfo));
        memcpy(&callerinfo, &e_callerinfo, sizeof(callerinfo));
        memcpy(&redirinfo, &e_redirinfo, sizeof(redirinfo));
+       memcpy(&rtpinfo, &e_rtpinfo, sizeof(rtpinfo));
        memset(&dialinginfo, 0, sizeof(dialinginfo));
        dialinginfo.itype = INFO_ITYPE_ISDN_EXTENSION;
        SCPY(dialinginfo.id, e_dialinginfo.id);
@@ -111,6 +84,8 @@ void EndpointAppPBX::action_dialing_internal(void)
                        capainfo.bearer_mode = INFO_BMODE_PACKET;
                }
                capainfo.bearer_info1 = INFO_INFO1_NONE;
+               capainfo.hlc = INFO_HLC_NONE;
+               capainfo.exthlc = INFO_HLC_NONE;
        }
        if ((rparam = routeparam(e_action, PARAM_BMODE))) {
                capainfo.bearer_mode = rparam->integer_value;
@@ -135,7 +110,7 @@ void EndpointAppPBX::action_dialing_internal(void)
                trace_header("ACTION extension (extension doesn't exist)", DIRECTION_NONE);
                add_trace("extension", NULL, dialinginfo.id);
                end_trace();
-               release(RELEASE_JOIN, LOCATION_PRIVATE_LOCAL, CAUSE_NORMAL, 0, 0);
+               release(RELEASE_JOIN, LOCATION_PRIVATE_LOCAL, CAUSE_NORMAL, 0, 0, 0);
                new_state(EPOINT_STATE_OUT_DISCONNECT);
                message_disconnect_port(portlist, CAUSE_UNALLOCATED, LOCATION_PRIVATE_LOCAL, "");
                set_tone(portlist, "cause_86");
@@ -147,7 +122,7 @@ void EndpointAppPBX::action_dialing_internal(void)
                add_trace("extension", NULL, dialinginfo.id);
                end_trace();
                new_state(EPOINT_STATE_OUT_DISCONNECT);
-               release(RELEASE_JOIN, LOCATION_PRIVATE_LOCAL, CAUSE_NORMAL, 0, 0);
+               release(RELEASE_JOIN, LOCATION_PRIVATE_LOCAL, CAUSE_NORMAL, 0, 0, 0);
                message_disconnect_port(portlist, CAUSE_REJECTED, LOCATION_PRIVATE_LOCAL, "");
                set_tone(portlist, "cause_81");
                return;
@@ -162,6 +137,7 @@ void EndpointAppPBX::action_dialing_internal(void)
        memcpy(&message->param.setup.redirinfo, &redirinfo, sizeof(struct redir_info));
        memcpy(&message->param.setup.callerinfo, &callerinfo, sizeof(struct caller_info));
        memcpy(&message->param.setup.capainfo, &capainfo, sizeof(struct capa_info));
+       memcpy(&message->param.setup.rtpinfo, &rtpinfo, sizeof(struct rtp_info));
        message_put(message);
 }
 
@@ -172,6 +148,7 @@ void EndpointAppPBX::action_dialing_external(void)
        struct capa_info capainfo;
        struct caller_info callerinfo;
        struct redir_info redirinfo;
+       struct rtp_info rtpinfo;
        struct dialing_info dialinginfo;
        char *p;
        struct port_list *portlist = ea_endpoint->ep_portlist;
@@ -204,19 +181,27 @@ void EndpointAppPBX::action_dialing_external(void)
        memcpy(&capainfo, &e_capainfo, sizeof(capainfo));
        memcpy(&callerinfo, &e_callerinfo, sizeof(callerinfo));
        memcpy(&redirinfo, &e_redirinfo, sizeof(redirinfo));
+       memcpy(&rtpinfo, &e_rtpinfo, sizeof(rtpinfo));
        memset(&dialinginfo, 0, sizeof(dialinginfo));
        dialinginfo.itype = INFO_ITYPE_ISDN;
-       dialinginfo.sending_complete = 0;
+//     dialinginfo.sending_complete = 0;
        SCPY(dialinginfo.id, e_extdialing);
 
        /* process prefix */
        if ((rparam = routeparam(e_action, PARAM_PREFIX)))
                SPRINT(dialinginfo.id, "%s%s", rparam->string_value, e_extdialing);
 
+       if ((rparam = routeparam(e_action, PARAM_CONTEXT)))
+               SCPY(dialinginfo.context, rparam->string_value);
+
+       /* process keypad */
+       if ((rparam = routeparam(e_action, PARAM_KEYPAD))) {
+               SCPY(dialinginfo.keypad, dialinginfo.id);
+               dialinginfo.id[0] = '\0';
+       }
+
        /* process number complete */
        if ((rparam = routeparam(e_action, PARAM_COMPLETE)))
-               if ((rparam = routeparam(e_action, PARAM_PREFIX)))
-                       SCPY(dialinginfo.id, rparam->string_value);
                dialinginfo.sending_complete = 1;
 
        /* process number type */
@@ -231,6 +216,8 @@ void EndpointAppPBX::action_dialing_external(void)
                        capainfo.bearer_mode = INFO_BMODE_PACKET;
                }
                capainfo.bearer_info1 = INFO_INFO1_NONE;
+               capainfo.hlc = INFO_HLC_NONE;
+               capainfo.exthlc = INFO_HLC_NONE;
        }
        if ((rparam = routeparam(e_action, PARAM_BMODE))) {
                capainfo.bearer_mode = rparam->integer_value;
@@ -267,7 +254,7 @@ void EndpointAppPBX::action_dialing_external(void)
        if (e_ext.rights < 2) {
                trace_header("ACTION extern (calling denied)", DIRECTION_NONE);
                end_trace();
-               release(RELEASE_JOIN, LOCATION_PRIVATE_LOCAL, CAUSE_REJECTED, 0, 0);
+               release(RELEASE_JOIN, LOCATION_PRIVATE_LOCAL, CAUSE_REJECTED, 0, 0, 0);
                set_tone(portlist, "cause_82");
                denied:
                message_disconnect_port(portlist, CAUSE_REJECTED, LOCATION_PRIVATE_LOCAL, "");
@@ -282,7 +269,7 @@ void EndpointAppPBX::action_dialing_external(void)
                if (e_ext.rights < 3) {
                        trace_header("ACTION extern (national calls denied)", DIRECTION_NONE);
                        end_trace();
-                       release(RELEASE_JOIN, LOCATION_PRIVATE_LOCAL, CAUSE_REJECTED, 0, 0);
+                       release(RELEASE_JOIN, LOCATION_PRIVATE_LOCAL, CAUSE_REJECTED, 0, 0, 0);
                        set_tone(portlist, "cause_83");
                        goto denied;
                }
@@ -294,7 +281,7 @@ void EndpointAppPBX::action_dialing_external(void)
                if (e_ext.rights < 4) {
                        trace_header("ACTION extern (international calls denied)", DIRECTION_NONE);
                        end_trace();
-                       release(RELEASE_JOIN, LOCATION_PRIVATE_LOCAL, CAUSE_REJECTED, 0, 0);
+                       release(RELEASE_JOIN, LOCATION_PRIVATE_LOCAL, CAUSE_REJECTED, 0, 0, 0);
                        set_tone(portlist, "cause_84");
                        goto denied;
                }
@@ -303,6 +290,8 @@ void EndpointAppPBX::action_dialing_external(void)
        /* add or update outgoing call */
        trace_header("ACTION extern (calling)", DIRECTION_NONE);
        add_trace("number", NULL, dialinginfo.id);
+       if (dialinginfo.sending_complete)
+       add_trace("number", "complete", "yes");
        if (dialinginfo.interfaces[0])
                add_trace("interfaces", NULL, dialinginfo.interfaces);
        end_trace();
@@ -311,82 +300,11 @@ void EndpointAppPBX::action_dialing_external(void)
        memcpy(&message->param.setup.redirinfo, &redirinfo, sizeof(struct redir_info));
        memcpy(&message->param.setup.callerinfo, &callerinfo, sizeof(struct caller_info));
        memcpy(&message->param.setup.capainfo, &capainfo, sizeof(struct capa_info));
+       memcpy(&message->param.setup.rtpinfo, &rtpinfo, sizeof(struct rtp_info));
        message_put(message);
 }
 
 
-void EndpointAppPBX::action_dialing_remote(void)
-{
-       struct route_param      *rparam;
-       struct port_list        *portlist = ea_endpoint->ep_portlist;
-       struct lcr_msg          *message;
-       struct capa_info        capainfo;
-       struct caller_info      callerinfo;
-       struct redir_info       redirinfo;
-       struct dialing_info     dialinginfo;
-       char                    context[128] = "";
-       char                    remote[32];
-
-       if (!ea_endpoint->ep_join_id) {
-               /* no join yet, sending setup */
-               if (!(rparam = routeparam(e_action, PARAM_APPLICATION))) {
-                       trace_header("ACTION remote (no application given)", DIRECTION_NONE);
-                       end_trace();
-                       new_state(EPOINT_STATE_OUT_DISCONNECT);
-                       message_disconnect_port(portlist, CAUSE_SERVICEUNAVAIL, LOCATION_PRIVATE_LOCAL, "");
-                       set_tone(portlist, "cause_3f");
-                       return;
-               }
-               SCPY(remote, rparam->string_value);
-               if (!_action_init_call(remote))
-                       return;
-
-               /* create bearer/caller/dialinginfo */
-               memcpy(&capainfo, &e_capainfo, sizeof(capainfo));
-               memcpy(&callerinfo, &e_callerinfo, sizeof(callerinfo));
-               memcpy(&redirinfo, &e_redirinfo, sizeof(redirinfo));
-               memset(&dialinginfo, 0, sizeof(dialinginfo));
-
-               if ((rparam = routeparam(e_action, PARAM_CONTEXT))) {
-                       SCPY(context, rparam->string_value);
-               }
-               if ((rparam = routeparam(e_action, PARAM_EXTEN))) {
-                       SCPY(dialinginfo.id, rparam->string_value);
-                       dialinginfo.ntype = INFO_NTYPE_UNKNOWN;
-               } else {
-                       SCPY(dialinginfo.id, e_extdialing);
-               }
-               e_extdialing = e_dialinginfo.id + strlen(e_dialinginfo.id);
-               /* send setup to remote */
-               trace_header("ACTION remote (setup)", DIRECTION_NONE);
-               add_trace("number", NULL, dialinginfo.id);
-               add_trace("remote", NULL, remote);
-               if (context[0])
-                       add_trace("context", NULL, context);
-               end_trace();
-               message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_join_id, EPOINT_TO_JOIN, MESSAGE_SETUP);
-               memcpy(&message->param.setup.dialinginfo, &dialinginfo, sizeof(struct dialing_info));
-               memcpy(&message->param.setup.redirinfo, &redirinfo, sizeof(struct redir_info));
-               memcpy(&message->param.setup.callerinfo, &callerinfo, sizeof(struct caller_info));
-               memcpy(&message->param.setup.capainfo, &capainfo, sizeof(struct capa_info));
-               SCPY(message->param.setup.context, context);
-               message_put(message);
-       } else {
-               /* send overlap digits */
-               trace_header("ACTION remote (dialing)", DIRECTION_NONE);
-               add_trace("number", NULL, e_extdialing);
-               end_trace();
-               if (e_extdialing[0]) {
-                       message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_join_id, EPOINT_TO_JOIN, MESSAGE_INFORMATION);
-                       memcpy(&message->param.information, &e_dialinginfo, sizeof(struct dialing_info));
-                       SCPY(message->param.information.id, e_extdialing);
-                       e_extdialing = e_dialinginfo.id + strlen(e_dialinginfo.id);
-                       message_put(message);
-               }
-       }
-}
-
-
 /*
  * process dialing the "am" and record
  */
@@ -638,16 +556,16 @@ void EndpointAppPBX::action_dialing_login(void)
                e_ruleset = NULL;
                e_rule = NULL;
                e_action = &action_password;
-               e_match_timeout = 0;
+               unsched_timer(&e_match_timeout);
                e_match_to_action = NULL;
                e_dialinginfo.id[0] = '\0';
                e_extdialing = strchr(e_dialinginfo.id, '\0');
 
                /* set timeout */
-               e_password_timeout = now+20;
+               schedule_timer(&e_password_timeout, 20, 0);
 
                /* do dialing */
-               process_dialing();
+               process_dialing(0);
        } else {
                /* make call state  */
                new_state(EPOINT_STATE_IN_OVERLAP);
@@ -939,7 +857,7 @@ void EndpointAppPBX::_action_redial_reply(int in)
                SCPY(e_dialinginfo.id, last);
                e_extdialing = e_dialinginfo.id;
                e_action = NULL;
-               process_dialing();
+               process_dialing(0);
                return;
        }
        e_extdialing[0] = '\0';
@@ -1033,10 +951,10 @@ void EndpointAppPBX::action_dialing_powerdial(void)
 
        /* do dialing */
        SCPY(e_dialinginfo.id, e_ext.last_out[0]);
-       e_powerdialing = -1; /* indicates the existence of powerdialing but no redial time given */
+       e_powerdial_on = 1; /* indicates the existence of powerdialing but no redial time given */
        e_powercount = 0;
        e_action = NULL;
-       process_dialing();
+       process_dialing(0);
 }
 
 
@@ -1138,7 +1056,7 @@ void EndpointAppPBX::action_hangup_callback(void)
        end_trace();
 
        /* set time to callback */
-       e_callback = now_d + delay;
+       schedule_timer(&e_callback_timeout, delay, 0);
 }
 
 
@@ -1191,7 +1109,7 @@ void EndpointAppPBX::action_dialing_abbrev(void)
        SCPY(e_dialinginfo.id, phone);
        e_extdialing = e_dialinginfo.id;
        e_action = NULL;
-       process_dialing();
+       process_dialing(0);
 }
 
 
@@ -1695,7 +1613,7 @@ void EndpointAppPBX::_action_goto_menu(int mode)
 
        /* do dialing with new ruleset */
        e_action = NULL;
-       process_dialing();
+       process_dialing(0);
 }
 
 /* process dialing goto
@@ -1775,6 +1693,48 @@ void EndpointAppPBX::action_dialing_disconnect(void)
 
 
 /*
+ * process dialing release
+ */
+void EndpointAppPBX::action_dialing_release(void)
+{
+       struct route_param *rparam;
+       int cause = CAUSE_NORMAL; /* normal call clearing */
+       int location = LOCATION_PRIVATE_LOCAL;
+       char cause_string[256] = "", display[84] = "";
+
+       /* check cause parameter */
+       if ((rparam = routeparam(e_action, PARAM_CAUSE))) {
+               cause = rparam->integer_value;
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): 'cause' is given: %d\n", ea_endpoint->ep_serial, cause);
+       }
+       if ((rparam = routeparam(e_action, PARAM_LOCATION))) {
+               location = rparam->integer_value;
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): 'location' is given: %d\n", ea_endpoint->ep_serial, location);
+       }
+
+
+       /* use cause as sample, if not given later */
+       SPRINT(cause_string, "cause_%02x", cause);
+
+       /* check display */
+       if ((rparam = routeparam(e_action, PARAM_DISPLAY))) {
+               SCPY(display, rparam->string_value);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): 'display' is given: %s\n", ea_endpoint->ep_serial, display);
+       }
+
+       /* disconnect only if connect parameter is not given */
+       trace_header("ACTION release", DIRECTION_NONE);
+       add_trace("cause", "value", "%d", cause);
+       add_trace("cause", "location", "%d", location);
+       if (display[0])
+               add_trace("display", NULL, "%s", display);
+       end_trace();
+       e_action = NULL;
+       release(RELEASE_ALL, LOCATION_PRIVATE_LOCAL, CAUSE_NORMAL, location, cause, 1);
+       return;
+}
+
+/*
  * process dialing help
  */
 void EndpointAppPBX::action_dialing_help(void)
@@ -1833,7 +1793,7 @@ void EndpointAppPBX::action_dialing_help(void)
                e_extdialing = e_dialinginfo.id+strlen(numbering->prefix);
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s selected a new menu '%s' dialing: %s\n", ea_endpoint->ep_serial, e_ext.number, numb_actions[numbering->action], e_dialinginfo.id);
 nesting?:
-               process_dialing();
+               process_dialing(0);
                return;
        }
 
@@ -1913,7 +1873,7 @@ void EndpointAppPBX::action_execute(void)
        int iWaitStatus;
        char *command = (char *)"";
        char isdn_port[10];
-       char *argv[11]; /* check also number of args below */
+       char *argv[12]; /* check also number of args below */
        int i = 0;
 
        /* get script / command */
@@ -1924,9 +1884,11 @@ void EndpointAppPBX::action_execute(void)
                end_trace();
                return;
        }
+#if 0
        argv[i++] = (char *)"/bin/sh";
        argv[i++] = (char *)"-c";
        argv[i++] = command;
+#endif
        argv[i++] = command;
        if ((rparam = routeparam(e_action, PARAM_PARAM))) {
                argv[i++] = rparam->string_value;
@@ -1937,6 +1899,7 @@ void EndpointAppPBX::action_execute(void)
        argv[i++] = e_callerinfo.name;
        SPRINT(isdn_port, "%d", e_callerinfo.isdn_port);
        argv[i++] = isdn_port;
+       argv[i++] = e_callerinfo.imsi;
        argv[i++] = NULL; /* check also number of args above */
        switch (pid = fork ()) {
                case -1:
@@ -1946,12 +1909,10 @@ void EndpointAppPBX::action_execute(void)
                case 0:
                        /* To be shure there are no zombies created double fork */
                        if ((pid2 = fork()) == 0) {
-                               execve("/bin/sh", argv, environ);
-                       }
-                       else {
-                               /* Exit immediately and release the waiting parent. The subprocess falls to init because the parent died */
-                               exit(0);
+                               execve(command, argv, environ);
                        }
+                       /* Exit immediately and release the waiting parent. The subprocess falls to init because the parent died */
+                       exit(0);
                        break;
                default:
                        trace_header("ACTION execute", DIRECTION_NONE);
@@ -2088,15 +2049,408 @@ void EndpointAppPBX::action_dialing_password_wr(void)
 }
 
 
+/* process pots-retrieve
+ */
+void EndpointAppPBX::action_init_pots_retrieve(void)
+{
+#ifdef ISDN_P_FXS_POTS
+       struct route_param *rparam;
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+       class Port *port;
+       class Pfxs *ourfxs, *fxs;
+       int count = 0;
+       class Endpoint *epoint;
+
+       /* check given call */
+       if (!(rparam = routeparam(e_action, PARAM_POTS_CALL))) {
+               trace_header("ACTION pots-retrieve (no call given)", DIRECTION_NONE);
+               end_trace();
+
+               disconnect:
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_UNSPECIFIED, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "cause_3f");
+               e_action = NULL;
+               return;
+       }
+
+       /* find call */
+       port = find_port_id(portlist->port_id);
+       if (!port)
+               goto disconnect;
+       if ((port->p_type & PORT_CLASS_POTS_MASK) != PORT_CLASS_POTS_FXS) {
+               trace_header("ACTION pots-retrieve (call not of FXS type)", DIRECTION_NONE);
+               end_trace();
+               goto disconnect;
+       }
+       ourfxs = (class Pfxs *)port;
+
+       port = port_first;
+       while(port) {
+               if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
+                       fxs = (class Pfxs *)port;
+                       if (fxs->p_m_mISDNport == ourfxs->p_m_mISDNport && fxs != ourfxs) {
+                               count++;
+                               if (count == rparam->integer_value)
+                                       break;
+                       }
+               }
+               port = port->next;
+       }
+       if (!port) {
+               trace_header("ACTION pots-retrieve (call # does not exist)", DIRECTION_NONE);
+               end_trace();
+               goto disconnect;
+       }
+
+       /* release our call */
+       ourfxs->hangup_ind(0);
+
+       /* retrieve selected call */
+       fxs->retrieve_ind(0);
+
+       /* split if selected call is member of a 3pty */
+       epoint = find_epoint_id(ACTIVE_EPOINT(fxs->p_epointlist));
+       if (epoint && epoint->ep_app_type == EAPP_TYPE_PBX) {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) try spliting 3pty. this may fail because we don't have a 3pty.\n", epoint->ep_serial);
+               ((class EndpointAppPBX *)epoint->ep_app)->split_3pty();
+       }
+#endif
+}
+
+
+/* process pots-release
+ */
+void EndpointAppPBX::action_init_pots_release(void)
+{
+#ifdef ISDN_P_FXS_POTS
+       struct route_param *rparam;
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+       class Port *port;
+       class Pfxs *ourfxs, *fxs;
+       int count = 0;
+
+       /* check given call */
+       if (!(rparam = routeparam(e_action, PARAM_POTS_CALL))) {
+               trace_header("ACTION pots-release (no call given)", DIRECTION_NONE);
+               end_trace();
+
+               disconnect:
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_UNSPECIFIED, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "cause_3f");
+               e_action = NULL;
+               return;
+       }
+
+       /* find call */
+       port = find_port_id(portlist->port_id);
+       if (!port)
+               goto disconnect;
+       if ((port->p_type & PORT_CLASS_POTS_MASK) != PORT_CLASS_POTS_FXS) {
+               trace_header("ACTION pots-release (call not of FXS type)", DIRECTION_NONE);
+               end_trace();
+               goto disconnect;
+       }
+       ourfxs = (class Pfxs *)port;
+
+       port = port_first;
+       while(port) {
+               if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
+                       fxs = (class Pfxs *)port;
+                       if (fxs->p_m_mISDNport == ourfxs->p_m_mISDNport && fxs != ourfxs) {
+                               count++;
+                               if (count == rparam->integer_value)
+                                       break;
+                       }
+               }
+               port = port->next;
+       }
+       if (!port) {
+               trace_header("ACTION pots-release (call # does not exist)", DIRECTION_NONE);
+               end_trace();
+               goto disconnect;
+       }
+
+#if 0
+       /* disconnect our call */
+       new_state(EPOINT_STATE_OUT_DISCONNECT);
+       message_disconnect_port(portlist, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL, "");
+       set_tone(portlist, "hangup");
+       e_action = NULL;
+#endif
+
+       /* release selected call */
+       fxs->hangup_ind(0);
+
+       /* indicate timeout, so next action will be processed */
+       process_dialing(1);
+#endif
+}
+
+
+/* process pots-reject
+ */
+void EndpointAppPBX::action_init_pots_reject(void)
+{
+#ifdef ISDN_P_FXS_POTS
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+       class Port *port;
+       class Pfxs *ourfxs, *fxs;
+
+       /* find call */
+       port = find_port_id(portlist->port_id);
+       if (!port)
+               goto disconnect;
+       if ((port->p_type & PORT_CLASS_POTS_MASK) != PORT_CLASS_POTS_FXS) {
+               trace_header("ACTION pots-reject (call not of FXS type)", DIRECTION_NONE);
+               end_trace();
+               disconnect:
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_UNSPECIFIED, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "cause_3f");
+               e_action = NULL;
+               return;
+       }
+       ourfxs = (class Pfxs *)port;
+
+       port = port_first;
+       while(port) {
+               if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
+                       fxs = (class Pfxs *)port;
+                       if (fxs->p_m_mISDNport == ourfxs->p_m_mISDNport && fxs != ourfxs) {
+                               if (fxs->p_state == PORT_STATE_OUT_ALERTING)
+                                       break;
+                       }
+               }
+               port = port->next;
+       }
+       if (!port) {
+               trace_header("ACTION pots-reject (no call waiting)", DIRECTION_NONE);
+               end_trace();
+               goto disconnect;
+       }
+
+       /* reject alerting call */
+       fxs->reject_ind(0);
+
+       /* indicate timeout, so next action will be processed */
+       process_dialing(1);
+#endif
+}
+
+
+/* process pots-answer
+ */
+void EndpointAppPBX::action_init_pots_answer(void)
+{
+#ifdef ISDN_P_FXS_POTS
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+       class Port *port;
+       class Pfxs *ourfxs, *fxs;
+
+       /* find call */
+       port = find_port_id(portlist->port_id);
+       if (!port)
+               goto disconnect;
+       if ((port->p_type & PORT_CLASS_POTS_MASK) != PORT_CLASS_POTS_FXS) {
+               trace_header("ACTION pots-answer (call not of FXS type)", DIRECTION_NONE);
+               end_trace();
+               disconnect:
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_UNSPECIFIED, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "cause_3f");
+               e_action = NULL;
+               return;
+       }
+       ourfxs = (class Pfxs *)port;
+
+       port = port_first;
+       while(port) {
+               if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
+                       fxs = (class Pfxs *)port;
+                       if (fxs->p_m_mISDNport == ourfxs->p_m_mISDNport && fxs != ourfxs) {
+                               if (fxs->p_state == PORT_STATE_OUT_ALERTING)
+                                       break;
+                       }
+               }
+               port = port->next;
+       }
+       if (!port) {
+               trace_header("ACTION pots-answer (no call waiting)", DIRECTION_NONE);
+               end_trace();
+               goto disconnect;
+       }
+
+       /* release our call */
+       ourfxs->hangup_ind(0);
+
+       /* answer alerting call */
+       fxs->answer_ind(0);
+#endif
+}
+
+
+/* process pots-3pty
+ */
+void EndpointAppPBX::action_init_pots_3pty(void)
+{
+#ifdef ISDN_P_FXS_POTS
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+       class Port *port;
+       class Pfxs *ourfxs, *fxs, *fxs1 = NULL, *fxs2 = NULL;
+       class Endpoint *epoint;
+       int count = 0;
+
+       /* find call */
+       port = find_port_id(portlist->port_id);
+       if (!port)
+               goto disconnect;
+       if ((port->p_type & PORT_CLASS_POTS_MASK) != PORT_CLASS_POTS_FXS) {
+               trace_header("ACTION pots-3pty (call not of FXS type)", DIRECTION_NONE);
+               end_trace();
+               disconnect:
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_UNSPECIFIED, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "cause_3f");
+               e_action = NULL;
+               return;
+       }
+       ourfxs = (class Pfxs *)port;
+
+       port = port_first;
+       while(port) {
+               if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
+                       fxs = (class Pfxs *)port;
+                       if (fxs->p_m_mISDNport == ourfxs->p_m_mISDNport && fxs != ourfxs) {
+                               if (count == 0)
+                                       fxs1 = fxs;
+                               if (count == 1)
+                                       fxs2 = fxs;
+                               count++;
+                       }
+               }
+               port = port->next;
+       }
+       if (count != 2) {
+               trace_header("ACTION pots-3pty (exactly two calls don't exist)", DIRECTION_NONE);
+               end_trace();
+               goto disconnect;
+       }
+
+       /* release our call */
+       ourfxs->hangup_ind(0);
+
+       /* retrieve latest active call */
+       if (fxs2->p_m_fxs_age > fxs1->p_m_fxs_age) {
+               fxs2->retrieve_ind(0);
+               epoint = find_epoint_id(ACTIVE_EPOINT(fxs2->p_epointlist));
+       } else {
+               fxs1->retrieve_ind(0);
+               epoint = find_epoint_id(ACTIVE_EPOINT(fxs2->p_epointlist));
+       }
+
+       if (!epoint) {
+               trace_header("ACTION pots-3pty (interal error: no endpoint)", DIRECTION_NONE);
+               end_trace();
+               return;
+       }
+
+       if (epoint->ep_app_type != EAPP_TYPE_PBX) {
+               trace_header("ACTION pots-3pty (interal error: endpoint not PBX type)", DIRECTION_NONE);
+               end_trace();
+               return;
+       }
+
+       /* bridge calls */
+       if (((class EndpointAppPBX *)epoint->ep_app)->join_3pty_fxs()) {
+               trace_header("ACTION pots-3pty (interal error: join_3pty_fsx failed)", DIRECTION_NONE);
+               end_trace();
+               return;
+       }
+#endif
+}
+
+/* process pots-transfer
+ */
+void EndpointAppPBX::action_init_pots_transfer(void)
+{
+#ifdef ISDN_P_FXS_POTS
+       struct route_param *rparam;
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+       class Port *port;
+       class Pfxs *ourfxs, *fxs, *fxs1 = NULL, *fxs2 = NULL;
+       int count = 0;
+
+       /* check given call */
+       if (!(rparam = routeparam(e_action, PARAM_POTS_CALL))) {
+               trace_header("ACTION pots-transfer (no call given)", DIRECTION_NONE);
+               end_trace();
+
+               disconnect:
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_UNSPECIFIED, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "cause_3f");
+               e_action = NULL;
+               return;
+       }
+
+       /* find call */
+       port = find_port_id(portlist->port_id);
+       if (!port)
+               goto disconnect;
+       if ((port->p_type & PORT_CLASS_POTS_MASK) != PORT_CLASS_POTS_FXS) {
+               trace_header("ACTION pots-transfer (call not of FXS type)", DIRECTION_NONE);
+               end_trace();
+               goto disconnect;
+       }
+       ourfxs = (class Pfxs *)port;
+
+       port = port_first;
+       while(port) {
+               if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
+                       fxs = (class Pfxs *)port;
+                       if (fxs->p_m_mISDNport == ourfxs->p_m_mISDNport && fxs != ourfxs) {
+                               if (count == 0)
+                                       fxs1 = fxs;
+                               if (count == 1)
+                                       fxs2 = fxs;
+                               count++;
+                       }
+               }
+               port = port->next;
+       }
+       if (count != 2) {
+               trace_header("ACTION pots-transfer (exactly two calls don't exist)", DIRECTION_NONE);
+               end_trace();
+               goto disconnect;
+       }
+
+       /* retrieve call */
+       if (fxs2->p_m_fxs_age > fxs1->p_m_fxs_age)
+               fxs2->retrieve_ind(0);
+       else
+               fxs1->retrieve_ind(0);
+       /* bridge calls */
+       join_join_fxs();
+#endif
+}
+
+
 /* general process dialing of incoming call
  * depending on the detected prefix, subfunctions above (action_*) will be
  * calles.
  */
-void EndpointAppPBX::process_dialing(void)
+void EndpointAppPBX::process_dialing(int timeout)
 {
        struct port_list *portlist = ea_endpoint->ep_portlist;
        struct lcr_msg *message;
        struct route_param *rparam;
+       struct timeval current_time;
+
+       /* set if timeout is active, or if timeout value was given due to timeout action */
+       if (e_action_timeout.active)
+               timeout = 1;
 
 //#warning Due to HANG-BUG somewhere here, I added some HANG-BUG-DEBUGGING output that cannot be disabled. after bug has been found, this will be removed.
 //PDEBUG(~0, "HANG-BUG-DEBUGGING: entered porcess_dialing\n");
@@ -2105,8 +2459,8 @@ void EndpointAppPBX::process_dialing(void)
        if (!portlist) {
                portlist_error:
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d): note: dialing call requires exactly one port object to process dialing. this case could happen due to a parked call. we end dialing here.\n", ea_endpoint->ep_serial, e_ext.number);
-               e_action_timeout = 0;
-               e_match_timeout = 0;
+               unsched_timer(&e_action_timeout);
+               unsched_timer(&e_match_timeout);
                return;
        }
        if (portlist->next) {
@@ -2121,15 +2475,15 @@ void EndpointAppPBX::process_dialing(void)
                new_state(EPOINT_STATE_OUT_DISCONNECT);
                message_disconnect_port(portlist, CAUSE_UNSPECIFIED, LOCATION_PRIVATE_LOCAL, "");
                set_tone(portlist, "cause_3f");
-               e_action_timeout = 0;
-               e_match_timeout = 0;
+               unsched_timer(&e_action_timeout);
+               unsched_timer(&e_match_timeout);
                goto end;
        }
 
 //PDEBUG(~0, "HANG-BUG-DEBUGGING: before action-timeout processing\n");
        /* process timeout */
-       if (e_action && e_action_timeout) { /* e_action may be NULL, but e_action_timeout may still be set and must be ignored */
-               e_action_timeout = 0;
+       if (e_action && timeout) { /* e_action may be NULL, but e_action_timeout may still be set and must be ignored */
+               unsched_timer(&e_action_timeout);
                if (e_state == EPOINT_STATE_CONNECT) {
                        PDEBUG(DEBUG_ROUTE|DEBUG_EPOINT, "EPOINT(%d): action timed out, but we already have connected, so we stop timer and continue.\n", ea_endpoint->ep_serial);
                        goto end;
@@ -2137,10 +2491,10 @@ void EndpointAppPBX::process_dialing(void)
                if (e_action->index == ACTION_DISCONNECT
                 || e_state == EPOINT_STATE_OUT_DISCONNECT) {
                        /* release after disconnect */
-                       release(RELEASE_ALL, LOCATION_PRIVATE_LOCAL, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL, CAUSE_NORMAL);
+                       release(RELEASE_ALL, LOCATION_PRIVATE_LOCAL, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL, CAUSE_NORMAL, 0);
                        goto end;
                }
-               release(RELEASE_JOIN, LOCATION_PRIVATE_LOCAL, CAUSE_NORMAL, 0, 0);
+               release(RELEASE_JOIN, LOCATION_PRIVATE_LOCAL, CAUSE_NORMAL, 0, 0, 0);
                e_action = e_action->next;
                if (!e_action) {
                        /* nothing more, so we release */
@@ -2157,7 +2511,7 @@ void EndpointAppPBX::process_dialing(void)
        if (e_state!=EPOINT_STATE_IN_SETUP
         && e_state!=EPOINT_STATE_IN_OVERLAP) {
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d): we are not in incoming setup/overlap state, so we ignore init/dialing process.\n", ea_endpoint->ep_serial, e_rule_nesting);
-               e_match_timeout = 0;
+               unsched_timer(&e_match_timeout);
                goto end;
        }
 
@@ -2170,8 +2524,8 @@ void EndpointAppPBX::process_dialing(void)
                        e_dialinginfo.id[0] = '\0';
                        e_action = NUMB_ACTION_MENU;
                        e_menu = 0;
-                       process_dialing();
-                       e_match_timeout = 0;
+                       process_dialing(0);
+                       unsched_timer(&e_match_timeout);
                        goto end;
                }
                /* invalid dialing */
@@ -2188,7 +2542,7 @@ void EndpointAppPBX::process_dialing(void)
                }
                new_state(EPOINT_STATE_OUT_DISCONNECT);
                set_tone(portlist,"cause_1c");
-               e_match_timeout = 0;
+               unsched_timer(&e_match_timeout);
                goto end;
        }
 #endif
@@ -2211,12 +2565,6 @@ void EndpointAppPBX::process_dialing(void)
                        e_action = &action_internal;
                        goto process_action;
                }
-               /* check for chan call */
-               if (!strncmp(e_dialinginfo.id, "remote:", 7)) {
-                       e_extdialing = e_dialinginfo.id+7;
-                       e_action = &action_remote;
-                       goto process_action;
-               }
                /* check for vbox call */
                if (!strncmp(e_dialinginfo.id, "vbox:", 5)) {
                        e_extdialing = e_dialinginfo.id+5;
@@ -2224,11 +2572,13 @@ void EndpointAppPBX::process_dialing(void)
                        goto process_action;
                }
 
-               if (e_match_timeout && now_d>=e_match_timeout) {
+               gettimeofday(&current_time, NULL);
+               if (e_match_to_action && TIME_SMALLER(&e_match_timeout.timeout, &current_time)) {
                        /* return timeout rule */
                        PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal '%s' dialing: '%s', timeout in ruleset '%s'\n", ea_endpoint->ep_serial, e_ext.number, e_dialinginfo.id, e_ruleset->name);
-                       e_match_timeout = 0;
+                       unsched_timer(&e_match_timeout);
                        e_action = e_match_to_action;
+                       e_match_to_action = NULL;
                        e_extdialing = e_match_to_extdialing;
                        trace_header("ROUTING (timeout)", DIRECTION_NONE);
                        add_trace("action", NULL, "%s", action_defs[e_action->index].name);
@@ -2266,9 +2616,9 @@ void EndpointAppPBX::process_dialing(void)
                action_timeout:
 
                /* set timeout */
-               e_action_timeout = 0;
+               unsched_timer(&e_action_timeout);
                if (e_action->timeout) {
-                       e_action_timeout = now_d + e_action->timeout;
+                       schedule_timer(&e_action_timeout, e_action->timeout, 0);
                        PDEBUG(DEBUG_ROUTE|DEBUG_EPOINT, "EPOINT(%d): action has a timeout of %d secods.\n", ea_endpoint->ep_serial, e_action->timeout);
                }