From 67b5919a15a97bce261c4dc98a14c5d8aeab0154 Mon Sep 17 00:00:00 2001 From: markster Date: Wed, 14 May 2003 05:33:06 +0000 Subject: [PATCH] Add transfer to IAX2, and transfer application git-svn-id: http://svn.digium.com/svn/asterisk/trunk@1016 f38db490-d61c-443f-a65b-d21fe96a405b --- apps/Makefile | 2 +- apps/app_enumlookup.c | 4 +- apps/app_transfer.c | 94 ++++++++++++++++++++++++++++++++++ channel.c | 20 ++++++++ channels/chan_iax2.c | 34 +++++++++++- channels/iax2-parser.c | 5 ++ channels/iax2-parser.h | 1 + channels/iax2.h | 2 + include/asterisk/channel.h | 4 ++ include/asterisk/channel_pvt.h | 2 + 10 files changed, 164 insertions(+), 4 deletions(-) create mode 100755 apps/app_transfer.c diff --git a/apps/Makefile b/apps/Makefile index 12cdd9072..03f5c558c 100755 --- a/apps/Makefile +++ b/apps/Makefile @@ -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 diff --git a/apps/app_enumlookup.c b/apps/app_enumlookup.c index 194279ae3..20a7304be 100755 --- a/apps/app_enumlookup.c +++ b/apps/app_enumlookup.c @@ -26,11 +26,11 @@ #include -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 index 000000000..3c4013e76 --- /dev/null +++ b/apps/app_transfer.c @@ -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 + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +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; +} diff --git a/channel.c b/channel.c index 0a9363a8f..ecd7c5fa4 100755 --- 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; diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index 6bb2c4817..2316cd686 100755 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -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)) diff --git a/channels/iax2-parser.c b/channels/iax2-parser.c index 73fca93e5..2c4ad9c8b 100755 --- a/channels/iax2-parser.c +++ b/channels/iax2-parser.c @@ -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); diff --git a/channels/iax2-parser.h b/channels/iax2-parser.h index bdca49686..9f20a1817 100755 --- a/channels/iax2-parser.h +++ b/channels/iax2-parser.h @@ -28,6 +28,7 @@ struct iax_ies { int version; unsigned short adsicpe; char *dnid; + char *rdnis; unsigned int authmethods; char *challenge; char *md5_result; diff --git a/channels/iax2.h b/channels/iax2.h index 570c1e87a..d69459999 100755 --- a/channels/iax2.h +++ b/channels/iax2.h @@ -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) diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 97f8af91f..e40a5378f 100755 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -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 diff --git a/include/asterisk/channel_pvt.h b/include/asterisk/channel_pvt.h index a815f5c4a..d52a90fc6 100755 --- a/include/asterisk/channel_pvt.h +++ b/include/asterisk/channel_pvt.h @@ -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