Add call forward no answer to skinny and cleanup general callfwd handling.
authorDamien Wedhorn <voip@facts.com.au>
Sat, 18 May 2013 23:20:53 +0000 (23:20 +0000)
committerDamien Wedhorn <voip@facts.com.au>
Sat, 18 May 2013 23:20:53 +0000 (23:20 +0000)
CallforwardNoAnswer uses a sched to determine when to forward the call.
Defaults to 20secs but configurable in skinny.conf.

Adds dialType to each subchannel structure to be used to differentiate
between normal dials that result in a call being placed (default) and
other uses for the skinny_dialer (such as cfwd digit collection).
Restructured all cfwd handling to use this new arrangement.

(closes issue ASTERISK-21292)
Reported by: wedhorn
Tested by: myself
Patches:
    skinny-callfwdnoans03.diff uploaded by wedhorn (license 5019)

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@389097 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_skinny.c
configs/skinny.conf.sample

index bf2871e..f5b7dff 100644 (file)
@@ -919,6 +919,7 @@ static const uint8_t soft_key_default_onhook[] = {
        SOFTKEY_NEWCALL,
        SOFTKEY_CFWDALL,
        SOFTKEY_CFWDBUSY,
+       SOFTKEY_CFWDNOANSWER,
        SOFTKEY_DND,
        SOFTKEY_GPICKUP,
        /*SOFTKEY_CONFRN,*/
@@ -931,6 +932,7 @@ static const uint8_t soft_key_default_connected[] = {
        SOFTKEY_PARK,
        SOFTKEY_CFWDALL,
        SOFTKEY_CFWDBUSY,
+       SOFTKEY_CFWDNOANSWER,
 };
 
 static const uint8_t soft_key_default_onhold[] = {
@@ -951,6 +953,7 @@ static const uint8_t soft_key_default_offhook[] = {
        SOFTKEY_ENDCALL,
        SOFTKEY_CFWDALL,
        SOFTKEY_CFWDBUSY,
+       SOFTKEY_CFWDNOANSWER,
        SOFTKEY_GPICKUP,
 };
 
@@ -961,6 +964,7 @@ static const uint8_t soft_key_default_connwithtrans[] = {
        SOFTKEY_PARK,
        SOFTKEY_CFWDALL,
        SOFTKEY_CFWDBUSY,
+       SOFTKEY_CFWDNOANSWER,
 };
 
 static const uint8_t soft_key_default_dadfd[] = {
@@ -1361,6 +1365,9 @@ static int matchdigittimeout = 3000;
 #define SUBSTATE_PROGRESS 12
 #define SUBSTATE_DIALING 101
 
+#define DIALTYPE_NORMAL      1<<0
+#define DIALTYPE_CFWD        1<<1
+
 struct skinny_subchannel {
        ast_mutex_t lock;
        struct ast_channel *owner;
@@ -1382,6 +1389,9 @@ struct skinny_subchannel {
        int aa_beep;
        int aa_mute;
        int dialer_sched;
+       int cfwd_sched;
+       int dialType;
+       int getforward;
        char *origtonum;
        char *origtoname;
 
@@ -1424,7 +1434,7 @@ struct skinny_subchannel {
        int threewaycalling;                            \
        int mwiblink;                                   \
        int cancallforward;                             \
-       int getforward;                                 \
+       int callfwdtimeout;                             \
        int dnd;                                        \
        int hidecallerid;                               \
        int amaflags;                                   \
@@ -1467,7 +1477,7 @@ static struct skinny_line_options{
        .instance = 0,
        .directmedia = 0,
        .nat = 0,
-       .getforward = 0,
+       .callfwdtimeout = 20000,
        .prune = 0,
 };
 static struct skinny_line_options *default_line = &default_line_struct;
@@ -2257,7 +2267,7 @@ static int skinny_register(struct skinny_req *req, struct skinnysession *s)
                                l->prefs = d->prefs; */
                                l->instance = instance;
                                l->newmsgs = ast_app_has_voicemail(l->mailbox, NULL);
-                               set_callforwards(l, NULL, 0);
+                               set_callforwards(l, NULL, SKINNY_CFWD_ALL|SKINNY_CFWD_BUSY|SKINNY_CFWD_NOANSWER);
                                manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: Skinny\r\nPeer: Skinny/%s@%s\r\nPeerStatus: Registered\r\n", l->name, d->name);
                                register_exten(l);
                                /* initialize MWI on line and device */
@@ -4464,6 +4474,7 @@ static char *_skinny_show_line(int type, int fd, struct mansession *s, const str
                                ast_cli(fd, "CFwdAll:          %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_ALL), l->call_forward_all, "<not set>"));
                                ast_cli(fd, "CFwdBusy:         %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_BUSY), l->call_forward_busy, "<not set>"));
                                ast_cli(fd, "CFwdNoAnswer:     %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_NOANSWER), l->call_forward_noanswer, "<not set>"));
+                               ast_cli(fd, "CFwdTimeout:      %dms\n", l->callfwdtimeout);
                                ast_cli(fd, "VoicemailBox:     %s\n", S_OR(l->mailbox, "<not set>"));
                                ast_cli(fd, "VoicemailNumber:  %s\n", S_OR(l->vmexten, "<not set>"));
                                ast_cli(fd, "MWIblink:         %d\n", l->mwiblink);
@@ -4882,6 +4893,17 @@ static int skinny_autoanswer_cb(const void *data)
        return 0;
 }
 
+static int skinny_cfwd_cb(const void *data)
+{
+       struct skinny_subchannel *sub = (struct skinny_subchannel *)data;
+       struct skinny_line *l = sub->line;
+       sub->cfwd_sched = 0;
+       SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - CFWDNOANS to %s.\n", sub->callid, l->call_forward_noanswer);
+       ast_channel_call_forward_set(sub->owner, l->call_forward_noanswer);
+       ast_queue_control(sub->owner, AST_CONTROL_REDIRECTING);
+       return 0;
+}
+
 static int skinny_call(struct ast_channel *ast, const char *dest, int timeout)
 {
        int res = 0;
@@ -5380,6 +5402,9 @@ static struct ast_channel *skinny_new(struct skinny_line *l, struct skinny_subli
                        sub->calldirection = direction;
                        sub->aa_sched = 0;
                        sub->dialer_sched = 0;
+                       sub->cfwd_sched = 0;
+                       sub->dialType = DIALTYPE_NORMAL;
+                       sub->getforward = 0;
 
                        if (subline) {
                                sub->subline = subline;
@@ -5429,13 +5454,15 @@ static struct ast_channel *skinny_new(struct skinny_line *l, struct skinny_subli
                ast_channel_named_callgroups_set(tmp, l->named_callgroups);
                ast_channel_named_pickupgroups_set(tmp, l->named_pickupgroups);
 
-               /* XXX Need to figure out how to handle CFwdNoAnswer */
                if (l->cfwdtype & SKINNY_CFWD_ALL) {
+                       SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - CFWDALL to %s.\n", sub->callid, l->call_forward_all);
                        ast_channel_call_forward_set(tmp, l->call_forward_all);
-               } else if (l->cfwdtype & SKINNY_CFWD_BUSY) {
-                       if (get_devicestate(l) != AST_DEVICE_NOT_INUSE) {
-                               ast_channel_call_forward_set(tmp, l->call_forward_busy);
-                       }
+               } else if ((l->cfwdtype & SKINNY_CFWD_BUSY) && (get_devicestate(l) != AST_DEVICE_NOT_INUSE)) {
+                       SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - CFWDBUSY to %s.\n", sub->callid, l->call_forward_busy);
+                       ast_channel_call_forward_set(tmp, l->call_forward_busy);
+               } else if (l->cfwdtype & SKINNY_CFWD_NOANSWER) {
+                       SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - CFWDNOANS Scheduling for %d seconds.\n", sub->callid, l->callfwdtimeout/1000);
+                       sub->cfwd_sched = skinny_sched_add(l->callfwdtimeout, skinny_cfwd_cb, sub);
                }
 
                if (subline) {
@@ -5535,6 +5562,19 @@ static void setsubstate(struct skinny_subchannel *sub, int state)
                sub->aa_mute = 0;
        }
 
+       if (sub->cfwd_sched) {
+               if (state == SUBSTATE_CONNECTED) {
+                       if (skinny_sched_del(sub->cfwd_sched, sub)) {
+                               SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - trying to change state from %s to %s, but already forwarded because no answer.\n",
+                                       sub->callid, substate2str(sub->substate), substate2str(actualstate));
+                               return;
+                       }
+                       sub->cfwd_sched = 0;
+               } else if (state == SUBSTATE_ONHOOK) {
+                       skinny_sched_del(sub->cfwd_sched, sub);
+               }
+       }
+
        if ((state == SUBSTATE_RINGIN) && ((d->hookstate == SKINNY_OFFHOOK) || (AST_LIST_NEXT(AST_LIST_FIRST(&l->sub), list)))) {
                actualstate = SUBSTATE_CALLWAIT;
        }
@@ -5665,8 +5705,6 @@ static void setsubstate(struct skinny_subchannel *sub, int state)
                sub->callid, substate2str(sub->substate), substate2str(actualstate));
 
        if (actualstate == sub->substate) {
-               send_callinfo(sub);
-               transmit_callstate(d, l->instance, sub->callid, SKINNY_HOLD);
                return;
        }
 
@@ -6001,26 +6039,19 @@ static void activatesub(struct skinny_subchannel *sub, int state)
 
 static void dialandactivatesub(struct skinny_subchannel *sub, char exten[AST_MAX_EXTENSION])
 {
-       if (sub->line->getforward) {
-               struct skinny_line *l = sub->line;
-               struct skinny_device *d = l->device;
-
-               // FIXME: needs some love and remove sleeps
-               SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - Set callforward to %s\n", sub->callid, exten);
-               set_callforwards(l, sub->exten, l->getforward);
-               transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
-               transmit_lamp_indication(d, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_ON);
-               transmit_displaynotify(d, "CFwd enabled", 10);
-               transmit_cfwdstate(d, l);
-               ast_safe_sleep(sub->owner, 500);
-               ast_indicate(sub->owner, -1);
-               ast_safe_sleep(sub->owner, 1000);
-               l->getforward = 0;
-               dumpsub(sub, 0);
-       } else {
+       struct skinny_line *l = sub->line;
+       struct skinny_device *d = l->device;
+       
+       if (sub->dialType == DIALTYPE_NORMAL) {
                SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - Dial %s and Activate\n", sub->callid, exten);
                ast_copy_string(sub->exten, exten, sizeof(sub->exten));
                activatesub(sub, SUBSTATE_DIALING);
+       } else if (sub->dialType == DIALTYPE_CFWD) {
+               SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - Set callforward(%d) to %s\n", sub->callid, sub->getforward, exten);
+               set_callforwards(l, sub->exten, sub->getforward);
+               dumpsub(sub, 1);
+               transmit_cfwdstate(d, l);
+               transmit_displaynotify(d, "CFwd enabled", 10);
        }
 }
 
@@ -6099,48 +6130,38 @@ static int handle_transfer_button(struct skinny_subchannel *sub)
        return 0;
 }
 
-static int handle_callforward_button(struct skinny_subchannel *sub, int cfwdtype)
+static void handle_callforward_button(struct skinny_line *l, struct skinny_subchannel *sub, int cfwdtype)
 {
-       struct skinny_line *l = sub->line;
        struct skinny_device *d = l->device;
-       struct ast_channel *c = sub->owner;
+       struct ast_channel *c;
 
        if (!d->session) {
                ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
-               return 0;
-       }
-
-       if (d->hookstate == SKINNY_ONHOOK) {
-               d->hookstate = SKINNY_OFFHOOK;
-               transmit_speaker_mode(d, SKINNY_SPEAKERON);
-               transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
-               transmit_activatecallplane(d, l);
+               return;
        }
-       transmit_clear_display_message(d, l->instance, sub->callid);
 
-       if (l->cfwdtype & cfwdtype) {
+       if (!sub && (l->cfwdtype & cfwdtype)) {
                set_callforwards(l, NULL, cfwdtype);
-               ast_safe_sleep(c, 500);
-               transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
-               transmit_closereceivechannel(d, sub);
-               transmit_stopmediatransmission(d, sub);
-               transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
-               transmit_clearpromptmessage(d, l->instance, sub->callid);
-               transmit_callstate(d, l->instance, sub->callid, SKINNY_ONHOOK);
-               transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK, KEYMASK_ALL);
-               transmit_activatecallplane(d, l);
-               transmit_displaynotify(d, "CFwd disabled", 10);
-               if (sub->owner && ast_channel_state(sub->owner) != AST_STATE_UP) {
-                       ast_indicate(c, -1);
-                       ast_hangup(c);
+               if (sub) {
+                       dumpsub(sub, 1);
                }
                transmit_cfwdstate(d, l);
+               transmit_displaynotify(d, "CFwd disabled", 10);
        } else {
-               l->getforward = cfwdtype;
-               setsubstate(sub, SUBSTATE_OFFHOOK);
+               if (!sub || !sub->owner) {
+                       if (!(c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING))) {
+                               ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+                               return;
+                       }
+                       sub = ast_channel_tech_pvt(c);
+                       l->activesub = sub;
+                       setsubstate(sub, SUBSTATE_OFFHOOK);
+               }
+               sub->getforward |= cfwdtype;
+               sub->dialType = DIALTYPE_CFWD;
        }
-       return 0;
 }
+
 static int handle_ip_port_message(struct skinny_req *req, struct skinnysession *s)
 {
        /* no response necessary */
@@ -6400,55 +6421,17 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
        case STIMULUS_FORWARDALL:
                SKINNY_DEBUG(DEBUG_PACKET, 3, "Received STIMULUS_FORWARDALL from %s, inst %d, callref %d\n",
                        d->name, instance, callreference);
-
-               if (!sub || !sub->owner) {
-                       c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
-               } else {
-                       c = sub->owner;
-               }
-
-               if (!c) {
-                       ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
-               } else {
-                       sub = ast_channel_tech_pvt(c);
-                       handle_callforward_button(sub, SKINNY_CFWD_ALL);
-               }
+               handle_callforward_button(l, sub, SKINNY_CFWD_ALL);
                break;
        case STIMULUS_FORWARDBUSY:
                SKINNY_DEBUG(DEBUG_PACKET, 3, "Received STIMULUS_FORWARDBUSY from %s, inst %d, callref %d\n",
                        d->name, instance, callreference);
-
-               if (!sub || !sub->owner) {
-                       c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
-               } else {
-                       c = sub->owner;
-               }
-
-               if (!c) {
-                       ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
-               } else {
-                       sub = ast_channel_tech_pvt(c);
-                       handle_callforward_button(sub, SKINNY_CFWD_BUSY);
-               }
+               handle_callforward_button(l, sub, SKINNY_CFWD_BUSY);
                break;
        case STIMULUS_FORWARDNOANSWER:
                SKINNY_DEBUG(DEBUG_PACKET, 3, "Received STIMULUS_FORWARDNOANSWER from %s, inst %d, callref %d\n",
                        d->name, instance, callreference);
-
-#if 0 /* Not sure how to handle this yet */
-               if (!sub || !sub->owner) {
-                       c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
-               } else {
-                       c = sub->owner;
-               }
-
-               if (!c) {
-                       ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
-               } else {
-                       sub = c->tech_pvt;
-                       handle_callforward_button(sub, SKINNY_CFWD_NOANSWER);
-               }
-#endif
+               handle_callforward_button(l, sub, SKINNY_CFWD_NOANSWER);
                break;
        case STIMULUS_DISPLAY:
                /* Not sure what this is */
@@ -7037,58 +7020,17 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
        case SOFTKEY_CFWDALL:
                SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_CFWDALL from %s, inst %d, callref %d\n",
                        d->name, instance, callreference);
-
-               if (!sub || !sub->owner) {
-                       c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
-               } else {
-                       c = sub->owner;
-               }
-
-               if (!c) {
-                       ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
-               } else {
-                       sub = ast_channel_tech_pvt(c);
-                       l->activesub = sub;
-                       handle_callforward_button(sub, SKINNY_CFWD_ALL);
-               }
+               handle_callforward_button(l, sub, SKINNY_CFWD_ALL);
                break;
        case SOFTKEY_CFWDBUSY:
                SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_CFWDBUSY from %s, inst %d, callref %d\n",
                        d->name, instance, callreference);
-
-               if (!sub || !sub->owner) {
-                       c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
-               } else {
-                       c = sub->owner;
-               }
-
-               if (!c) {
-                       ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
-               } else {
-                       sub = ast_channel_tech_pvt(c);
-                       l->activesub = sub;
-                       handle_callforward_button(sub, SKINNY_CFWD_BUSY);
-               }
+               handle_callforward_button(l, sub, SKINNY_CFWD_BUSY);
                break;
        case SOFTKEY_CFWDNOANSWER:
                SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_CFWDNOANSWER from %s, inst %d, callref %d\n",
                        d->name, instance, callreference);
-
-#if 0 /* Not sure how to handle this yet */
-               if (!sub || !sub->owner) {
-                       c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
-               } else {
-                       c = sub->owner;
-               }
-
-               if (!c) {
-                       ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
-               } else {
-                       sub = c->tech_pvt;
-                       l->activesub = sub;
-                       handle_callforward_button(sub, SKINNY_CFWD_NOANSWER);
-               }
-#endif
+               handle_callforward_button(l, sub, SKINNY_CFWD_NOANSWER);
                break;
        case SOFTKEY_BKSPC:
                SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_BKSPC from %s, inst %d, callref %d\n",
@@ -7950,6 +7892,11 @@ static void config_parse_variables(int type, void *item, struct ast_variable *vp
                                CLINE_OPTS->cancallforward = ast_true(v->value);
                                continue;
                        }
+               } else if (!strcasecmp(v->name, "callfwdtimeout")) {
+                       if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
+                               CLINE_OPTS->callfwdtimeout = atoi(v->value);
+                               continue;
+                       }
                } else if (!strcasecmp(v->name, "mailbox")) {
                        if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
                                ast_copy_string(CLINE_OPTS->mailbox, v->value, sizeof(CLINE_OPTS->mailbox));
index 9509099..0a618ac 100644 (file)
@@ -100,6 +100,7 @@ keepalive=120
 ;                            ; (earlyrtp=1) or device generated (earlyrtp=0). default=yes
 ;transfer=1                  ; whether the device is allowed to transfer. default=yes
 ;context=default             ; context to use for this line.
+;callfwdtimeout=20000        ; ms before cfwd_noans occurs (default 20 secs)
 ;------------------------------- SPECIFIC LINE OPTIONS -----------------------------
 ;setvar=                    ; allows for the setting of chanvars.
 ;-----------------------------------------------------------------------------------