Add transfer to IAX2, and transfer application
authorMark Spencer <markster@digium.com>
Wed, 14 May 2003 05:33:06 +0000 (05:33 +0000)
committerMark Spencer <markster@digium.com>
Wed, 14 May 2003 05:33:06 +0000 (05:33 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@1016 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/Makefile
apps/app_enumlookup.c
apps/app_transfer.c [new file with mode: 0755]
channel.c
channels/chan_iax2.c
channels/iax2-parser.c
channels/iax2-parser.h
channels/iax2.h
include/asterisk/channel.h
include/asterisk/channel_pvt.h

index 12cdd90..03f5c55 100755 (executable)
@@ -20,7 +20,7 @@ APPS=app_dial.so app_playback.so app_voicemail.so app_directory.so app_intercom.
      app_setcidname.so app_lookupcidname.so app_substring.so app_macro.so \
      app_authenticate.so app_softhangup.so app_lookupblacklist.so \
      app_waitforring.so app_privacy.so app_db.so app_chanisavail.so \
-     app_enumlookup.so app_voicemail2.so
+     app_enumlookup.so app_voicemail2.so app_transfer.so
 
 #APPS+=app_sql_postgres.so
 #APPS+=app_sql_odbc.so
index 194279a..20a7304 100755 (executable)
 #include <pthread.h>
 
 
-static char *tdesc = "Date and Time";
+static char *tdesc = "ENUM Lookup";
 
 static char *app = "EnumLookup";
 
-static char *synopsis = "Say the date and time";
+static char *synopsis = "Lookup number in ENUM";
 
 static char *descrip = 
 "  EnumLookup(exten):  Looks up an extension via ENUM and sets\n"
diff --git a/apps/app_transfer.c b/apps/app_transfer.c
new file mode 100755 (executable)
index 0000000..3c4013e
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Time of day - Report the time of day
+ * 
+ * Copyright (C) 1999, Mark Spencer
+ *
+ * Mark Spencer <markster@linux-support.net>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+#include <asterisk/lock.h>
+#include <asterisk/file.h>
+#include <asterisk/logger.h>
+#include <asterisk/channel.h>
+#include <asterisk/pbx.h>
+#include <asterisk/module.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <pthread.h>
+
+
+static char *tdesc = "Transfer";
+
+static char *app = "Transfer";
+
+static char *synopsis = "Transfer caller to remote extension";
+
+static char *descrip = 
+"  Transfer(exten):  Requests the remote caller be transferred to\n"
+"a given   Returns -1 on hangup, or 0 on completion\n"
+"regardless of whether the transfer was successful.  If the transfer\n"
+"was *not* supported or successful and there exists a priority n + 101,\n"
+"then that priority will be taken next.\n" ;
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static int transfer_exec(struct ast_channel *chan, void *data)
+{
+       int res=0;
+       struct localuser *u;
+       if (!data || !strlen(data)) {
+               ast_log(LOG_WARNING, "Transfer requires an argument (destination)\n");
+               res = 1;
+       }
+       LOCAL_USER_ADD(u);
+       if (!res) {
+               res = ast_transfer(chan, data);
+       }
+       if (!res) {
+               /* Look for a "busy" place */
+               if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
+                       chan->priority += 100;
+       }
+       if (res > 0)
+               res = 0;
+       LOCAL_USER_REMOVE(u);
+       return res;
+}
+
+int unload_module(void)
+{
+       STANDARD_HANGUP_LOCALUSERS;
+       return ast_unregister_application(app);
+}
+
+int load_module(void)
+{
+       return ast_register_application(app, transfer_exec, synopsis, descrip);
+}
+
+char *description(void)
+{
+       return tdesc;
+}
+
+int usecount(void)
+{
+       int res;
+       STANDARD_USECOUNT(res);
+       return res;
+}
+
+char *key()
+{
+       return ASTERISK_GPL_KEY;
+}
index 0a9363a..ecd7c5f 100755 (executable)
--- a/channel.c
+++ b/channel.c
@@ -1568,6 +1568,26 @@ int ast_call(struct ast_channel *chan, char *addr, int timeout)
        return res;
 }
 
+int ast_transfer(struct ast_channel *chan, char *dest) 
+{
+       /* Place an outgoing call, but don't wait any longer than timeout ms before returning. 
+          If the remote end does not answer within the timeout, then do NOT hang up, but 
+          return anyway.  */
+       int res = -1;
+       /* Stop if we're a zombie or need a soft hangup */
+       ast_pthread_mutex_lock(&chan->lock);
+       if (!chan->zombie && !ast_check_hangup(chan)) {
+               if (chan->pvt->transfer) {
+                       res = chan->pvt->transfer(chan, dest);
+                       if (!res)
+                               res = 1;
+               } else
+                       res = 0;
+       }
+       pthread_mutex_unlock(&chan->lock);
+       return res;
+}
+
 int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int ftimeout, char *enders)
 {
        int pos=0;
index 6bb2c48..2316cd6 100755 (executable)
@@ -994,7 +994,6 @@ static void iax2_destroy(int callno)
 retry:
        ast_pthread_mutex_lock(&iaxsl[callno]);
        pvt = iaxs[callno];
-       iaxs[callno] = NULL;
        gettimeofday(&lastused[callno], NULL);
 
        if (pvt)
@@ -1009,6 +1008,7 @@ retry:
                        goto retry;
                }
        }
+       iaxs[callno] = NULL;
        if (pvt) {
                pvt->owner = NULL;
                /* No more pings or lagrq's */
@@ -1918,6 +1918,26 @@ static int iax2_indicate(struct ast_channel *c, int condition)
        return send_command(pvt, AST_FRAME_CONTROL, condition, 0, NULL, 0, -1);
 }
        
+static int iax2_transfer(struct ast_channel *c, char *dest)
+{
+       struct chan_iax2_pvt *pvt = c->pvt->pvt;
+       struct iax_ie_data ied;
+       char tmp[256] = "", *context;
+       strncpy(tmp, dest, sizeof(tmp) - 1);
+       context = strchr(tmp, '@');
+       if (context) {
+               *context = '\0';
+               context++;
+       }
+       memset(&ied, 0, sizeof(ied));
+       iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, tmp);
+       if (context)
+               iax_ie_append_str(&ied, IAX_IE_CALLED_CONTEXT, context);
+       if (option_debug)
+               ast_log(LOG_DEBUG, "Transferring '%s' to '%s'\n", c->name, dest);
+       return send_command(pvt, AST_FRAME_IAX, IAX_COMMAND_TRANSFER, 0, ied.buf, ied.pos, -1);
+}
+       
 
 static int iax2_write(struct ast_channel *c, struct ast_frame *f);
 
@@ -1966,6 +1986,7 @@ static struct ast_channel *ast_iax2_new(struct chan_iax2_pvt *i, int state, int
                tmp->pvt->indicate = iax2_indicate;
                tmp->pvt->setoption = iax2_setoption;
                tmp->pvt->bridge = iax2_bridge;
+               tmp->pvt->transfer = iax2_transfer;
                if (strlen(i->callerid))
                        tmp->callerid = strdup(i->callerid);
                if (strlen(i->ani))
@@ -3973,6 +3994,17 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                                send_command_immediate(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.iseqno);
                                iax2_destroy_nolock(fr.callno);
                                break;
+                       case IAX_COMMAND_TRANSFER:
+                               if (iaxs[fr.callno]->owner && iaxs[fr.callno]->owner->bridge && ies.called_number) {
+                                       if (ast_async_goto(iaxs[fr.callno]->owner->bridge, iaxs[fr.callno]->context, ies.called_number, 1, 1))
+                                               ast_log(LOG_WARNING, "Async goto of '%s' to '%s@%s' failed\n", iaxs[fr.callno]->owner->bridge->name, 
+                                                       ies.called_number, iaxs[fr.callno]->context);
+                                       else
+                                               ast_log(LOG_DEBUG, "Async goto of '%s' to '%s@%s' started\n", iaxs[fr.callno]->owner->bridge->name, 
+                                                       ies.called_number, iaxs[fr.callno]->context);
+                               } else
+                                               ast_log(LOG_DEBUG, "Async goto not applicable on call %d\n", fr.callno);
+                               break;
                        case IAX_COMMAND_ACCEPT:
                                /* Ignore if call is already up or needs authentication or is a TBD */
                                if (iaxs[fr.callno]->state & (IAX_STATE_STARTED | IAX_STATE_TBD | IAX_STATE_AUTHENTICATED))
index 73fca93..2c4ad9c 100755 (executable)
@@ -116,6 +116,7 @@ static struct iax2_ie {
        { IAX_IE_MSGCOUNT, "MESSAGE COUNT", dump_short },
        { IAX_IE_AUTOANSWER, "AUTO ANSWER REQ" },
        { IAX_IE_TRANSFERID, "TRANSFER ID", dump_int },
+       { IAX_IE_RDNIS, "REFERRING DNIS", dump_string },
 };
 
 const char *iax_ie2str(int ie)
@@ -217,6 +218,7 @@ void iax_showframe(struct iax_frame *f, struct ast_iax2_full_hdr *fhi, int rx, s
                "PAGE",
                "MWI",
                "UNSUPPORTED",
+               "TRANSFER",
        };
        char *cmds[] = {
                "(0?)",
@@ -420,6 +422,9 @@ int iax_parse_ies(struct iax_ies *ies, unsigned char *data, int datalen)
                case IAX_IE_DNID:
                        ies->dnid = data + 2;
                        break;
+               case IAX_IE_RDNIS:
+                       ies->rdnis = data + 2;
+                       break;
                case IAX_IE_AUTHMETHODS:
                        if (len != sizeof(unsigned short))  {
                                snprintf(tmp, sizeof(tmp), "Expecting authmethods to be %d bytes long but was %d\n", sizeof(unsigned short), len);
index bdca496..9f20a18 100755 (executable)
@@ -28,6 +28,7 @@ struct iax_ies {
        int version;
        unsigned short adsicpe;
        char *dnid;
+       char *rdnis;
        unsigned int authmethods;
        char *challenge;
        char *md5_result;
index 570c1e8..d694599 100755 (executable)
@@ -63,6 +63,7 @@
 #define IAX_COMMAND_PAGE       31      /* Paging description */
 #define IAX_COMMAND_MWI        32      /* Stand-alone message waiting indicator */
 #define IAX_COMMAND_UNSUPPORT  33      /* Unsupported message received */
+#define IAX_COMMAND_TRANSFER   34      /* Request remote transfer */
 
 #define IAX_DEFAULT_REG_EXPIRE  60     /* By default require re-registration once per minute */
 
@@ -98,6 +99,7 @@
 #define IAX_IE_AUTOANSWER                      25              /* Request auto-answering -- none */
 #define IAX_IE_MUSICONHOLD                     26              /* Request musiconhold with QUELCH -- none or string */
 #define IAX_IE_TRANSFERID                      27              /* Transfer Request Identifier -- int */
+#define IAX_IE_RDNIS                           28              /* Referring DNIS -- string */
 
 #define IAX_AUTH_PLAINTEXT                     (1 << 0)
 #define IAX_AUTH_MD5                           (1 << 1)
index 97f8af9..e40a537 100755 (executable)
@@ -676,6 +676,10 @@ int ast_autoservice_stop(struct ast_channel *chan);
    timer fd */
 int ast_settimeout(struct ast_channel *c, int ms);
 
+/* Transfer a channel (if supported).  Returns -1 on error, 0 if not supported
+   and 1 if supported and requested */
+int ast_transfer(struct ast_channel *chan, char *dest);
+
 /* Misc. functions below */
 
 //! Waits for activity on a group of channels
index a815f5c..d52a90f 100755 (executable)
@@ -65,6 +65,8 @@ struct ast_channel_pvt {
        int (*setoption)(struct ast_channel *chan, int option, void *data, int datalen);
        /*! Query a given option */
        int (*queryoption)(struct ast_channel *chan, int option, void *data, int *datalen);
+       /*! Blind transfer other side */
+       int (*transfer)(struct ast_channel *chan, char *newdest);
 };
 
 //! Create a channel structure