Index: .version =================================================================== --- a/.version (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/.version (.../trunk) (revision 202568) @@ -1 +1 @@ -1.6.2.0-beta3 +1.6.3.0-r202568 Index: build_tools/strip_nonapi =================================================================== --- a/build_tools/strip_nonapi (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/build_tools/strip_nonapi (.../trunk) (revision 202568) @@ -1,38 +0,0 @@ -#!/bin/sh -e - -# This script is designed to remove all non-API global symbols from an object -# file. The only global symbols that should be retained are those that belong -# to the official namespace. Unfortunately doing this is platform-specific, as -# the object file manipulation tools are not consistent across platforms. -# -# On platforms where this script does not know what to do, the object file -# will retain non-API global symbols, and this may have unpleasant side effects. -# -# Prefixes that belong to the official namespace are: -# ast_ -# _ast_ -# __ast_ -# astman_ -# pbx_ -# resample_ - -FILTER="${GREP} -v -e ^ast_ -e ^_ast_ -e ^__ast_ -e ^astman_ -e ^pbx_ -e ^resample_" - -case "${PROC}" in - powerpc64) - TEXTSYM=" D " - ;; - *) - TEXTSYM=" T " - ;; -esac - -case "${OSARCH}" in - linux-gnu|FreeBSD) - nm ${1} | ${GREP} -e "$TEXTSYM" | cut -d" " -f3 | ${FILTER} > striplist - sed -e "s/^/-N /" striplist | xargs -n 40 ${STRIP} ${1} - rm -f striplist - ;; - *) - ;; -esac Index: build_tools/cflags.xml =================================================================== --- a/build_tools/cflags.xml (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/build_tools/cflags.xml (.../trunk) (revision 202568) @@ -32,8 +32,6 @@ G711_NEW_ALGORITHM - - Index: build_tools/menuselect-deps.in =================================================================== --- a/build_tools/menuselect-deps.in (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/build_tools/menuselect-deps.in (.../trunk) (revision 202568) @@ -11,6 +11,7 @@ GTK=@PBX_GTK@ H323=@PBX_H323@ HOARD=@PBX_HOARD@ +ICAL=@PBX_ICAL@ ICONV=@PBX_ICONV@ IKSEMEL=@PBX_IKSEMEL@ IMAP_TK=@PBX_IMAP_TK@ @@ -19,12 +20,14 @@ IXJUSER=@PBX_IXJUSER@ JACK=@PBX_JACK@ LDAP=@PBX_LDAP@ +LIBXML2=@PBX_LIBXML2@ LTDL=@PBX_LTDL@ LUA=@PBX_LUA@ MISDN=@PBX_MISDN@ NBS=@PBX_NBS@ NETSNMP=@PBX_NETSNMP@ NEWT=@PBX_NEWT@ +NEON=@PBX_NEON@ OGG=@PBX_OGG@ OPENH323=@PBX_OPENH323@ OSPTK=@PBX_OSPTK@ @@ -33,6 +36,7 @@ POPT=@PBX_POPT@ PORTAUDIO=@PBX_PORTAUDIO@ PRI=@PBX_PRI@ +OPENR2=@PBX_OPENR2@ RESAMPLE=@PBX_RESAMPLE@ AIS=@PBX_AIS@ RADIUS=@PBX_RADIUS@ Index: configure =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: default.exports =================================================================== --- a/default.exports (.../tags/1.6.2.0-beta3) (revision 0) +++ b/default.exports (.../trunk) (revision 202568) @@ -0,0 +1,4 @@ +{ + local: + *; +}; Property changes on: default.exports ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + Author Date Id Revision Index: pbx/pbx_config.c =================================================================== --- a/pbx/pbx_config.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/pbx/pbx_config.c (.../trunk) (revision 202568) @@ -328,7 +328,7 @@ * Priority input checking ... */ if (a->argc == 5) { - char *c = a->argv[4]; + const char *c = a->argv[4]; /* check for digits in whole parameter for right priority ... * why? because atoi (strtol) returns 0 if any characters in @@ -451,8 +451,7 @@ ast_unlock_contexts(); error2: - if (exten) - free(exten); + free(exten); } else if (a->pos == 4) { /* 'dialplan remove extension EXT _X_' (priority) */ char *exten = NULL, *context, *cid, *p; struct ast_context *c; @@ -509,8 +508,7 @@ } ast_unlock_contexts(); error3: - if (exten) - free(exten); + free(exten); } return ret; } @@ -935,8 +933,7 @@ if (strcmp(a->argv[6], "replace")) return CLI_SHOWUSAGE; - /* XXX overwrite argv[3] */ - whole_exten = a->argv[3]; + whole_exten = ast_strdupa(a->argv[3]); exten = strsep(&whole_exten,","); if (strchr(exten, '/')) { cidmatch = exten; @@ -1139,8 +1136,7 @@ ret = strdup(ast_get_context_name(c)); } - if (ignorepat) - free(ignorepat); + free(ignorepat); ast_unlock_contexts(); return ret; } Index: pbx/pbx_spool.c =================================================================== --- a/pbx/pbx_spool.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/pbx/pbx_spool.c (.../trunk) (revision 202568) @@ -339,7 +339,7 @@ ast_log(LOG_NOTICE, "Call failed to go through, reason (%d) %s\n", reason, ast_channel_reason2str(reason)); if (o->retries >= o->maxretries + 1) { /* Max retries exceeded */ - ast_log(LOG_EVENT, "Queued call to %s/%s expired without completion after %d attempt%s\n", o->tech, o->dest, o->retries - 1, ((o->retries - 1) != 1) ? "s" : ""); + ast_log(LOG_NOTICE, "Queued call to %s/%s expired without completion after %d attempt%s\n", o->tech, o->dest, o->retries - 1, ((o->retries - 1) != 1) ? "s" : ""); remove_from_queue(o, "Expired"); } else { /* Notate that the call is still active */ @@ -347,7 +347,6 @@ } } else { ast_log(LOG_NOTICE, "Call completed to %s/%s\n", o->tech, o->dest); - ast_log(LOG_EVENT, "Queued call to %s/%s completed\n", o->tech, o->dest); remove_from_queue(o, "Completed"); } free_outgoing(o); @@ -425,7 +424,7 @@ } res = now; } else { - ast_log(LOG_EVENT, "Queued call to %s/%s expired without completion after %d attempt%s\n", o->tech, o->dest, o->retries - 1, ((o->retries - 1) != 1) ? "s" : ""); + ast_log(LOG_NOTICE, "Queued call to %s/%s expired without completion after %d attempt%s\n", o->tech, o->dest, o->retries - 1, ((o->retries - 1) != 1) ? "s" : ""); remove_from_queue(o, "Expired"); free_outgoing(o); } Index: pbx/pbx_ael.c =================================================================== --- a/pbx/pbx_ael.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/pbx/pbx_ael.c (.../trunk) (revision 202568) @@ -251,7 +251,7 @@ ); #ifdef AAL_ARGCHECK -static char *ael_funclist[] = +static const char * const ael_funclist[] = { "AGENT", "ARRAY", Index: pbx/pbx_lua.c =================================================================== --- a/pbx/pbx_lua.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/pbx/pbx_lua.c (.../trunk) (revision 202568) @@ -93,8 +93,8 @@ static int exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data); AST_MUTEX_DEFINE_STATIC(config_file_lock); -char *config_file_data = NULL; -long config_file_size = 0; +static char *config_file_data = NULL; +static long config_file_size = 0; static struct ast_context *local_contexts = NULL; static struct ast_hashtab *local_table = NULL; Index: pbx/pbx_realtime.c =================================================================== --- a/pbx/pbx_realtime.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/pbx/pbx_realtime.c (.../trunk) (revision 202568) @@ -54,9 +54,9 @@ #define EXT_DATA_SIZE 256 -enum { +enum option_flags { OPTION_PATTERNS_DISABLED = (1 << 0), -} option_flags; +}; AST_APP_OPTIONS(switch_opts, { AST_APP_OPTION('p', OPTION_PATTERNS_DISABLED), Index: pbx/pbx_dundi.c =================================================================== --- a/pbx/pbx_dundi.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/pbx/pbx_dundi.c (.../trunk) (revision 202568) @@ -3876,7 +3876,7 @@ AST_APP_OPTION('b', OPT_BYPASS_CACHE), END_OPTIONS ); -unsigned int dundi_result_id; +static unsigned int dundi_result_id; struct dundi_result_datastore { struct dundi_result results[MAX_RESULTS]; Index: pbx/dundi-parser.c =================================================================== --- a/pbx/dundi-parser.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/pbx/dundi-parser.c (.../trunk) (revision 202568) @@ -155,7 +155,7 @@ static void dump_cause(char *output, int maxlen, void *value, int len) { - static char *causes[] = { + static const char * const causes[] = { "SUCCESS", "GENERAL", "DYNAMIC", Index: channels/h323/ast_h323.cxx =================================================================== --- a/channels/h323/ast_h323.cxx (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/h323/ast_h323.cxx (.../trunk) (revision 202568) @@ -572,8 +572,7 @@ { #ifdef H323_H450 /* Dispatcher will free out all registered handlers */ - if (h450dispatcher) - delete h450dispatcher; + delete h450dispatcher; h450dispatcher = new H450xDispatcher(*this); h4502handler = new H4502Handler(*this, *h450dispatcher); h4504handler = new MyH4504Handler(*this, *h450dispatcher); @@ -1988,8 +1987,9 @@ if (cap && cap->IsUsable(*this)) { lastcap++; lastcap = localCapabilities.SetCapability(0, lastcap, cap); - } else if (cap) + } else { delete cap; /* Capability is not usable */ + } dtmfMode = dtmf_mode; if (h323debug) { @@ -2001,8 +2001,9 @@ cap = new H323_UserInputCapability(H323_UserInputCapability::BasicString); if (cap && cap->IsUsable(*this)) { lastcap = localCapabilities.SetCapability(0, lastcap, cap); - } else if (cap) + } else { delete cap; /* Capability is not usable */ + } sendUserInputMode = SendUserInputAsString; } else { if ((dtmfMode & H323_DTMF_RFC2833) != 0) { @@ -2011,8 +2012,7 @@ lastcap = localCapabilities.SetCapability(0, lastcap, cap); else { dtmfMode |= H323_DTMF_SIGNAL; - if (cap) - delete cap; /* Capability is not usable */ + delete cap; /* Capability is not usable */ } } if ((dtmfMode & H323_DTMF_CISCO) != 0) { @@ -2024,8 +2024,7 @@ dtmfMode |= H323_DTMF_SIGNAL; } else { dtmfMode |= H323_DTMF_SIGNAL; - if (cap) - delete cap; /* Capability is not usable */ + delete cap; /* Capability is not usable */ } } if ((dtmfMode & H323_DTMF_SIGNAL) != 0) { @@ -2033,7 +2032,7 @@ cap = new H323_UserInputCapability(H323_UserInputCapability::SignalToneH245); if (cap && cap->IsUsable(*this)) lastcap = localCapabilities.SetCapability(0, lastcap, cap); - else if (cap) + else delete cap; /* Capability is not usable */ } sendUserInputMode = SendUserInputAsTone; /* RFC2833 transmission handled at Asterisk level */ Index: channels/h323/README =================================================================== --- a/channels/h323/README (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/h323/README (.../trunk) (revision 202568) @@ -137,7 +137,7 @@ If you are motivated to update/fix this code please submit a disclaimer along with the patch to the Asterisk bug -tracker: http://bugs.digium.com/ +tracker: https://issues.asterisk.org/ Jeremy McNamara Index: channels/misdn_config.c =================================================================== --- a/channels/misdn_config.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/misdn_config.c (.../trunk) (revision 202568) @@ -1,6 +1,6 @@ /* * Asterisk -- An open source telephony toolkit. - * + * * Copyright (C) 2005, Christian Richter * * Christian Richter @@ -132,7 +132,7 @@ { "musicclass", MISDN_CFG_MUSICCLASS, MISDN_CTYPE_STR, "default", NONE, "Sets the musiconhold class." }, { "callerid", MISDN_CFG_CALLERID, MISDN_CTYPE_STR, "", NONE, - "Sets the caller ID." }, + "Set the outgoing caller id to the value." }, { "method", MISDN_CFG_METHOD, MISDN_CTYPE_STR, "standard", NONE, "Set the method to use for channel selection:\n" "\t standard - Use the first free channel starting from the lowest number.\n" @@ -140,62 +140,71 @@ "\t round_robin - Use the round robin algorithm to select a channel. Use this\n" "\t if you want to balance your load." }, { "dialplan", MISDN_CFG_DIALPLAN, MISDN_CTYPE_INT, "0", NONE, - "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n" - "\n" + "Dialplan means Type Of Number in ISDN Terms\n" "\tThere are different types of the dialplan:\n" "\n" - "\tdialplan -> outgoing Number\n" - "\tlocaldialplan -> callerid\n" - "\tcpndialplan -> connected party number\n" + "\tdialplan -> for outgoing call's dialed number\n" + "\tlocaldialplan -> for outgoing call's callerid\n" + "\t (if -1 is set use the value from the asterisk channel)\n" + "\tcpndialplan -> for incoming call's connected party number sent to caller\n" + "\t (if -1 is set use the value from the asterisk channel)\n" "\n" "\tdialplan options:\n" "\n" "\t0 - unknown\n" "\t1 - International\n" "\t2 - National\n" - "\t4 - Subscriber\n" - "\n" - "\tThis setting is used for outgoing calls." }, + "\t4 - Subscriber" }, { "localdialplan", MISDN_CFG_LOCALDIALPLAN, MISDN_CTYPE_INT, "0", NONE, - "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n" - "\n" + "Dialplan means Type Of Number in ISDN Terms\n" "\tThere are different types of the dialplan:\n" "\n" - "\tdialplan -> outgoing Number\n" - "\tlocaldialplan -> callerid\n" - "\tcpndialplan -> connected party number\n" + "\tdialplan -> for outgoing call's dialed number\n" + "\tlocaldialplan -> for outgoing call's callerid\n" + "\t (if -1 is set use the value from the asterisk channel)\n" + "\tcpndialplan -> for incoming call's connected party number sent to caller\n" + "\t (if -1 is set use the value from the asterisk channel)\n" "\n" "\tdialplan options:\n" "\n" "\t0 - unknown\n" "\t1 - International\n" "\t2 - National\n" - "\t4 - Subscriber\n" - "\n" - "\tThis setting is used for outgoing calls." }, + "\t4 - Subscriber" }, { "cpndialplan", MISDN_CFG_CPNDIALPLAN, MISDN_CTYPE_INT, "0", NONE, - "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n" - "\n" + "Dialplan means Type Of Number in ISDN Terms\n" "\tThere are different types of the dialplan:\n" "\n" - "\tdialplan -> outgoing Number\n" - "\tlocaldialplan -> callerid\n" - "\tcpndialplan -> connected party number\n" + "\tdialplan -> for outgoing call's dialed number\n" + "\tlocaldialplan -> for outgoing call's callerid\n" + "\t (if -1 is set use the value from the asterisk channel)\n" + "\tcpndialplan -> for incoming call's connected party number sent to caller\n" + "\t (if -1 is set use the value from the asterisk channel)\n" "\n" "\tdialplan options:\n" "\n" "\t0 - unknown\n" "\t1 - International\n" "\t2 - National\n" - "\t4 - Subscriber\n" - "\n" - "\tThis setting is used for outgoing calls." }, - { "nationalprefix", MISDN_CFG_NATPREFIX, MISDN_CTYPE_STR, "0", NONE, - "Prefix for national, this is put before the\n" - "\toad if an according dialplan is set by the other end." }, - { "internationalprefix", MISDN_CFG_INTERNATPREFIX, MISDN_CTYPE_STR, "00", NONE, - "Prefix for international, this is put before the\n" - "\toad if an according dialplan is set by the other end." }, + "\t4 - Subscriber" }, + { "unknownprefix", MISDN_CFG_TON_PREFIX_UNKNOWN, MISDN_CTYPE_STR, "", NONE, + "Prefix for unknown numbers, this is put before an incoming number\n" + "\tif its type-of-number is unknown." }, + { "internationalprefix", MISDN_CFG_TON_PREFIX_INTERNATIONAL, MISDN_CTYPE_STR, "00", NONE, + "Prefix for international numbers, this is put before an incoming number\n" + "\tif its type-of-number is international." }, + { "nationalprefix", MISDN_CFG_TON_PREFIX_NATIONAL, MISDN_CTYPE_STR, "0", NONE, + "Prefix for national numbers, this is put before an incoming number\n" + "\tif its type-of-number is national." }, + { "netspecificprefix", MISDN_CFG_TON_PREFIX_NETWORK_SPECIFIC, MISDN_CTYPE_STR, "", NONE, + "Prefix for network-specific numbers, this is put before an incoming number\n" + "\tif its type-of-number is network-specific." }, + { "subscriberprefix", MISDN_CFG_TON_PREFIX_SUBSCRIBER, MISDN_CTYPE_STR, "", NONE, + "Prefix for subscriber numbers, this is put before an incoming number\n" + "\tif its type-of-number is subscriber." }, + { "abbreviatedprefix", MISDN_CFG_TON_PREFIX_ABBREVIATED, MISDN_CTYPE_STR, "", NONE, + "Prefix for abbreviated numbers, this is put before an incoming number\n" + "\tif its type-of-number is abbreviated." }, { "presentation", MISDN_CFG_PRES, MISDN_CTYPE_INT, "-1", NONE, "These (presentation and screen) are the exact isdn screening and presentation\n" "\tindicators.\n" @@ -212,6 +221,28 @@ "\n" "\tscreen=0, presentation=0 -> callerid presented\n" "\tscreen=1, presentation=1 -> callerid restricted (the remote end doesn't see it!)" }, + { "outgoing_colp", MISDN_CFG_OUTGOING_COLP, MISDN_CTYPE_INT, "0", NONE, + "Select what to do with outgoing COLP information on this port.\n" + "\n" + "\t0 - Send out COLP information unaltered.\n" + "\t1 - Force COLP to restricted on all outgoing COLP information.\n" + "\t2 - Do not send COLP information." }, + { "display_connected", MISDN_CFG_DISPLAY_CONNECTED, MISDN_CTYPE_INT, "0", NONE, + "Put a display ie in the CONNECT message containing the following\n" + "\tinformation if it is available (nt port only):\n" + "\n" + "\t0 - Do not put the connected line information in the display ie.\n" + "\t1 - Put the available connected line name in the display ie.\n" + "\t2 - Put the available connected line number in the display ie.\n" + "\t3 - Put the available connected line name and number in the display ie." }, + { "display_setup", MISDN_CFG_DISPLAY_SETUP, MISDN_CTYPE_INT, "0", NONE, + "Put a display ie in the SETUP message containing the following\n" + "\tinformation if it is available (nt port only):\n" + "\n" + "\t0 - Do not put the caller information in the display ie.\n" + "\t1 - Put the available caller name in the display ie.\n" + "\t2 - Put the available caller number in the display ie.\n" + "\t3 - Put the available caller name and number in the display ie." }, { "always_immediate", MISDN_CFG_ALWAYS_IMMEDIATE, MISDN_CTYPE_BOOL, "no", NONE, "Enable this to get into the s dialplan-extension.\n" "\tThere you can use DigitTimeout if you can't or don't want to use\n" @@ -220,7 +251,7 @@ { "nodialtone", MISDN_CFG_NODIALTONE, MISDN_CTYPE_BOOL, "no", NONE, "Enable this to prevent chan_misdn to generate the dialtone\n" "\tThis makes only sense together with the always_immediate=yes option\n" - "\tto generate your own dialtone with Playtones or so."}, + "\tto generate your own dialtone with Playtones or so." }, { "immediate", MISDN_CFG_IMMEDIATE, MISDN_CTYPE_BOOL, "no", NONE, "Enable this if you want callers which called exactly the base\n" "\tnumber (so no extension is set) to jump into the s extension.\n" @@ -257,17 +288,17 @@ #endif #ifdef WITH_BEROEC { "bnechocancel", MISDN_CFG_BNECHOCANCEL, MISDN_CTYPE_BOOLINT, "yes", 64, - "echotail in ms (1-200)\n"}, + "echotail in ms (1-200)" }, { "bnec_antihowl", MISDN_CFG_BNEC_ANTIHOWL, MISDN_CTYPE_INT, "0", NONE, - "Use antihowl\n"}, + "Use antihowl" }, { "bnec_nlp", MISDN_CFG_BNEC_NLP, MISDN_CTYPE_BOOL, "yes", NONE, - "Nonlinear Processing (much faster adaption)"}, + "Nonlinear Processing (much faster adaption)" }, { "bnec_zerocoeff", MISDN_CFG_BNEC_ZEROCOEFF, MISDN_CTYPE_BOOL, "no", NONE, - "ZeroCoeffeciens\n"}, + "ZeroCoeffeciens" }, { "bnec_tonedisabler", MISDN_CFG_BNEC_TD, MISDN_CTYPE_BOOL, "no", NONE, - "Disable Tone\n"}, + "Disable Tone" }, { "bnec_adaption", MISDN_CFG_BNEC_ADAPT, MISDN_CTYPE_INT, "1", NONE, - "Adaption mode (0=no,1=full,2=fast)\n"}, + "Adaption mode (0=no,1=full,2=fast)" }, #endif { "need_more_infos", MISDN_CFG_NEED_MORE_INFOS, MISDN_CTYPE_BOOL, "0", NONE, "Send Setup_Acknowledge on incoming calls anyway (instead of PROCEEDING),\n" @@ -311,13 +342,13 @@ { "faxdetect_context", MISDN_CFG_FAXDETECT_CONTEXT, MISDN_CTYPE_STR, NO_DEFAULT, NONE, "Context to jump into if we detect a fax. Don't set this if you want to stay in the current context." }, { "l1watcher_timeout", MISDN_CFG_L1_TIMEOUT, MISDN_CTYPE_BOOLINT, "0", 4, - "Watches the layer 1. If the layer 1 is down, it tries to\n" - "\tget it up. The timeout is given in seconds. with 0 as value it\n" - "\tdoes not watch the l1 at all\n" + "Monitors L1 of the port. If L1 is down it tries\n" + "\tto bring it up. The polling timeout is given in seconds.\n" + "\tSetting the value to 0 disables monitoring L1 of the port.\n" "\n" - "\tThis option is only read at loading time of chan_misdn, which\n" - "\tmeans you need to unload and load chan_misdn to change the value,\n" - "\tan Asterisk restart should do the trick." }, + "\tThis option is only read at chan_misdn loading time.\n" + "\tYou need to unload and load chan_misdn to change the\n" + "\tvalue. An asterisk restart will also do the trick." }, { "overlapdial", MISDN_CFG_OVERLAP_DIAL, MISDN_CTYPE_BOOLINT, "0", 4, "Enables overlap dial for the given amount of seconds.\n" "\tPossible values are positive integers or:\n" @@ -336,6 +367,8 @@ "MSN's for TE ports, listen on those numbers on the above ports, and\n" "\tindicate the incoming calls to Asterisk.\n" "\tHere you can give a comma separated list, or simply an '*' for any msn." }, + { "cc_request_retention", MISDN_CFG_CC_REQUEST_RETENTION, MISDN_CTYPE_BOOL, "yes", NONE, + "Enable/Disable call-completion request retention support (ptp)." }, }; static const struct misdn_cfg_spec gen_spec[] = { @@ -365,8 +398,8 @@ { "crypt_keys", MISDN_GEN_CRYPT_KEYS, MISDN_CTYPE_STR, NO_DEFAULT, NONE, "Keys for cryption, you reference them in the dialplan\n" "\tLater also in dynamic encr." }, - { "ntkeepcalls", MISDN_GEN_NTKEEPCALLS, MISDN_CTYPE_BOOL, "no", NONE, - "avoid dropping calls if the L2 goes down. some Nortel pbx\n" + { "ntkeepcalls", MISDN_GEN_NTKEEPCALLS, MISDN_CTYPE_BOOL, "no", NONE, + "avoid dropping calls if the L2 goes down. some Nortel pbx\n" "do put down the L2/L1 for some milliseconds even if there\n" "are running calls. with this option you can avoid dropping them" }, { "ntdebugflags", MISDN_GEN_NTDEBUGFLAGS, MISDN_CTYPE_INT, "0", NONE, @@ -387,7 +420,7 @@ /* maps enum config elements to array positions */ static int *map; -static ast_mutex_t config_mutex; +static ast_mutex_t config_mutex; #define CLI_ERROR(name, value, section) ({ \ ast_log(LOG_WARNING, "misdn.conf: \"%s=%s\" (section: %s) invalid or out of range. " \ @@ -476,7 +509,7 @@ int i, j; int gn = map[MISDN_CFG_GROUPNAME]; union misdn_cfg_pt* free_list[max_ports + 2]; - + memset(free_list, 0, sizeof(free_list)); free_list[0] = port_cfg[0]; for (i = 1; i <= max_ports; ++i) { @@ -508,7 +541,7 @@ { int i; - for (i = 0; i < NUM_GEN_ELEMENTS; i++) + for (i = 0; i < NUM_GEN_ELEMENTS; i++) if (general_cfg[i].any) ast_free(general_cfg[i].any); } @@ -567,7 +600,7 @@ misdn_cfg_unlock(); } -enum misdn_cfg_elements misdn_cfg_get_elem(char *name) +enum misdn_cfg_elements misdn_cfg_get_elem(const char *name) { int pos; @@ -580,11 +613,11 @@ pos = get_cfg_position(name, PORT_CFG); if (pos >= 0) return port_spec[pos].elem; - + pos = get_cfg_position(name, GEN_CFG); if (pos >= 0) return gen_spec[pos].elem; - + return MISDN_CFG_FIRST; } @@ -598,7 +631,7 @@ memset(buf, 0, 1); return; } - + /* here comes a hack to replace the (not existing) "name" element with the "ports" element */ if (elem == MISDN_CFG_GROUPNAME) { if (!snprintf(buf, bufsize, "ports")) @@ -631,7 +664,7 @@ spec = (struct misdn_cfg_spec *)port_spec; else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST)) spec = (struct misdn_cfg_spec *)gen_spec; - + if (!spec || !spec[place].desc) memset(buf, 0, 1); else { @@ -660,7 +693,7 @@ iter = port_cfg[port][map[MISDN_CFG_MSNS]].ml; else iter = port_cfg[0][map[MISDN_CFG_MSNS]].ml; - for (; iter; iter = iter->next) + for (; iter; iter = iter->next) if (*(iter->msn) == '*' || ast_extension_match(iter->msn, msn)) { re = 1; break; @@ -689,7 +722,7 @@ for (i = 1; i <= max_ports; i++) { if (port_cfg[i] && port_cfg[i][map[MISDN_CFG_GROUPNAME]].str) { if (!strcasecmp(port_cfg[i][map[MISDN_CFG_GROUPNAME]].str, group)) - method = (port_cfg[i][map[MISDN_CFG_METHOD]].str ? + method = (port_cfg[i][map[MISDN_CFG_METHOD]].str ? port_cfg[i][map[MISDN_CFG_METHOD]].str : port_cfg[0][map[MISDN_CFG_METHOD]].str); } } @@ -709,7 +742,7 @@ return re; } -/*! +/*! * \brief Generate a comma separated list of all active ports */ void misdn_cfg_get_ports_string (char *ports) @@ -777,10 +810,10 @@ break; case MISDN_CTYPE_ASTGROUP: if (port_cfg[port][place].grp) - snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, + snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[port][place].grp)); else if (port_cfg[0][place].grp) - snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, + snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[0][place].grp)); else snprintf(buf, bufsize, " -> %s:", port_spec[place].name); @@ -847,7 +880,7 @@ { int p = -1; int gn = map[MISDN_CFG_GROUPNAME]; - + misdn_cfg_lock(); for (port++; port <= max_ports; port++) { if (port_cfg[port][gn].str) { @@ -939,7 +972,7 @@ for (; v; v = v->next) { if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) continue; - if (((pos = get_cfg_position(v->name, GEN_CFG)) < 0) || + if (((pos = get_cfg_position(v->name, GEN_CFG)) < 0) || (_parse(&general_cfg[pos], v->value, gen_spec[pos].type, gen_spec[pos].boolint_def) < 0)) CLI_ERROR(v->name, v->value, "general"); } @@ -961,7 +994,7 @@ cfg_for_ports[0] = 1; } - if (((pos = get_cfg_position("name", PORT_CFG)) < 0) || + if (((pos = get_cfg_position("name", PORT_CFG)) < 0) || (_parse(&cfg_tmp[pos], cat, port_spec[pos].type, port_spec[pos].boolint_def) < 0)) { CLI_ERROR(v->name, v->value, cat); return; @@ -972,7 +1005,7 @@ char *token, *tmp = ast_strdupa(v->value); char ptpbuf[BUFFERSIZE] = ""; int start, end; - for (token = strsep(&tmp, ","); token; token = strsep(&tmp, ","), *ptpbuf = 0) { + for (token = strsep(&tmp, ","); token; token = strsep(&tmp, ","), *ptpbuf = 0) { if (!*token) continue; if (sscanf(token, "%d-%d%s", &start, &end, ptpbuf) >= 2) { @@ -995,7 +1028,7 @@ } } } else { - if (((pos = get_cfg_position(v->name, PORT_CFG)) < 0) || + if (((pos = get_cfg_position(v->name, PORT_CFG)) < 0) || (_parse(&cfg_tmp[pos], v->value, port_spec[pos].type, port_spec[pos].boolint_def) < 0)) CLI_ERROR(v->name, v->value, cat); } Index: channels/chan_jingle.c =================================================================== --- a/channels/chan_jingle.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/chan_jingle.c (.../trunk) (revision 202568) @@ -53,7 +53,7 @@ #include "asterisk/pbx.h" #include "asterisk/sched.h" #include "asterisk/io.h" -#include "asterisk/rtp.h" +#include "asterisk/rtp_engine.h" #include "asterisk/acl.h" #include "asterisk/callerid.h" #include "asterisk/file.h" @@ -112,9 +112,9 @@ char exten[80]; /*!< Called extension */ struct ast_channel *owner; /*!< Master Channel */ char audio_content_name[100]; /*!< name attribute of content tag */ - struct ast_rtp *rtp; /*!< RTP audio session */ + struct ast_rtp_instance *rtp; /*!< RTP audio session */ char video_content_name[100]; /*!< name attribute of content tag */ - struct ast_rtp *vrtp; /*!< RTP video session */ + struct ast_rtp_instance *vrtp; /*!< RTP video session */ int jointcapability; /*!< Supported capability at both ends (codecs ) */ int peercapability; struct jingle_pvt *next; /* Next entity */ @@ -183,11 +183,6 @@ static struct jingle_pvt *jingle_alloc(struct jingle *client, const char *from, const char *sid); static char *jingle_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); static char *jingle_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); -/*----- RTP interface functions */ -static int jingle_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, - struct ast_rtp *vrtp, struct ast_rtp *tpeer, int codecs, int nat_active); -static enum ast_rtp_get_result jingle_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); -static int jingle_get_codec(struct ast_channel *chan); /*! \brief PBX interface structure for channel registration */ static const struct ast_channel_tech jingle_tech = { @@ -197,7 +192,7 @@ .requester = jingle_request, .send_digit_begin = jingle_digit_begin, .send_digit_end = jingle_digit_end, - .bridge = ast_rtp_bridge, + .bridge = ast_rtp_instance_bridge, .call = jingle_call, .hangup = jingle_hangup, .answer = jingle_answer, @@ -216,15 +211,6 @@ static struct io_context *io; /*!< The IO context */ static struct in_addr __ourip; - -/*! \brief RTP driver interface */ -static struct ast_rtp_protocol jingle_rtp = { - type: "Jingle", - get_rtp_info: jingle_get_rtp_peer, - set_rtp_peer: jingle_set_rtp_peer, - get_codec: jingle_get_codec, -}; - static struct ast_cli_entry jingle_cli[] = { AST_CLI_DEFINE(jingle_do_reload, "Reload Jingle configuration"), AST_CLI_DEFINE(jingle_show_channels, "Show Jingle channels"), @@ -304,7 +290,6 @@ iks_insert_attrib(payload_g723, "name", "G723"); iks_insert_node(dcodecs, payload_g723); } - ast_rtp_lookup_code(p->rtp, 1, codec); } static int jingle_accept_call(struct jingle *client, struct jingle_pvt *p) @@ -398,18 +383,19 @@ return res; } -static enum ast_rtp_get_result jingle_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) +static enum ast_rtp_glue_result jingle_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) { struct jingle_pvt *p = chan->tech_pvt; - enum ast_rtp_get_result res = AST_RTP_GET_FAILED; + enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_FORBID; if (!p) return res; ast_mutex_lock(&p->lock); if (p->rtp) { - *rtp = p->rtp; - res = AST_RTP_TRY_PARTIAL; + ao2_ref(p->rtp, +1); + *instance = p->rtp; + res = AST_RTP_GLUE_RESULT_LOCAL; } ast_mutex_unlock(&p->lock); @@ -422,7 +408,7 @@ return p->peercapability; } -static int jingle_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *tpeer, int codecs, int nat_active) +static int jingle_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *tpeer, int codecs, int nat_active) { struct jingle_pvt *p; @@ -442,6 +428,13 @@ return 0; } +static struct ast_rtp_glue jingle_rtp_glue = { + .type = "Jingle", + .get_rtp_info = jingle_get_rtp_peer, + .get_codec = jingle_get_codec, + .update_peer = jingle_set_rtp_peer, +}; + static int jingle_response(struct jingle *client, ikspak *pak, const char *reasonstr, const char *reasonstr2) { iks *response = NULL, *error = NULL, *reason = NULL; @@ -584,7 +577,7 @@ struct jingle_candidate *tmp; struct aji_client *c = client->connection; struct jingle_candidate *ours1 = NULL, *ours2 = NULL; - struct sockaddr_in sin; + struct sockaddr_in sin = { 0, }; struct sockaddr_in dest; struct in_addr us; struct in_addr externaddr; @@ -621,7 +614,7 @@ goto safeout; } - ast_rtp_get_us(p->rtp, &sin); + ast_rtp_instance_get_local_address(p->rtp, &sin); ast_find_ourip(&us, bindaddr); /* Setup our first jingle candidate */ @@ -779,7 +772,7 @@ ast_copy_string(tmp->them, idroster, sizeof(tmp->them)); tmp->initiator = 1; } - tmp->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); + tmp->rtp = ast_rtp_instance_new(NULL, sched, &bindaddr, NULL); tmp->parent = client; if (!tmp->rtp) { ast_log(LOG_WARNING, "Out of RTP sessions?\n"); @@ -825,18 +818,18 @@ /* Set Frame packetization */ if (i->rtp) - ast_rtp_codec_setpref(i->rtp, &i->prefs); + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(i->rtp), i->rtp, &i->prefs); tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) | (i->jointcapability & AST_FORMAT_VIDEO_MASK); fmt = ast_best_codec(tmp->nativeformats); if (i->rtp) { - ast_channel_set_fd(tmp, 0, ast_rtp_fd(i->rtp)); - ast_channel_set_fd(tmp, 1, ast_rtcp_fd(i->rtp)); + ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(i->rtp, 0)); + ast_channel_set_fd(tmp, 1, ast_rtp_instance_fd(i->rtp, 1)); } if (i->vrtp) { - ast_channel_set_fd(tmp, 2, ast_rtp_fd(i->vrtp)); - ast_channel_set_fd(tmp, 3, ast_rtcp_fd(i->vrtp)); + ast_channel_set_fd(tmp, 2, ast_rtp_instance_fd(i->vrtp, 0)); + ast_channel_set_fd(tmp, 3, ast_rtp_instance_fd(i->vrtp, 1)); } if (state == AST_STATE_RING) tmp->rings = 1; @@ -942,9 +935,9 @@ if (p->owner) ast_log(LOG_WARNING, "Uh oh, there's an owner, this is going to be messy.\n"); if (p->rtp) - ast_rtp_destroy(p->rtp); + ast_rtp_instance_destroy(p->rtp); if (p->vrtp) - ast_rtp_destroy(p->vrtp); + ast_rtp_instance_destroy(p->vrtp); jingle_free_candidates(p->theircandidates); ast_free(p); } @@ -1009,8 +1002,8 @@ ast_copy_string(p->audio_content_name, iks_find_attrib(content, "name"), sizeof(p->audio_content_name)); while (codec) { - ast_rtp_set_m_type(p->rtp, atoi(iks_find_attrib(codec, "id"))); - ast_rtp_set_rtpmap_type(p->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); + ast_rtp_codecs_payloads_set_m_type(ast_rtp_instance_get_codecs(p->rtp), p->rtp, atoi(iks_find_attrib(codec, "id"))); + ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(p->rtp), p->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); codec = iks_next(codec); } } @@ -1025,8 +1018,8 @@ ast_copy_string(p->video_content_name, iks_find_attrib(content, "name"), sizeof(p->video_content_name)); while (codec) { - ast_rtp_set_m_type(p->rtp, atoi(iks_find_attrib(codec, "id"))); - ast_rtp_set_rtpmap_type(p->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); + ast_rtp_codecs_payloads_set_m_type(ast_rtp_instance_get_codecs(p->rtp), p->rtp, atoi(iks_find_attrib(codec, "id"))); + ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(p->rtp), p->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); codec = iks_next(codec); } } @@ -1079,7 +1072,7 @@ sin.sin_port = htons(tmp->port); snprintf(username, sizeof(username), "%s:%s", tmp->ufrag, p->ourcandidates->ufrag); - ast_rtp_stun_request(p->rtp, &sin, username); + ast_rtp_instance_stun_request(p->rtp, &sin, username); tmp = tmp->next; } return 1; @@ -1169,7 +1162,7 @@ if (!p->rtp) return &ast_null_frame; - f = ast_rtp_read(p->rtp); + f = ast_rtp_instance_read(p->rtp, 0); jingle_update_stun(p->parent, p); if (p->owner) { /* We already hold the channel lock */ @@ -1220,7 +1213,7 @@ if (p) { ast_mutex_lock(&p->lock); if (p->rtp) { - res = ast_rtp_write(p->rtp, frame); + res = ast_rtp_instance_write(p->rtp, frame); } ast_mutex_unlock(&p->lock); } @@ -1229,7 +1222,7 @@ if (p) { ast_mutex_lock(&p->lock); if (p->vrtp) { - res = ast_rtp_write(p->vrtp, frame); + res = ast_rtp_instance_write(p->vrtp, frame); } ast_mutex_unlock(&p->lock); } @@ -1879,7 +1872,7 @@ return 0; } - ast_rtp_proto_register(&jingle_rtp); + ast_rtp_glue_register(&jingle_rtp_glue); ast_cli_register_multiple(jingle_cli, ARRAY_LEN(jingle_cli)); /* Make sure we can register our channel type */ if (ast_channel_register(&jingle_tech)) { @@ -1902,7 +1895,7 @@ ast_cli_unregister_multiple(jingle_cli, ARRAY_LEN(jingle_cli)); /* First, take us out of the channel loop */ ast_channel_unregister(&jingle_tech); - ast_rtp_proto_unregister(&jingle_rtp); + ast_rtp_glue_unregister(&jingle_rtp_glue); if (!ast_mutex_lock(&jinglelock)) { /* Hangup all interfaces if they have an owner */ Index: channels/chan_dahdi.c =================================================================== --- a/channels/chan_dahdi.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/chan_dahdi.c (.../trunk) (revision 202568) @@ -62,6 +62,7 @@ #include #include +#include "sig_analog.h" #ifdef HAVE_PRI #include @@ -154,11 +155,94 @@ This application will Accept the R2 call either with charge or no charge. + + + Transfer DAHDI Channel. + + + + + DAHDI channel name to transfer. + + + + Transfer a DAHDI channel. + + + + + Hangup DAHDI Channel. + + + + + DAHDI channel name to hangup. + + + + Hangup a DAHDI channel. + + + + + Dial over DAHDI channel while offhook. + + + + + + + + + + + + Toggle DAHDI channel Do Not Disturb status ON. + + + + + + + + + + + Toggle DAHDI channel Do Not Disturb status OFF. + + + + + + + + + + + Show status DAHDI channels. + + + + + + + + + + + Fully Restart DAHDI channels (terminates calls). + + + + + + + ***/ #define SMDI_MD_WAIT_TIMEOUT 1500 /* 1.5 seconds */ -static const char *lbostr[] = { +static const char * const lbostr[] = { "0 db (CSU)/0-133 feet (DSX-1)", "133-266 feet (DSX-1)", "266-399 feet (DSX-1)", @@ -292,6 +376,28 @@ #define CALLPROGRESS_FAX_INCOMING 4 #define CALLPROGRESS_FAX (CALLPROGRESS_FAX_INCOMING | CALLPROGRESS_FAX_OUTGOING) +#ifdef HAVE_PRI_SERVICE_MESSAGES +/*! \brief Persistent Service State */ +#define SRVST_DBKEY "service-state" +/*! \brief The out-of-service SERVICE state */ +#define SRVST_TYPE_OOS "O" +/*! \brief SRVST_INITIALIZED is used to indicate a channel being out-of-service + * The SRVST_INITIALIZED is mostly used maintain backwards compatibility but also may + * mean that the channel has not yet received a RESTART message. If a channel is + * out-of-service with this reason a RESTART message will result in the channel + * being put into service. */ +#define SRVST_INITIALIZED 0 +/*! \brief SRVST_NEAREND is used to indicate that the near end was put out-of-service */ +#define SRVST_NEAREND (1 << 0) +/*! \brief SRVST_FAREND is used to indicate that the far end was taken out-of-service */ +#define SRVST_FAREND (1 << 1) +/*! \brief SRVST_BOTH is used to indicate that both sides of the channel are out-of-service */ +#define SRVST_BOTH (SRVST_NEAREND | SRVST_FAREND) + +/*! \brief The AstDB family */ +static const char dahdi_db[] = "dahdi/registry"; +#endif + static char defaultcic[64] = ""; static char defaultozz[64] = ""; @@ -363,6 +469,8 @@ static int dahdi_sendtext(struct ast_channel *c, const char *text); +static int analog_lib_handles(int signalling, int radio, int oprmode); + static void mwi_event_cb(const struct ast_event *event, void *userdata) { /* This module does not handle MWI in an event-based manner. However, it @@ -475,6 +583,9 @@ int max_ani; int max_dnis; int get_ani_first:1; +#if defined(OR2_LIB_INTERFACE) && OR2_LIB_INTERFACE > 1 + int skip_category_request:1; +#endif int call_files:1; int allow_collect_calls:1; int charge_calls:1; @@ -543,6 +654,9 @@ int resetting; /*! \brief Current position during a reset (-1 if not started) */ int resetpos; +#ifdef HAVE_PRI_SERVICE_MESSAGES + unsigned int enable_service_message_support:1; /*!< enable SERVICE message support */ +#endif #ifdef HAVE_PRI_INBANDDISCONNECT unsigned int inbanddisconnect:1; /*!< Should we support inband audio after receiving DISCONNECT? */ #endif @@ -595,7 +709,7 @@ struct ringContextData ringContext[3]; }; -static char *subnames[] = { +static const char * const subnames[] = { "Real", "Callwait", "Threeway" @@ -645,6 +759,7 @@ static struct dahdi_pvt { ast_mutex_t lock; + struct callerid_state *cs; struct ast_channel *owner; /*!< Our current active owner (if applicable) */ /*!< Up to three channels can be associated with this call */ @@ -757,10 +872,10 @@ unsigned int echocanon:1; /*! \brief TRUE if a fax tone has already been handled. */ unsigned int faxhandled:1; - /*! \brief TRUE if dynamic faxbuffers are configured for use, default is OFF */ + /*! TRUE if dynamic faxbuffers are configured for use, default is OFF */ unsigned int usefaxbuffers:1; - /*! \brief TRUE while dynamic faxbuffers are in use */ - unsigned int faxbuffersinuse:1; + /*! TRUE while buffer configuration override is in use */ + unsigned int bufferoverrideinuse:1; /*! \brief TRUE if over a radio and dahdi_read() has been called. */ unsigned int firstradio:1; /*! @@ -1225,6 +1340,7 @@ char begindigit; /*! \brief TRUE if confrence is muted. */ int muting; + void *sig_pvt; } *iflist = NULL, *ifend = NULL; /*! \brief Channel configuration from chan_dahdi.conf . @@ -1305,6 +1421,9 @@ .max_ani = 10, .max_dnis = 4, .get_ani_first = -1, +#if defined(OR2_LIB_INTERFACE) && OR2_LIB_INTERFACE > 1 + .skip_category_request = -1, +#endif .call_files = 0, .allow_collect_calls = 0, .charge_calls = 1, @@ -1356,8 +1475,6 @@ .buf_policy = DAHDI_POLICY_IMMEDIATE, .buf_no = numbufs, .usefaxbuffers = 0, - .faxbuf_policy = DAHDI_POLICY_IMMEDIATE, - .faxbuf_no = numbufs, }, .timing = { .prewinktime = -1, @@ -1390,8 +1507,10 @@ static int dahdi_indicate(struct ast_channel *chan, int condition, const void *data, size_t datalen); static int dahdi_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); static int dahdi_setoption(struct ast_channel *chan, int option, void *data, int datalen); +static int dahdi_queryoption(struct ast_channel *chan, int option, void *data, int *datalen); static int dahdi_func_read(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len); static int handle_init_event(struct dahdi_pvt *i, int event); +static int dahdi_func_write(struct ast_channel *chan, const char *function, char *data, const char *value); static const struct ast_channel_tech dahdi_tech = { .type = "DAHDI", @@ -1411,7 +1530,9 @@ .indicate = dahdi_indicate, .fixup = dahdi_fixup, .setoption = dahdi_setoption, + .queryoption = dahdi_queryoption, .func_channel_read = dahdi_func_read, + .func_channel_write = dahdi_func_write, }; #ifdef HAVE_PRI @@ -1420,8 +1541,913 @@ #define GET_CHANNEL(p) ((p)->channel) #endif -struct dahdi_pvt *round_robin[32]; +static enum analog_sigtype dahdisig_to_analogsig(int sig) +{ + switch (sig) { + case SIG_FXOLS: + return ANALOG_SIG_FXOLS; + case SIG_FXOGS: + return ANALOG_SIG_FXOGS; + case SIG_FXOKS: + return ANALOG_SIG_FXOKS; + case SIG_FXSLS: + return ANALOG_SIG_FXSLS; + case SIG_FXSGS: + return ANALOG_SIG_FXSGS; + case SIG_FXSKS: + return ANALOG_SIG_FXSKS; + case SIG_EMWINK: + return ANALOG_SIG_EMWINK; + case SIG_EM: + return ANALOG_SIG_EM; + case SIG_EM_E1: + return ANALOG_SIG_EM_E1; + case SIG_FEATD: + return ANALOG_SIG_FEATD; + case SIG_FEATDMF: + return ANALOG_SIG_FEATDMF; + case SIG_E911: + return SIG_E911; + case SIG_FGC_CAMA: + return ANALOG_SIG_FGC_CAMA; + case SIG_FGC_CAMAMF: + return ANALOG_SIG_FGC_CAMAMF; + case SIG_FEATB: + return ANALOG_SIG_FEATB; + case SIG_SFWINK: + return ANALOG_SIG_SFWINK; + case SIG_SF: + return ANALOG_SIG_SF; + case SIG_SF_FEATD: + return ANALOG_SIG_SF_FEATD; + case SIG_SF_FEATDMF: + return ANALOG_SIG_SF_FEATDMF; + case SIG_FEATDMF_TA: + return ANALOG_SIG_FEATDMF_TA; + case SIG_SF_FEATB: + return ANALOG_SIG_FEATB; + default: + return -1; + } +} + +static int analog_tone_to_dahditone(enum analog_tone tone) +{ + switch (tone) { + case ANALOG_TONE_RINGTONE: + return DAHDI_TONE_RINGTONE; + case ANALOG_TONE_STUTTER: + return DAHDI_TONE_STUTTER; + case ANALOG_TONE_CONGESTION: + return DAHDI_TONE_CONGESTION; + case ANALOG_TONE_DIALTONE: + return DAHDI_TONE_DIALTONE; + case ANALOG_TONE_DIALRECALL: + return DAHDI_TONE_DIALRECALL; + case ANALOG_TONE_INFO: + return DAHDI_TONE_INFO; + default: + return -1; + } +} + +static int analogsub_to_dahdisub(enum analog_sub analogsub) +{ + int index; + + switch (analogsub) { + case ANALOG_SUB_REAL: + index = SUB_REAL; + break; + case ANALOG_SUB_CALLWAIT: + index = SUB_CALLWAIT; + break; + case ANALOG_SUB_THREEWAY: + index = SUB_THREEWAY; + break; + default: + ast_log(LOG_ERROR, "Unidentified sub!\n"); + index = SUB_REAL; + } + + return index; +} + +static enum analog_event dahdievent_to_analogevent(int event); +static int bump_gains(struct dahdi_pvt *p); +static int dahdi_setlinear(int dfd, int linear); + +static int my_start_cid_detect(void *pvt, int cid_signalling) +{ + struct dahdi_pvt *p = pvt; + int index = SUB_REAL; + p->cs = callerid_new(cid_signalling); + if (!p->cs) { + ast_log(LOG_ERROR, "Unable to alloc callerid\n"); + return -1; + } + bump_gains(p); + dahdi_setlinear(p->subs[index].dfd, 0); + + return 0; +} + +static int my_stop_cid_detect(void *pvt) +{ + struct dahdi_pvt *p = pvt; + int index = SUB_REAL; + if (p->cs) + callerid_free(p->cs); + dahdi_setlinear(p->subs[index].dfd, p->subs[index].linear); + return 0; +} + +static int my_get_callerid(void *pvt, char *namebuf, char *numbuf, enum analog_event *ev, size_t timeout) +{ + struct dahdi_pvt *p = pvt; + struct pollfd poller; + char *name, *num; + int index = SUB_REAL; + int res; + unsigned char buf[256]; + int flags; + + poller.fd = p->subs[SUB_REAL].dfd; + poller.events = POLLPRI | POLLIN; + poller.revents = 0; + + res = poll(&poller, 1, timeout); + + if (poller.revents & POLLPRI) { + *ev = dahdievent_to_analogevent(dahdi_get_event(p->subs[SUB_REAL].dfd)); + return 1; + } + + if (poller.revents & POLLIN) { + /*** NOTES ***/ + /* Change API: remove cid_signalling from get_callerid, add a new start_cid_detect and stop_cid_detect function + * to enable slin mode and allocate cid detector. get_callerid should be able to be called any number of times until + * either a timeout occurss or CID is detected (returns 0). returning 1 should be event received, and -1 should be fail + * and die */ + res = read(p->subs[index].dfd, buf, sizeof(buf)); + if (res < 0) { + if (errno != ELAST) { + ast_log(LOG_WARNING, "read returned error: %s\n", strerror(errno)); + callerid_free(p->cs); + return -1; + } + } + res = callerid_feed(p->cs, buf, res, AST_LAW(p)); + if (res < 0) { + ast_log(LOG_WARNING, "CallerID feed failed: %s\n", strerror(errno)); + return -1; + } + + if (res == 1) { + callerid_get(p->cs, &name, &num, &flags); + if (name) + ast_copy_string(namebuf, name, ANALOG_MAX_CID); + if (num) + ast_copy_string(numbuf, num, ANALOG_MAX_CID); + + ast_log(LOG_DEBUG, "CallerID number: %s, name: %s, flags=%d\n", num, name, flags); + return 0; + } + } + + *ev = ANALOG_EVENT_NONE; + return 1; +} + +static int send_callerid(struct dahdi_pvt *p); + +static int my_stop_callwait(void *pvt) +{ + struct dahdi_pvt *p = pvt; + p->callwaitingrepeat = 0; + p->cidcwexpire = 0; + + return 0; +} + +static int save_conference(struct dahdi_pvt *p); + +static int my_callwait(void *pvt) +{ + struct dahdi_pvt *p = pvt; + p->callwaitingrepeat = CALLWAITING_REPEAT_SAMPLES; + if (p->cidspill) { + ast_log(LOG_WARNING, "Spill already exists?!?\n"); + free(p->cidspill); + } + if (!(p->cidspill = ast_malloc(2400 /* SAS */ + 680 /* CAS */ + READ_SIZE * 4))) + return -1; + save_conference(p); + /* Silence */ + memset(p->cidspill, 0x7f, 2400 + 600 + READ_SIZE * 4); + if (!p->callwaitrings && p->callwaitingcallerid) { + ast_gen_cas(p->cidspill, 1, 2400 + 680, AST_LAW(p)); + p->callwaitcas = 1; + p->cidlen = 2400 + 680 + READ_SIZE * 4; + } else { + ast_gen_cas(p->cidspill, 1, 2400, AST_LAW(p)); + p->callwaitcas = 0; + p->cidlen = 2400 + READ_SIZE * 4; + } + p->cidpos = 0; + send_callerid(p); + + return 0; +} + +static int my_send_callerid(void *pvt, int cwcid, struct ast_callerid *cid) +{ + struct dahdi_pvt *p = pvt; + + ast_log(LOG_ERROR, "Starting cid spill\n"); + + if (p->cidspill) { + ast_log(LOG_WARNING, "cidspill already exists??\n"); + free(p->cidspill); + } + + if ((p->cidspill = ast_malloc(MAX_CALLERID_SIZE))) { + if (cwcid == 0) { + p->cidlen = ast_callerid_generate(p->cidspill, cid->cid_name, cid->cid_num, AST_LAW(p)); + } else { + p->callwaitcas = 0; + p->cidcwexpire = 0; + p->cidlen = ast_callerid_callwaiting_generate(p->cidspill, cid->cid_name, cid->cid_num, AST_LAW(p)); + p->cidlen += READ_SIZE * 4; + } + p->cidpos = 0; + send_callerid(p); + } + return 0; +} + +static int my_dsp_reset_and_flush_digits(void *pvt) +{ + struct dahdi_pvt *p = pvt; + if (p->dsp) + ast_dsp_digitreset(p->dsp); + + return 0; +} + +static int my_dsp_set_digitmode(void *pvt, enum analog_dsp_digitmode mode) +{ + struct dahdi_pvt *p = pvt; + + if (p->channel == CHAN_PSEUDO) + ast_log(LOG_ERROR, "You have assumed incorrectly sir!\n"); + + if (mode == ANALOG_DIGITMODE_DTMF) { + /* If we do hardware dtmf, no need for a DSP */ + if (p->hardwaredtmf) { + if (p->dsp) { + ast_dsp_free(p->dsp); + p->dsp = NULL; + } + return 0; + } + + if (!p->dsp) { + p->dsp = ast_dsp_new(); + if (!p->dsp) { + ast_log(LOG_ERROR, "Unable to allocate DSP\n"); + return -1; + } + } + + ast_dsp_set_digitmode(p->dsp, DSP_DIGITMODE_DTMF | p->dtmfrelax); + } else if (mode == ANALOG_DIGITMODE_MF) { + if (!p->dsp) { + p->dsp = ast_dsp_new(); + if (!p->dsp) { + ast_log(LOG_ERROR, "Unable to allocate DSP\n"); + return -1; + } + } + ast_dsp_set_digitmode(p->dsp, DSP_DIGITMODE_MF | p->dtmfrelax); + } + return 0; +} + +static int dahdi_wink(struct dahdi_pvt *p, int index); + +static int my_wink(void *pvt, enum analog_sub sub) +{ + struct dahdi_pvt *p = pvt; + int index = analogsub_to_dahdisub(sub); + if (index != SUB_REAL) { + ast_log(LOG_ERROR, "We used a sub other than SUB_REAL (incorrect assumption sir)\n"); + } + return dahdi_wink(p, index); +} + +static void wakeup_sub(struct dahdi_pvt *p, int a, struct dahdi_pri *pri); + +static int reset_conf(struct dahdi_pvt *p); + +static inline int dahdi_confmute(struct dahdi_pvt *p, int muted); + +static void my_handle_dtmfup(void *pvt, struct ast_channel *ast, enum analog_sub analog_index, struct ast_frame **dest) +{ + struct ast_frame *f = *dest; + struct dahdi_pvt *p = pvt; + int idx = analogsub_to_dahdisub(analog_index); + + ast_debug(1, "DTMF digit: %c on %s\n", f->subclass, ast->name); + + if (f->subclass == 'f') { + /* Fax tone -- Handle and return NULL */ + if ((p->callprogress & CALLPROGRESS_FAX) && !p->faxhandled) { + /* If faxbuffers are configured, use them for the fax transmission */ + if (p->usefaxbuffers && !p->bufferoverrideinuse) { + struct dahdi_bufferinfo bi = { + .txbufpolicy = p->faxbuf_policy, + .bufsize = p->bufsize, + .numbufs = p->faxbuf_no + }; + int res; + + if ((res = ioctl(p->subs[idx].dfd, DAHDI_SET_BUFINFO, &bi)) < 0) { + ast_log(LOG_WARNING, "Channel '%s' unable to set buffer policy, reason: %s\n", ast->name, strerror(errno)); + } else { + p->bufferoverrideinuse = 1; + } + } + p->faxhandled = 1; + if (strcmp(ast->exten, "fax")) { + const char *target_context = S_OR(ast->macrocontext, ast->context); + + /* We need to unlock 'ast' here because ast_exists_extension has the + * potential to start autoservice on the channel. Such action is prone + * to deadlock. + */ + ast_mutex_unlock(&p->lock); + ast_channel_unlock(ast); + if (ast_exists_extension(ast, target_context, "fax", 1, ast->cid.cid_num)) { + ast_channel_lock(ast); + ast_mutex_lock(&p->lock); + ast_verb(3, "Redirecting %s to fax extension\n", ast->name); + /* Save the DID/DNIS when we transfer the fax call to a "fax" extension */ + pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast->exten); + if (ast_async_goto(ast, target_context, "fax", 1)) + ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast->name, target_context); + } else { + ast_channel_lock(ast); + ast_mutex_lock(&p->lock); + ast_log(LOG_NOTICE, "Fax detected, but no fax extension\n"); + } + } else { + ast_debug(1, "Already in a fax extension, not redirecting\n"); + } + } else { + ast_debug(1, "Fax already handled\n"); + } + dahdi_confmute(p, 0); + p->subs[idx].f.frametype = AST_FRAME_NULL; + p->subs[idx].f.subclass = 0; + *dest = &p->subs[idx].f; + } +} + +static void my_lock_private(void *pvt) +{ + struct dahdi_pvt *p = pvt; + + ast_mutex_lock(&p->lock); +} + +static void my_unlock_private(void *pvt) +{ + struct dahdi_pvt *p = pvt; + + ast_mutex_unlock(&p->lock); +} + +static void my_increase_ss_count(void) +{ + ast_mutex_lock(&ss_thread_lock); + ss_thread_count++; + ast_mutex_unlock(&ss_thread_lock); +} + +static void my_decrease_ss_count(void) +{ + ast_mutex_lock(&ss_thread_lock); + ss_thread_count--; + ast_cond_signal(&ss_thread_complete); + ast_mutex_unlock(&ss_thread_lock); +} + +static void my_all_subchannels_hungup(void *pvt) +{ + struct dahdi_pvt *p = pvt; + int res, law; + + p->faxhandled = 0; + p->didtdd = 0; + + if (p->dsp) { + ast_dsp_free(p->dsp); + p->dsp = NULL; + } + + law = DAHDI_LAW_DEFAULT; + res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_SETLAW, &law); + if (res < 0) + ast_log(LOG_WARNING, "Unable to set law on channel %d to default: %s\n", p->channel, strerror(errno)); + + dahdi_setlinear(p->subs[SUB_REAL].dfd, 0); + +#if 1 + { + int i; + p->owner = NULL; + /* Cleanup owners here */ + for (i = 0; i < 3; i++) { + p->subs[i].owner = NULL; + } + } +#endif + + reset_conf(p); + if (num_restart_pending == 0) { + restart_monitor(); + } +} + +static int conf_del(struct dahdi_pvt *p, struct dahdi_subchannel *c, int index); + +static int my_conf_del(void *pvt, enum analog_sub sub) +{ + struct dahdi_pvt *p = pvt; + int x = analogsub_to_dahdisub(sub); + + return conf_del(p, &p->subs[x], x); +} + +static int conf_add(struct dahdi_pvt *p, struct dahdi_subchannel *c, int index, int slavechannel); + +static int my_conf_add(void *pvt, enum analog_sub sub) +{ + struct dahdi_pvt *p = pvt; + int x = analogsub_to_dahdisub(sub); + + return conf_add(p, &p->subs[x], x, 0); +} + +static int isslavenative(struct dahdi_pvt *p, struct dahdi_pvt **out); + +static int my_complete_conference_update(void *pvt, int needconference) +{ + struct dahdi_pvt *p = pvt; + int needconf = needconference; + int x; + int useslavenative; + struct dahdi_pvt *slave = NULL; + + useslavenative = isslavenative(p, &slave); + + /* If we have a slave, add him to our conference now. or DAX + if this is slave native */ + for (x = 0; x < MAX_SLAVES; x++) { + if (p->slaves[x]) { + if (useslavenative) + conf_add(p, &p->slaves[x]->subs[SUB_REAL], SUB_REAL, GET_CHANNEL(p)); + else { + conf_add(p, &p->slaves[x]->subs[SUB_REAL], SUB_REAL, 0); + needconf++; + } + } + } + /* If we're supposed to be in there, do so now */ + if (p->inconference && !p->subs[SUB_REAL].inthreeway) { + if (useslavenative) + conf_add(p, &p->subs[SUB_REAL], SUB_REAL, GET_CHANNEL(slave)); + else { + conf_add(p, &p->subs[SUB_REAL], SUB_REAL, 0); + needconf++; + } + } + /* If we have a master, add ourselves to his conference */ + if (p->master) { + if (isslavenative(p->master, NULL)) { + conf_add(p->master, &p->subs[SUB_REAL], SUB_REAL, GET_CHANNEL(p->master)); + } else { + conf_add(p->master, &p->subs[SUB_REAL], SUB_REAL, 0); + } + } + if (!needconf) { + /* Nobody is left (or should be left) in our conference. + Kill it. */ + p->confno = -1; + } + + return 0; +} + +static int check_for_conference(struct dahdi_pvt *p); + +static int my_check_for_conference(void *pvt) +{ + struct dahdi_pvt *p = pvt; + return check_for_conference(p); +} + +static void my_swap_subchannels(void *pvt, enum analog_sub a, struct ast_channel *ast_a, enum analog_sub b, struct ast_channel *ast_b) +{ + struct dahdi_pvt *p = pvt; + int da, db; + int tchan; + + da = analogsub_to_dahdisub(a); + db = analogsub_to_dahdisub(b); + + tchan = p->subs[da].chan; + + p->subs[da].chan = p->subs[db].chan; + + p->subs[db].chan = tchan; + + if (ast_a) + ast_a->fds[0] = p->subs[da].dfd; + if (ast_b) + ast_b->fds[0] = p->subs[db].dfd; + + p->subs[da].owner = ast_a; + p->subs[db].owner = ast_b; + + wakeup_sub(p, a, NULL); + wakeup_sub(p, b, NULL); + + return; +} + +static struct ast_channel *dahdi_new(struct dahdi_pvt *, int, int, int, int, int); + +static struct ast_channel * my_new_analog_ast_channel(void *pvt, int state, int startpbx, enum analog_sub sub) +{ + struct dahdi_pvt *p = pvt; + int dsub = analogsub_to_dahdisub(sub); + + return dahdi_new(p, state, startpbx, dsub, 0, 0); +} + +static int unalloc_sub(struct dahdi_pvt *p, int x); + +static int my_unallocate_sub(void *pvt, enum analog_sub analogsub) +{ + struct dahdi_pvt *p = pvt; + + return unalloc_sub(p, analogsub_to_dahdisub(analogsub)); +} + +static int alloc_sub(struct dahdi_pvt *p, int x); + +static int my_allocate_sub(void *pvt, enum analog_sub analogsub) +{ + struct dahdi_pvt *p = pvt; + + return alloc_sub(p, analogsub_to_dahdisub(analogsub)); +} + +static int has_voicemail(struct dahdi_pvt *p); + +static int my_has_voicemail(void *pvt) +{ + struct dahdi_pvt *p = pvt; + + return has_voicemail(p); +} + +static int my_play_tone(void *pvt, enum analog_sub sub, enum analog_tone tone) +{ + struct dahdi_pvt *p = pvt; + int index; + + index = analogsub_to_dahdisub(sub); + + return tone_zone_play_tone(p->subs[index].dfd, analog_tone_to_dahditone(tone)); +} + +static enum analog_event dahdievent_to_analogevent(int event) +{ + enum analog_event res = ANALOG_EVENT_ERROR; + + switch (event) { + case DAHDI_EVENT_DIALCOMPLETE: + res = ANALOG_EVENT_DIALCOMPLETE; + break; + case DAHDI_EVENT_WINKFLASH: + res = ANALOG_EVENT_WINKFLASH; + break; + case DAHDI_EVENT_ONHOOK: + res = ANALOG_EVENT_ONHOOK; + break; + case DAHDI_EVENT_RINGOFFHOOK: + res = ANALOG_EVENT_RINGOFFHOOK; + break; + case DAHDI_EVENT_ALARM: + res = ANALOG_EVENT_ALARM; + break; + case DAHDI_EVENT_NOALARM: + res = ANALOG_EVENT_NOALARM; + break; + case DAHDI_EVENT_HOOKCOMPLETE: + res = ANALOG_EVENT_HOOKCOMPLETE; + break; + case DAHDI_EVENT_POLARITY: + res = ANALOG_EVENT_POLARITY; + break; + case DAHDI_EVENT_RINGERON: + res = ANALOG_EVENT_RINGERON; + break; + case DAHDI_EVENT_RINGEROFF: + res = ANALOG_EVENT_RINGEROFF; + break; + case DAHDI_EVENT_RINGBEGIN: + res = ANALOG_EVENT_RINGBEGIN; + break; + case DAHDI_EVENT_PULSE_START: + res = ANALOG_EVENT_PULSE_START; + break; + case DAHDI_EVENT_NEONMWI_ACTIVE: + res = ANALOG_EVENT_NEONMWI_ACTIVE; + break; + case DAHDI_EVENT_NEONMWI_INACTIVE: + res = ANALOG_EVENT_NEONMWI_INACTIVE; + break; + } + + return res; +} + +static inline int dahdi_wait_event(int fd); + +static int my_wait_event(void *pvt) +{ + struct dahdi_pvt *p = pvt; + + return dahdi_wait_event(p->subs[SUB_REAL].dfd); +} + +static int my_get_event(void *pvt) +{ + struct dahdi_pvt *p = pvt; + int res; + + if (p->fake_event) { + res = p->fake_event; + p->fake_event = 0; + } else + res = dahdi_get_event(p->subs[SUB_REAL].dfd); + + return dahdievent_to_analogevent(res); +} + +static int my_is_off_hook(void *pvt) +{ + struct dahdi_pvt *p = pvt; + int res; + struct dahdi_params par; + + if (p->subs[SUB_REAL].dfd > -1) + res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &par); + else { + /* Assume not off hook on CVRS */ + res = 0; + par.rxisoffhook = 0; + } + if (res) { + ast_log(LOG_WARNING, "Unable to check hook state on channel %d: %s\n", p->channel, strerror(errno)); + } + + return (par.rxbits > -1) || par.rxisoffhook; +} + +static void dahdi_enable_ec(struct dahdi_pvt *p); +static void dahdi_disable_ec(struct dahdi_pvt *p); + +static int my_set_echocanceller(void *pvt, int enable) +{ + struct dahdi_pvt *p = pvt; + + if (enable) + dahdi_enable_ec(p); + else + dahdi_disable_ec(p); + + return 0; +} + +static int dahdi_ring_phone(struct dahdi_pvt *p); + +static int my_ring(void *pvt) +{ + struct dahdi_pvt *p = pvt; + + return dahdi_ring_phone(p); +} + +static inline int dahdi_set_hook(int fd, int hs); + +static int my_off_hook(void *pvt) +{ + struct dahdi_pvt *p = pvt; + return dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_OFFHOOK); +} + +static int my_start(void *pvt) +{ + struct dahdi_pvt *p = pvt; + int x = DAHDI_START; + + return ioctl(p->subs[SUB_REAL].dfd, DAHDI_HOOK, &x); +} + +static int my_dial_digits(void *pvt, enum analog_sub sub, struct analog_dialoperation *dop) +{ + int index = analogsub_to_dahdisub(sub); + int res; + struct dahdi_pvt *p = pvt; + struct dahdi_dialoperation ddop; + + if (dop->op != ANALOG_DIAL_OP_REPLACE) { + ast_log(LOG_ERROR, "Fix the dial_digits callback!\n"); + return -1; + } + + if (sub != ANALOG_SUB_REAL) + printf("Trying to dial digits on sub %d\n", sub); + + ddop.op = DAHDI_DIAL_OP_REPLACE; + strncpy(ddop.dialstr, dop->dialstr, sizeof(ddop.dialstr)); + + printf("Dialing %s on %d\n", ddop.dialstr, p->channel); + + res = ioctl(p->subs[index].dfd, DAHDI_DIAL, &ddop); + + if (res == -1) + ast_log(LOG_DEBUG, "DAHDI_DIAL ioctl failed on %s: %s\n", p->owner->name, strerror(errno)); + + return res; +} + +static void dahdi_train_ec(struct dahdi_pvt *p); + +static int my_train_echocanceller(void *pvt) +{ + struct dahdi_pvt *p = pvt; + + dahdi_train_ec(p); + + return 0; +} + +static int my_is_dialing(void *pvt, enum analog_sub sub) +{ + struct dahdi_pvt *p = pvt; + int index; + int x; + + index = analogsub_to_dahdisub(sub); + + if (ioctl(p->subs[index].dfd, DAHDI_DIALING, &x)) { + ast_log(LOG_DEBUG, "DAHDI_DIALING ioctl failed!\n"); + return -1; + } + + return x; +} + +static int my_on_hook(void *pvt) +{ + struct dahdi_pvt *p = pvt; + int x = DAHDI_ONHOOK; + + return ioctl(p->subs[ANALOG_SUB_REAL].dfd, DAHDI_HOOK, &x); +} + +/*! + * \brief Send MWI state change + * + * \arg mailbox_full This is the mailbox associated with the FXO line that the + * MWI state has changed on. + * \arg thereornot This argument should simply be set to 1 or 0, to indicate + * whether there are messages waiting or not. + * + * \return nothing + * + * This function does two things: + * + * 1) It generates an internal Asterisk event notifying any other module that + * cares about MWI that the state of a mailbox has changed. + * + * 2) It runs the script specified by the mwimonitornotify option to allow + * some custom handling of the state change. + */ +static void notify_message(char *mailbox_full, int thereornot) +{ + char s[sizeof(mwimonitornotify) + 80]; + struct ast_event *event; + char *mailbox, *context; + + /* Strip off @default */ + context = mailbox = ast_strdupa(mailbox_full); + strsep(&context, "@"); + if (ast_strlen_zero(context)) + context = "default"; + + if (!(event = ast_event_new(AST_EVENT_MWI, + AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox, + AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context, + AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, thereornot, + AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, thereornot, + AST_EVENT_IE_END))) { + return; + } + + ast_event_queue_and_cache(event); + + if (!ast_strlen_zero(mailbox) && !ast_strlen_zero(mwimonitornotify)) { + snprintf(s, sizeof(s), "%s %s %d", mwimonitornotify, mailbox, thereornot); + ast_safe_system(s); + } +} + +static void my_handle_notify_message(struct ast_channel *chan, void *pvt, int cid_flags, int neon_mwievent) +{ + struct dahdi_pvt *p = pvt; + + if (neon_mwievent > -1 && !p->mwimonitor_neon) + return; + + if (neon_mwievent == ANALOG_EVENT_NEONMWI_ACTIVE || cid_flags & CID_MSGWAITING) { + ast_log(LOG_NOTICE, "MWI: Channel %d message waiting, mailbox %s\n", p->channel, p->mailbox); + notify_message(p->mailbox, 1); + } else if (neon_mwievent == ANALOG_EVENT_NEONMWI_INACTIVE || cid_flags & CID_NOMSGWAITING) { + ast_log(LOG_NOTICE, "MWI: Channel %d no message waiting, mailbox %s\n", p->channel, p->mailbox); + notify_message(p->mailbox, 0); + } + /* If the CID had Message waiting payload, assume that this for MWI only and hangup the call */ + /* If generated using Ring Pulse Alert, then ring has been answered as a call and needs to be hungup */ + if (neon_mwievent == -1 && p->mwimonitor_rpas) { + ast_hangup(chan); + return; + } +} + + +static const char *event2str(int event); + +static struct analog_callback dahdi_analog_callbacks = +{ + .play_tone = my_play_tone, + .get_event = my_get_event, + .wait_event = my_wait_event, + .is_off_hook = my_is_off_hook, + .set_echocanceller = my_set_echocanceller, + .ring = my_ring, + .off_hook = my_off_hook, + .dial_digits = my_dial_digits, + .train_echocanceller = my_train_echocanceller, + .on_hook = my_on_hook, + .is_dialing = my_is_dialing, + .allocate_sub = my_allocate_sub, + .unallocate_sub = my_unallocate_sub, + .swap_subs = my_swap_subchannels, + .has_voicemail = my_has_voicemail, + .check_for_conference = my_check_for_conference, + .conf_add = my_conf_add, + .conf_del = my_conf_del, + .complete_conference_update = my_complete_conference_update, + .start = my_start, + .all_subchannels_hungup = my_all_subchannels_hungup, + .lock_private = my_lock_private, + .unlock_private = my_unlock_private, + .handle_dtmfup = my_handle_dtmfup, + .wink = my_wink, + .new_ast_channel = my_new_analog_ast_channel, + .dsp_set_digitmode = my_dsp_set_digitmode, + .dsp_reset_and_flush_digits = my_dsp_reset_and_flush_digits, + .send_callerid = my_send_callerid, + .callwait = my_callwait, + .stop_callwait = my_stop_callwait, + .get_callerid = my_get_callerid, + .start_cid_detect = my_start_cid_detect, + .stop_cid_detect = my_stop_cid_detect, + .handle_notify_message = my_handle_notify_message, + .increase_ss_count = my_increase_ss_count, + .decrease_ss_count = my_decrease_ss_count, +}; + +static struct dahdi_pvt *round_robin[32]; + #if defined(HAVE_PRI) static inline int pri_grab(struct dahdi_pvt *pvt, struct dahdi_pri *pri) { @@ -2339,7 +3365,7 @@ return res; } -static char *events[] = { +static const char * const events[] = { "No event", "On hook", "Ring/Answered", @@ -2384,7 +3410,7 @@ return alm ? "Unknown Alarm" : "No Alarm"; } -static char *event2str(int event) +static const char *event2str(int event) { static char buf[256]; if ((event < (ARRAY_LEN(events))) && (event > -1)) @@ -2471,6 +3497,45 @@ } } +int analog_lib_handles(int signalling, int radio, int oprmode) +{ + switch (signalling) { + case SIG_FXOLS: + case SIG_FXOGS: + case SIG_FXOKS: + case SIG_FXSLS: + case SIG_FXSGS: + case SIG_FXSKS: + case SIG_EMWINK: + case SIG_EM: + case SIG_EM_E1: + case SIG_FEATD: + case SIG_FEATDMF: + case SIG_E911: + case SIG_FGC_CAMA: + case SIG_FGC_CAMAMF: + case SIG_FEATB: + case SIG_SFWINK: + case SIG_SF: + case SIG_SF_FEATD: + case SIG_SF_FEATDMF: + case SIG_FEATDMF_TA: + case SIG_SF_FEATB: + break; + default: + /* The rest of the function should cover the remainder of signalling types */ + return 0; + } + + if (radio) + return 0; + + if (oprmode) + return 0; + + return 1; +} + #define sig2str dahdi_sig2str static int conf_add(struct dahdi_pvt *p, struct dahdi_subchannel *c, int idx, int slavechannel) @@ -2917,53 +3982,6 @@ return 0; } -/*! - * \brief Send MWI state change - * - * \arg mailbox_full This is the mailbox associated with the FXO line that the - * MWI state has changed on. - * \arg thereornot This argument should simply be set to 1 or 0, to indicate - * whether there are messages waiting or not. - * - * \return nothing - * - * This function does two things: - * - * 1) It generates an internal Asterisk event notifying any other module that - * cares about MWI that the state of a mailbox has changed. - * - * 2) It runs the script specified by the mwimonitornotify option to allow - * some custom handling of the state change. - */ -static void notify_message(char *mailbox_full, int thereornot) -{ - char s[sizeof(mwimonitornotify) + 80]; - struct ast_event *event; - char *mailbox, *context; - - /* Strip off @default */ - context = mailbox = ast_strdupa(mailbox_full); - strsep(&context, "@"); - if (ast_strlen_zero(context)) - context = "default"; - - if (!(event = ast_event_new(AST_EVENT_MWI, - AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox, - AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context, - AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, thereornot, - AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, thereornot, - AST_EVENT_IE_END))) { - return; - } - - ast_event_queue_and_cache(event); - - if (!ast_strlen_zero(mailbox) && !ast_strlen_zero(mwimonitornotify)) { - snprintf(s, sizeof(s), "%s %s %d", mwimonitornotify, mailbox, thereornot); - ast_safe_system(s); - } -} - static int restore_conference(struct dahdi_pvt *p) { int res; @@ -3021,6 +4039,8 @@ return new_msgs; } + + static int send_callerid(struct dahdi_pvt *p) { /* Assumes spill in p->cidspill, p->cidlen in length and we're p->cidpos into it */ @@ -3135,6 +4155,14 @@ set_actual_gain(p->subs[SUB_REAL].dfd, 0, p->rxgain, p->txgain, p->law); + /* If this is analog signalling we can exit here */ + if (analog_lib_handles(p->sig, p->radio, p->oprmode)) { + p->callwaitrings = 0; + res = analog_call(p->sig_pvt, ast, rdest, timeout); + ast_mutex_unlock(&p->lock); + return res; + } + mysig = p->sig; if (p->outsigmod > -1) mysig = p->outsigmod; @@ -3156,7 +4184,7 @@ } p->callwaitcas = 0; if ((p->cidspill = ast_malloc(MAX_CALLERID_SIZE))) { - p->cidlen = ast_callerid_generate(p->cidspill, ast->cid.cid_name, ast->cid.cid_num, AST_LAW(p)); + p->cidlen = ast_callerid_generate(p->cidspill, ast->connected.id.name, ast->connected.id.number, AST_LAW(p)); p->cidpos = 0; send_callerid(p); } @@ -3197,12 +4225,12 @@ } else { /* Call waiting call */ p->callwaitrings = 0; - if (ast->cid.cid_num) - ast_copy_string(p->callwait_num, ast->cid.cid_num, sizeof(p->callwait_num)); + if (ast->connected.id.number) + ast_copy_string(p->callwait_num, ast->connected.id.number, sizeof(p->callwait_num)); else p->callwait_num[0] = '\0'; - if (ast->cid.cid_name) - ast_copy_string(p->callwait_name, ast->cid.cid_name, sizeof(p->callwait_name)); + if (ast->connected.id.name) + ast_copy_string(p->callwait_name, ast->connected.id.name, sizeof(p->callwait_name)); else p->callwait_name[0] = '\0'; /* Call waiting tone instead */ @@ -3214,8 +4242,8 @@ if (tone_zone_play_tone(p->subs[SUB_CALLWAIT].dfd, DAHDI_TONE_RINGTONE)) ast_log(LOG_WARNING, "Unable to generate call-wait ring-back on channel %s\n", ast->name); } - n = ast->cid.cid_name; - l = ast->cid.cid_num; + n = ast->connected.id.name; + l = ast->connected.id.number; if (l) ast_copy_string(p->lastcid_num, l, sizeof(p->lastcid_num)); else @@ -3281,14 +4309,14 @@ switch (mysig) { case SIG_FEATD: - l = ast->cid.cid_num; + l = ast->connected.id.number; if (l) snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T*%s*%s*", l, c); else snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T**%s*", c); break; case SIG_FEATDMF: - l = ast->cid.cid_num; + l = ast->connected.id.number; if (l) snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*00%s#*%s#", l, c); else @@ -3423,7 +4451,7 @@ } if (!p->hidecallerid) { - l = ast->cid.cid_num; + l = ast->connected.id.number; } else { l = NULL; } @@ -3472,10 +4500,10 @@ } } isup_set_calling(p->ss7call, l ? (l + calling_nai_strip) : NULL, ss7_calling_nai, - p->use_callingpres ? cid_pres2ss7pres(ast->cid.cid_pres) : (l ? SS7_PRESENTATION_ALLOWED : SS7_PRESENTATION_RESTRICTED), - p->use_callingpres ? cid_pres2ss7screen(ast->cid.cid_pres) : SS7_SCREENING_USER_PROVIDED ); + p->use_callingpres ? cid_pres2ss7pres(ast->connected.id.number_presentation) : (l ? SS7_PRESENTATION_ALLOWED : SS7_PRESENTATION_RESTRICTED), + p->use_callingpres ? cid_pres2ss7screen(ast->connected.id.number_presentation) : SS7_SCREENING_USER_PROVIDED ); - isup_set_oli(p->ss7call, ast->cid.cid_ani2); + isup_set_oli(p->ss7call, ast->connected.ani2); isup_init_call(p->ss7->ss7, p->ss7call, p->cic, p->dpc); ast_channel_lock(ast); @@ -3587,9 +4615,9 @@ l = NULL; n = NULL; if (!p->hidecallerid) { - l = ast->cid.cid_num; + l = ast->connected.id.number; if (!p->hidecalleridname) { - n = ast->cid.cid_name; + n = ast->connected.id.name; } } @@ -3803,7 +4831,7 @@ } } pri_sr_set_caller(sr, l ? (l + ldp_strip) : NULL, n, prilocaldialplan, - p->use_callingpres ? ast->cid.cid_pres : (l ? PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN : PRES_NUMBER_NOT_AVAILABLE)); + p->use_callingpres ? ast->connected.id.number_presentation : (l ? PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN : PRES_NUMBER_NOT_AVAILABLE)); if ((rr_str = pbx_builtin_getvar_helper(ast, "PRIREDIRECTREASON"))) { if (!strcasecmp(rr_str, "UNKNOWN")) redirect_reason = 0; @@ -3934,11 +4962,25 @@ pl = p; p = p->next; x = pl->channel; +#ifdef HAVE_PRI_SERVICE_MESSAGES + { + char db_chan_name[20], db_answer[5], state; + int why = -1; + + snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, pl->span, x); + if (!ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) { + sscanf(db_answer, "%c:%d", &state, &why); + } + if (!why) { + /* SRVST persistence is not required */ + ast_db_del(db_chan_name, SRVST_DBKEY); + } + } +#endif /* Free associated memory */ if (pl) destroy_dahdi_pvt(&pl); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_2 "Unregistered channel %d\n", x); + ast_verb(3, "Unregistered channel %d\n", x); } iflist = NULL; ifcount = 0; @@ -3948,11 +4990,11 @@ #if defined(HAVE_PRI) static char *dahdi_send_keypad_facility_app = "DAHDISendKeypadFacility"; -static int dahdi_send_keypad_facility_exec(struct ast_channel *chan, void *data) +static int dahdi_send_keypad_facility_exec(struct ast_channel *chan, const char *data) { /* Data will be our digit string */ struct dahdi_pvt *p; - char *digits = (char *) data; + const char *digits = data; if (ast_strlen_zero(digits)) { ast_debug(1, "No digit string sent to application!\n"); @@ -3993,7 +5035,7 @@ #if defined(HAVE_PRI_PROG_W_CAUSE) static char *dahdi_send_callrerouting_facility_app = "DAHDISendCallreroutingFacility"; -static int dahdi_send_callrerouting_facility_exec(struct ast_channel *chan, void *data) +static int dahdi_send_callrerouting_facility_exec(struct ast_channel *chan, const char *data) { /* Data will be our digit string */ struct dahdi_pvt *p; @@ -4157,9 +5199,9 @@ #endif /* defined(HAVE_PRI) */ #if defined(HAVE_OPENR2) -static const char *dahdi_accept_r2_call_app = "DAHDIAcceptR2Call"; +static const char * const dahdi_accept_r2_call_app = "DAHDIAcceptR2Call"; -static int dahdi_accept_r2_call_exec(struct ast_channel *chan, void *data) +static int dahdi_accept_r2_call_exec(struct ast_channel *chan, const char *data) { /* data is whether to accept with charge or no charge */ openr2_call_mode_t accept_mode; @@ -4293,8 +5335,8 @@ r2cause = OR2_CAUSE_NORMAL_CLEARING; break; } - ast_log(LOG_DEBUG, "dahdi_ast_cause_to_r2_cause returned %d/%s for ast cause %d\n", - r2cause, openr2_proto_get_disconnect_string(r2cause), cause); + ast_log(LOG_DEBUG, "ast cause %d resulted in openr2 cause %d/%s\n", + cause, r2cause, openr2_proto_get_disconnect_string(r2cause)); return r2cause; } #endif @@ -4315,6 +5357,34 @@ return 0; } + if (analog_lib_handles(p->sig, p->radio, p->oprmode)) { + ast_mutex_lock(&p->lock); + + dahdi_confmute(p, 0); + restore_gains(p); + p->ignoredtmf = 0; + + if (p->bufferoverrideinuse) { + /* faxbuffers are in use, revert them */ + struct dahdi_bufferinfo bi = { + .txbufpolicy = p->buf_policy, + .rxbufpolicy = p->buf_policy, + .bufsize = p->bufsize, + .numbufs = p->buf_no + }; + int bpres; + + if ((bpres = ioctl(p->subs[SUB_REAL].dfd, DAHDI_SET_BUFINFO, &bi)) < 0) { + ast_log(LOG_WARNING, "Channel '%s' unable to revert buffer policy: %s\n", ast->name, strerror(errno)); + } + p->bufferoverrideinuse = 0; + } + + res = analog_hangup(p->sig_pvt, ast); + + goto hangup_out; + } + ast_mutex_lock(&p->lock); idx = dahdi_get_index(ast, p, 1); @@ -4470,7 +5540,7 @@ p->dsp = NULL; } - if (p->faxbuffersinuse) { + if (p->bufferoverrideinuse) { /* faxbuffers are in use, revert them */ struct dahdi_bufferinfo bi = { .txbufpolicy = p->buf_policy, @@ -4481,9 +5551,9 @@ int bpres; if ((bpres = ioctl(p->subs[SUB_REAL].dfd, DAHDI_SET_BUFINFO, &bi)) < 0) { - ast_log(LOG_WARNING, "Channel '%s' unable to revert faxbuffer policy: %s\n", ast->name, strerror(errno)); + ast_log(LOG_WARNING, "Channel '%s' unable to revert buffer policy: %s\n", ast->name, strerror(errno)); } - p->faxbuffersinuse = 0; + p->bufferoverrideinuse = 0; } law = DAHDI_LAW_DEFAULT; @@ -4623,15 +5693,12 @@ default: tone_zone_play_tone(p->subs[SUB_REAL].dfd, -1); } - if (p->cidspill) - ast_free(p->cidspill); if (p->sig) dahdi_disable_ec(p); x = 0; ast_channel_setoption(ast,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0); ast_channel_setoption(ast,AST_OPTION_TDD,&x,sizeof(char),0); p->didtdd = 0; - p->cidspill = NULL; p->callwaitcas = 0; p->callwaiting = p->permcallwaiting; p->hidecallerid = p->permhidecallerid; @@ -4667,6 +5734,11 @@ p->cidcwexpire = 0; p->oprmode = 0; ast->tech_pvt = NULL; +hangup_out: + if (p->cidspill) + ast_free(p->cidspill); + p->cidspill = NULL; + ast_mutex_unlock(&p->lock); ast_module_unref(ast_module_info->self); ast_verb(3, "Hungup '%s'\n", ast->name); @@ -4710,6 +5782,13 @@ ast_mutex_unlock(&p->lock); return 0; } + + if (analog_lib_handles(p->sig, p->radio, p->oprmode)) { + res = analog_answer(p->sig_pvt, ast); + ast_mutex_unlock(&p->lock); + return res; + } + switch (p->sig) { case SIG_FXSLS: case SIG_FXSGS: @@ -4813,6 +5892,66 @@ return res; } +static void disable_dtmf_detect(struct dahdi_pvt *p) +{ + int val = 0; + + p->ignoredtmf = 1; + + ioctl(p->subs[SUB_REAL].dfd, DAHDI_TONEDETECT, &val); + + if (!p->hardwaredtmf && p->dsp) { + p->dsp_features &= ~DSP_FEATURE_DIGIT_DETECT; + ast_dsp_set_features(p->dsp, p->dsp_features); + } +} + +static void enable_dtmf_detect(struct dahdi_pvt *p) +{ + int val = DAHDI_TONEDETECT_ON | DAHDI_TONEDETECT_MUTE; + + if (p->channel == CHAN_PSEUDO) + return; + + p->ignoredtmf = 0; + + ioctl(p->subs[SUB_REAL].dfd, DAHDI_TONEDETECT, &val); + + if (!p->hardwaredtmf && p->dsp) { + p->dsp_features |= DSP_FEATURE_DIGIT_DETECT; + ast_dsp_set_features(p->dsp, p->dsp_features); + } +} + +static int dahdi_queryoption(struct ast_channel *chan, int option, void *data, int *datalen) +{ + char *cp; + struct dahdi_pvt *p = chan->tech_pvt; + + /* all supported options require data */ + if (!data || (*datalen < 1)) { + errno = EINVAL; + return -1; + } + + switch (option) { + case AST_OPTION_DIGIT_DETECT: + cp = (char *) data; + *cp = p->ignoredtmf ? 0 : 1; + ast_debug(1, "Reporting digit detection %sabled on %s\n", *cp ? "en" : "dis", chan->name); + break; + case AST_OPTION_FAX_DETECT: + cp = (char *) data; + *cp = (p->callprogress & CALLPROGRESS_FAX) ? 0 : 1; + ast_debug(1, "Reporting fax tone detection %sabled on %s\n", *cp ? "en" : "dis", chan->name); + break; + } + + errno = 0; + + return 0; +} + static int dahdi_setoption(struct ast_channel *chan, int option, void *data, int datalen) { char *cp; @@ -4995,6 +6134,31 @@ dahdi_disable_ec(p); } break; + case AST_OPTION_DIGIT_DETECT: + cp = (char *) data; + ast_debug(1, "%sabling digit detection on %s\n", *cp ? "En" : "Dis", chan->name); + if (*cp) { + enable_dtmf_detect(p); + } else { + disable_dtmf_detect(p); + } + break; + case AST_OPTION_FAX_DETECT: + cp = (char *) data; + if (p->dsp) { + ast_debug(1, "%sabling fax tone detection on %s\n", *cp ? "En" : "Dis", chan->name); + if (*cp) { + p->callprogress |= CALLPROGRESS_FAX; + p->dsp_features |= DSP_FEATURE_FAX_DETECT; + } else { + p->callprogress &= ~CALLPROGRESS_FAX; + p->dsp_features &= ~DSP_FEATURE_FAX_DETECT; + } + ast_dsp_set_features(p->dsp, p->dsp_features); + } + break; + default: + return -1; } errno = 0; @@ -5004,7 +6168,8 @@ static int dahdi_func_read(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len) { struct dahdi_pvt *p = chan->tech_pvt; - + int res = 0; + if (!strcasecmp(data, "rxgain")) { ast_mutex_lock(&p->lock); snprintf(buf, len, "%f", p->rxgain); @@ -5015,11 +6180,111 @@ ast_mutex_unlock(&p->lock); } else { ast_copy_string(buf, "", len); + res = -1; } + + return res; +} + + +static int parse_buffers_policy(const char *parse, int *num_buffers, int *policy) +{ + int res; + char policy_str[21] = ""; + + if ((res = sscanf(parse, "%d,%20s", num_buffers, policy_str)) != 2) { + ast_log(LOG_WARNING, "Parsing buffer string '%s' failed.\n", parse); + return 1; + } + if (*num_buffers < 0) { + ast_log(LOG_WARNING, "Invalid buffer count given '%d'.\n", *num_buffers); + return -1; + } + if (!strcasecmp(policy_str, "full")) { + *policy = DAHDI_POLICY_WHEN_FULL; + } else if (!strcasecmp(policy_str, "immediate")) { + *policy = DAHDI_POLICY_IMMEDIATE; +#if defined(HAVE_DAHDI_HALF_FULL) + } else if (!strcasecmp(policy_str, "half")) { + *policy = DAHDI_POLICY_HALF_FULL; +#endif + } else { + ast_log(LOG_WARNING, "Invalid policy name given '%s'.\n", policy_str); + return -1; + } + return 0; } +static int dahdi_func_write(struct ast_channel *chan, const char *function, char *data, const char *value) +{ + struct dahdi_pvt *p = chan->tech_pvt; + int res = 0; + + if (!strcasecmp(data, "buffers")) { + int num_bufs, policy; + if (!(parse_buffers_policy(value, &num_bufs, &policy))) { + struct dahdi_bufferinfo bi = { + .txbufpolicy = policy, + .rxbufpolicy = policy, + .bufsize = p->bufsize, + .numbufs = num_bufs, + }; + int bpres; + + if ((bpres = ioctl(p->subs[SUB_REAL].dfd, DAHDI_SET_BUFINFO, &bi)) < 0) { + ast_log(LOG_WARNING, "Channel '%d' unable to override buffer policy: %s\n", p->channel, strerror(errno)); + } else { + p->bufferoverrideinuse = 1; + } + } else { + res = -1; + } + } else if (!strcasecmp(data, "echocan_mode")) { + if (!strcasecmp(value, "on")) { + ast_mutex_lock(&p->lock); + dahdi_enable_ec(p); + ast_mutex_unlock(&p->lock); + } else if (!strcasecmp(value, "off")) { + ast_mutex_lock(&p->lock); + dahdi_disable_ec(p); + ast_mutex_unlock(&p->lock); +#ifdef HAVE_DAHDI_ECHOCANCEL_FAX_MODE + } else if (!strcasecmp(value, "fax")) { + int blah = 1; + + ast_mutex_lock(&p->lock); + if (!p->echocanon) { + dahdi_enable_ec(p); + } + if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_ECHOCANCEL_FAX_MODE, &blah)) { + ast_log(LOG_WARNING, "Unable to place echocan into fax mode on channel %d: %s\n", p->channel, strerror(errno)); + } + ast_mutex_unlock(&p->lock); + } else if (!strcasecmp(value, "voice")) { + int blah = 0; + + ast_mutex_lock(&p->lock); + if (!p->echocanon) { + dahdi_enable_ec(p); + } + if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_ECHOCANCEL_FAX_MODE, &blah)) { + ast_log(LOG_WARNING, "Unable to place echocan into voice mode on channel %d: %s\n", p->channel, strerror(errno)); + } + ast_mutex_unlock(&p->lock); +#endif + } else { + ast_log(LOG_WARNING, "Unsupported value '%s' provided for '%s' item.\n", value, data); + res = -1; + } + } else { + res = -1; + } + + return res; +} + static void dahdi_unlink(struct dahdi_pvt *slave, struct dahdi_pvt *master, int needlock) { /* Unlink a specific slave or all slaves/masters from a given master */ @@ -5099,39 +6364,6 @@ ast_debug(1, "Making %d slave to master %d at %d\n", slave->channel, master->channel, x); } -static void disable_dtmf_detect(struct dahdi_pvt *p) -{ - int val; - - p->ignoredtmf = 1; - - val = 0; - ioctl(p->subs[SUB_REAL].dfd, DAHDI_TONEDETECT, &val); - - if (!p->hardwaredtmf && p->dsp) { - p->dsp_features &= ~DSP_FEATURE_DIGIT_DETECT; - ast_dsp_set_features(p->dsp, p->dsp_features); - } -} - -static void enable_dtmf_detect(struct dahdi_pvt *p) -{ - int val; - - if (p->channel == CHAN_PSEUDO) - return; - - p->ignoredtmf = 0; - - val = DAHDI_TONEDETECT_ON | DAHDI_TONEDETECT_MUTE; - ioctl(p->subs[SUB_REAL].dfd, DAHDI_TONEDETECT, &val); - - if (!p->hardwaredtmf && p->dsp) { - p->dsp_features |= DSP_FEATURE_DIGIT_DETECT; - ast_dsp_set_features(p->dsp, p->dsp_features); - } -} - static enum ast_bridge_result dahdi_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms) { struct ast_channel *who; @@ -5441,6 +6673,11 @@ } if (newchan->_state == AST_STATE_RINGING) dahdi_indicate(newchan, AST_CONTROL_RINGING, NULL, 0); + + if (analog_lib_handles(p->sig, p->radio, p->oprmode)) { + analog_fixup(oldchan, newchan, p->sig_pvt); + } + update_conf(p); ast_mutex_unlock(&p->lock); return 0; @@ -5476,7 +6713,7 @@ return res; } -static void *ss_thread(void *data); +static void *analog_ss_thread(void *data); static int attempt_transfer(struct dahdi_pvt *p) { @@ -5614,7 +6851,7 @@ /* Fax tone -- Handle and return NULL */ if ((p->callprogress & CALLPROGRESS_FAX) && !p->faxhandled) { /* If faxbuffers are configured, use them for the fax transmission */ - if (p->usefaxbuffers && !p->faxbuffersinuse) { + if (p->usefaxbuffers && !p->bufferoverrideinuse) { struct dahdi_bufferinfo bi = { .txbufpolicy = p->faxbuf_policy, .bufsize = p->bufsize, @@ -5623,12 +6860,16 @@ int res; if ((res = ioctl(p->subs[idx].dfd, DAHDI_SET_BUFINFO, &bi)) < 0) { - ast_log(LOG_WARNING, "Channel '%s' unable to set faxbuffer policy, reason: %s\n", ast->name, strerror(errno)); + ast_log(LOG_WARNING, "Channel '%s' unable to set buffer policy, reason: %s\n", ast->name, strerror(errno)); } else { - p->faxbuffersinuse = 1; + p->bufferoverrideinuse = 1; } } p->faxhandled = 1; + p->callprogress &= ~CALLPROGRESS_FAX; + p->dsp_features &= ~DSP_FEATURE_FAX_DETECT; + ast_dsp_set_features(p->dsp, p->dsp_features); + ast_debug(1, "Disabling FAX tone detection on %s after tone received\n", ast->name); if (strcmp(ast->exten, "fax")) { const char *target_context = S_OR(ast->macrocontext, ast->context); @@ -5736,806 +6977,818 @@ } switch (res) { - case DAHDI_EVENT_EC_DISABLED: - ast_verb(3, "Channel %d echo canceler disabled due to CED detection\n", p->channel); - p->echocanon = 0; - break; - case DAHDI_EVENT_BITSCHANGED: + case DAHDI_EVENT_EC_DISABLED: + ast_verb(3, "Channel %d echo canceler disabled.\n", p->channel); + p->echocanon = 0; + break; +#ifdef HAVE_DAHDI_ECHOCANCEL_FAX_MODE + case DAHDI_EVENT_TX_CED_DETECTED: + ast_verb(3, "Channel %d detected a CED tone towards the network.\n", p->channel); + break; + case DAHDI_EVENT_RX_CED_DETECTED: + ast_verb(3, "Channel %d detected a CED tone from the network.\n", p->channel); + break; + case DAHDI_EVENT_EC_NLP_DISABLED: + ast_verb(3, "Channel %d echo canceler disabled its NLP.\n", p->channel); + break; + case DAHDI_EVENT_EC_NLP_ENABLED: + ast_verb(3, "Channel %d echo canceler enabled its NLP.\n", p->channel); + break; +#endif + case DAHDI_EVENT_BITSCHANGED: #ifdef HAVE_OPENR2 - if (p->sig != SIG_MFCR2) { - ast_log(LOG_WARNING, "Recieved bits changed on %s signalling?\n", sig2str(p->sig)); - } else { - ast_log(LOG_DEBUG, "bits changed in chan %d\n", p->channel); - openr2_chan_handle_cas(p->r2chan); - } + if (p->sig != SIG_MFCR2) { + ast_log(LOG_WARNING, "Recieved bits changed on %s signalling?\n", sig2str(p->sig)); + } else { + ast_log(LOG_DEBUG, "bits changed in chan %d\n", p->channel); + openr2_chan_handle_cas(p->r2chan); + } #else - ast_log(LOG_WARNING, "Recieved bits changed on %s signalling?\n", sig2str(p->sig)); + ast_log(LOG_WARNING, "Recieved bits changed on %s signalling?\n", sig2str(p->sig)); #endif - case DAHDI_EVENT_PULSE_START: - /* Stop tone if there's a pulse start and the PBX isn't started */ - if (!ast->pbx) - tone_zone_play_tone(p->subs[idx].dfd, -1); + case DAHDI_EVENT_PULSE_START: + /* Stop tone if there's a pulse start and the PBX isn't started */ + if (!ast->pbx) + tone_zone_play_tone(p->subs[idx].dfd, -1); + break; + case DAHDI_EVENT_DIALCOMPLETE: +#ifdef HAVE_OPENR2 + if ((p->sig & SIG_MFCR2) && p->r2chan && ast->_state != AST_STATE_UP) { + /* we don't need to do anything for this event for R2 signaling + if the call is being setup */ break; - case DAHDI_EVENT_DIALCOMPLETE: -#ifdef HAVE_OPENR2 - if ((p->sig & SIG_MFCR2) && p->r2chan && ast->_state != AST_STATE_UP) { - /* we don't need to do anything for this event for R2 signaling - if the call is being setup */ - break; - } + } #endif - if (p->inalarm) break; - if ((p->radio || (p->oprmode < 0))) break; - if (ioctl(p->subs[idx].dfd,DAHDI_DIALING,&x) == -1) { - ast_log(LOG_DEBUG, "DAHDI_DIALING ioctl failed on %s: %s\n",ast->name, strerror(errno)); - return NULL; - } - if (!x) { /* if not still dialing in driver */ - dahdi_enable_ec(p); - if (p->echobreak) { - dahdi_train_ec(p); - ast_copy_string(p->dop.dialstr, p->echorest, sizeof(p->dop.dialstr)); - p->dop.op = DAHDI_DIAL_OP_REPLACE; - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop); - p->echobreak = 0; - } else { - p->dialing = 0; - if ((mysig == SIG_E911) || (mysig == SIG_FGC_CAMA) || (mysig == SIG_FGC_CAMAMF)) { - /* if thru with dialing after offhook */ - if (ast->_state == AST_STATE_DIALING_OFFHOOK) { - ast_setstate(ast, AST_STATE_UP); - p->subs[idx].f.frametype = AST_FRAME_CONTROL; - p->subs[idx].f.subclass = AST_CONTROL_ANSWER; - break; - } else { /* if to state wait for offhook to dial rest */ - /* we now wait for off hook */ - ast_setstate(ast,AST_STATE_DIALING_OFFHOOK); - } + if (p->inalarm) break; + if ((p->radio || (p->oprmode < 0))) break; + if (ioctl(p->subs[idx].dfd,DAHDI_DIALING,&x) == -1) { + ast_log(LOG_DEBUG, "DAHDI_DIALING ioctl failed on %s: %s\n",ast->name, strerror(errno)); + return NULL; + } + if (!x) { /* if not still dialing in driver */ + dahdi_enable_ec(p); + if (p->echobreak) { + dahdi_train_ec(p); + ast_copy_string(p->dop.dialstr, p->echorest, sizeof(p->dop.dialstr)); + p->dop.op = DAHDI_DIAL_OP_REPLACE; + res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop); + p->echobreak = 0; + } else { + p->dialing = 0; + if ((mysig == SIG_E911) || (mysig == SIG_FGC_CAMA) || (mysig == SIG_FGC_CAMAMF)) { + /* if thru with dialing after offhook */ + if (ast->_state == AST_STATE_DIALING_OFFHOOK) { + ast_setstate(ast, AST_STATE_UP); + p->subs[idx].f.frametype = AST_FRAME_CONTROL; + p->subs[idx].f.subclass = AST_CONTROL_ANSWER; + break; + } else { /* if to state wait for offhook to dial rest */ + /* we now wait for off hook */ + ast_setstate(ast,AST_STATE_DIALING_OFFHOOK); } - if (ast->_state == AST_STATE_DIALING) { - if ((p->callprogress & CALLPROGRESS_PROGRESS) && CANPROGRESSDETECT(p) && p->dsp && p->outgoing) { - ast_debug(1, "Done dialing, but waiting for progress detection before doing more...\n"); - } else if (p->confirmanswer || (!p->dialednone - && ((mysig == SIG_EM) || (mysig == SIG_EM_E1) - || (mysig == SIG_EMWINK) || (mysig == SIG_FEATD) - || (mysig == SIG_FEATDMF_TA) || (mysig == SIG_FEATDMF) - || (mysig == SIG_E911) || (mysig == SIG_FGC_CAMA) - || (mysig == SIG_FGC_CAMAMF) || (mysig == SIG_FEATB) - || (mysig == SIG_SF) || (mysig == SIG_SFWINK) - || (mysig == SIG_SF_FEATD) || (mysig == SIG_SF_FEATDMF) - || (mysig == SIG_SF_FEATB)))) { - ast_setstate(ast, AST_STATE_RINGING); - } else if (!p->answeronpolarityswitch) { - ast_setstate(ast, AST_STATE_UP); - p->subs[idx].f.frametype = AST_FRAME_CONTROL; - p->subs[idx].f.subclass = AST_CONTROL_ANSWER; - /* If aops=0 and hops=1, this is necessary */ - p->polarity = POLARITY_REV; - } else { - /* Start clean, so we can catch the change to REV polarity when party answers */ - p->polarity = POLARITY_IDLE; - } + } + if (ast->_state == AST_STATE_DIALING) { + if ((p->callprogress & CALLPROGRESS_PROGRESS) && CANPROGRESSDETECT(p) && p->dsp && p->outgoing) { + ast_debug(1, "Done dialing, but waiting for progress detection before doing more...\n"); + } else if (p->confirmanswer || (!p->dialednone + && ((mysig == SIG_EM) || (mysig == SIG_EM_E1) + || (mysig == SIG_EMWINK) || (mysig == SIG_FEATD) + || (mysig == SIG_FEATDMF_TA) || (mysig == SIG_FEATDMF) + || (mysig == SIG_E911) || (mysig == SIG_FGC_CAMA) + || (mysig == SIG_FGC_CAMAMF) || (mysig == SIG_FEATB) + || (mysig == SIG_SF) || (mysig == SIG_SFWINK) + || (mysig == SIG_SF_FEATD) || (mysig == SIG_SF_FEATDMF) + || (mysig == SIG_SF_FEATB)))) { + ast_setstate(ast, AST_STATE_RINGING); + } else if (!p->answeronpolarityswitch) { + ast_setstate(ast, AST_STATE_UP); + p->subs[idx].f.frametype = AST_FRAME_CONTROL; + p->subs[idx].f.subclass = AST_CONTROL_ANSWER; + /* If aops=0 and hops=1, this is necessary */ + p->polarity = POLARITY_REV; + } else { + /* Start clean, so we can catch the change to REV polarity when party answers */ + p->polarity = POLARITY_IDLE; } } } - break; - case DAHDI_EVENT_ALARM: + } + break; + case DAHDI_EVENT_ALARM: #ifdef HAVE_PRI - if ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) { - if (!p->pri || !p->pri->pri || (pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0)) { - /* T309 is not enabled : hangup calls when alarm occurs */ - if (p->call) { - if (p->pri && p->pri->pri) { - if (!pri_grab(p, p->pri)) { - pri_hangup(p->pri->pri, p->call, -1); - pri_destroycall(p->pri->pri, p->call); - p->call = NULL; - pri_rel(p->pri); - } else - ast_log(LOG_WARNING, "Failed to grab PRI!\n"); + if ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) { + if (!p->pri || !p->pri->pri || (pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0)) { + /* T309 is not enabled : hangup calls when alarm occurs */ + if (p->call) { + if (p->pri && p->pri->pri) { + if (!pri_grab(p, p->pri)) { + pri_hangup(p->pri->pri, p->call, -1); + pri_destroycall(p->pri->pri, p->call); + p->call = NULL; + pri_rel(p->pri); } else - ast_log(LOG_WARNING, "The PRI Call has not been destroyed\n"); - } - if (p->owner) - p->owner->_softhangup |= AST_SOFTHANGUP_DEV; + ast_log(LOG_WARNING, "Failed to grab PRI!\n"); + } else + ast_log(LOG_WARNING, "The PRI Call has not been destroyed\n"); } + if (p->owner) + p->owner->_softhangup |= AST_SOFTHANGUP_DEV; } - if (p->bearer) - p->bearer->inalarm = 1; - else + } + if (p->bearer) + p->bearer->inalarm = 1; + else #endif - p->inalarm = 1; - res = get_alarms(p); - handle_alarms(p, res); + p->inalarm = 1; + res = get_alarms(p); + handle_alarms(p, res); #ifdef HAVE_PRI - if (!p->pri || !p->pri->pri || pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0) { - /* fall through intentionally */ - } else { - break; - } + if (!p->pri || !p->pri->pri || pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0) { + /* fall through intentionally */ + } else { + break; + } #endif #ifdef HAVE_SS7 - if (p->sig == SIG_SS7) - break; + if (p->sig == SIG_SS7) + break; #endif #ifdef HAVE_OPENR2 - if (p->sig == SIG_MFCR2) - break; + if (p->sig == SIG_MFCR2) + break; #endif - case DAHDI_EVENT_ONHOOK: - if (p->radio) { - p->subs[idx].f.frametype = AST_FRAME_CONTROL; - p->subs[idx].f.subclass = AST_CONTROL_RADIO_UNKEY; - break; - } - if (p->oprmode < 0) + case DAHDI_EVENT_ONHOOK: + if (p->radio) { + p->subs[idx].f.frametype = AST_FRAME_CONTROL; + p->subs[idx].f.subclass = AST_CONTROL_RADIO_UNKEY; + break; + } + if (p->oprmode < 0) + { + if (p->oprmode != -1) break; + if ((p->sig == SIG_FXOLS) || (p->sig == SIG_FXOKS) || (p->sig == SIG_FXOGS)) { - if (p->oprmode != -1) break; - if ((p->sig == SIG_FXOLS) || (p->sig == SIG_FXOKS) || (p->sig == SIG_FXOGS)) - { - /* Make sure it starts ringing */ - dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_RINGOFF); - dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_RING); - save_conference(p->oprpeer); - tone_zone_play_tone(p->oprpeer->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE); - } - break; + /* Make sure it starts ringing */ + dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_RINGOFF); + dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_RING); + save_conference(p->oprpeer); + tone_zone_play_tone(p->oprpeer->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE); } - switch (p->sig) { - case SIG_FXOLS: - case SIG_FXOGS: - case SIG_FXOKS: - p->onhooktime = time(NULL); - p->fxsoffhookstate = 0; - p->msgstate = -1; - /* Check for some special conditions regarding call waiting */ - if (idx == SUB_REAL) { - /* The normal line was hung up */ - if (p->subs[SUB_CALLWAIT].owner) { - /* There's a call waiting call, so ring the phone, but make it unowned in the mean time */ - swap_subs(p, SUB_CALLWAIT, SUB_REAL); - ast_verb(3, "Channel %d still has (callwait) call, ringing phone\n", p->channel); - unalloc_sub(p, SUB_CALLWAIT); + break; + } + switch (p->sig) { + case SIG_FXOLS: + case SIG_FXOGS: + case SIG_FXOKS: + p->onhooktime = time(NULL); + p->fxsoffhookstate = 0; + p->msgstate = -1; + /* Check for some special conditions regarding call waiting */ + if (idx == SUB_REAL) { + /* The normal line was hung up */ + if (p->subs[SUB_CALLWAIT].owner) { + /* There's a call waiting call, so ring the phone, but make it unowned in the mean time */ + swap_subs(p, SUB_CALLWAIT, SUB_REAL); + ast_verb(3, "Channel %d still has (callwait) call, ringing phone\n", p->channel); + unalloc_sub(p, SUB_CALLWAIT); #if 0 - p->subs[idx].needanswer = 0; - p->subs[idx].needringing = 0; + p->subs[idx].needanswer = 0; + p->subs[idx].needringing = 0; #endif - p->callwaitingrepeat = 0; - p->cidcwexpire = 0; - p->owner = NULL; - /* Don't start streaming audio yet if the incoming call isn't up yet */ - if (p->subs[SUB_REAL].owner->_state != AST_STATE_UP) - p->dialing = 1; - dahdi_ring_phone(p); - } else if (p->subs[SUB_THREEWAY].owner) { - unsigned int mssinceflash; - /* Here we have to retain the lock on both the main channel, the 3-way channel, and - the private structure -- not especially easy or clean */ - while (p->subs[SUB_THREEWAY].owner && ast_channel_trylock(p->subs[SUB_THREEWAY].owner)) { - /* Yuck, didn't get the lock on the 3-way, gotta release everything and re-grab! */ - DLA_UNLOCK(&p->lock); - CHANNEL_DEADLOCK_AVOIDANCE(ast); - /* We can grab ast and p in that order, without worry. We should make sure - nothing seriously bad has happened though like some sort of bizarre double - masquerade! */ - DLA_LOCK(&p->lock); - if (p->owner != ast) { - ast_log(LOG_WARNING, "This isn't good...\n"); - return NULL; - } - } - if (!p->subs[SUB_THREEWAY].owner) { - ast_log(LOG_NOTICE, "Whoa, threeway disappeared kinda randomly.\n"); + p->callwaitingrepeat = 0; + p->cidcwexpire = 0; + p->owner = NULL; + /* Don't start streaming audio yet if the incoming call isn't up yet */ + if (p->subs[SUB_REAL].owner->_state != AST_STATE_UP) + p->dialing = 1; + dahdi_ring_phone(p); + } else if (p->subs[SUB_THREEWAY].owner) { + unsigned int mssinceflash; + /* Here we have to retain the lock on both the main channel, the 3-way channel, and + the private structure -- not especially easy or clean */ + while (p->subs[SUB_THREEWAY].owner && ast_channel_trylock(p->subs[SUB_THREEWAY].owner)) { + /* Yuck, didn't get the lock on the 3-way, gotta release everything and re-grab! */ + DLA_UNLOCK(&p->lock); + CHANNEL_DEADLOCK_AVOIDANCE(ast); + /* We can grab ast and p in that order, without worry. We should make sure + nothing seriously bad has happened though like some sort of bizarre double + masquerade! */ + DLA_LOCK(&p->lock); + if (p->owner != ast) { + ast_log(LOG_WARNING, "This isn't good...\n"); return NULL; } - mssinceflash = ast_tvdiff_ms(ast_tvnow(), p->flashtime); - ast_debug(1, "Last flash was %d ms ago\n", mssinceflash); - if (mssinceflash < MIN_MS_SINCE_FLASH) { - /* It hasn't been long enough since the last flashook. This is probably a bounce on - hanging up. Hangup both channels now */ - if (p->subs[SUB_THREEWAY].owner) - ast_queue_hangup_with_cause(p->subs[SUB_THREEWAY].owner, AST_CAUSE_NO_ANSWER); - p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV; - ast_debug(1, "Looks like a bounced flash, hanging up both calls on %d\n", p->channel); - ast_channel_unlock(p->subs[SUB_THREEWAY].owner); - } else if ((ast->pbx) || (ast->_state == AST_STATE_UP)) { - if (p->transfer) { - /* In any case this isn't a threeway call anymore */ - p->subs[SUB_REAL].inthreeway = 0; - p->subs[SUB_THREEWAY].inthreeway = 0; - /* Only attempt transfer if the phone is ringing; why transfer to busy tone eh? */ - if (!p->transfertobusy && ast->_state == AST_STATE_BUSY) { - ast_channel_unlock(p->subs[SUB_THREEWAY].owner); - /* Swap subs and dis-own channel */ - swap_subs(p, SUB_THREEWAY, SUB_REAL); - p->owner = NULL; - /* Ring the phone */ - dahdi_ring_phone(p); - } else { - if ((res = attempt_transfer(p)) < 0) { - p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV; - if (p->subs[SUB_THREEWAY].owner) - ast_channel_unlock(p->subs[SUB_THREEWAY].owner); - } else if (res) { - /* Don't actually hang up at this point */ - if (p->subs[SUB_THREEWAY].owner) - ast_channel_unlock(p->subs[SUB_THREEWAY].owner); - break; - } + } + if (!p->subs[SUB_THREEWAY].owner) { + ast_log(LOG_NOTICE, "Whoa, threeway disappeared kinda randomly.\n"); + return NULL; + } + mssinceflash = ast_tvdiff_ms(ast_tvnow(), p->flashtime); + ast_debug(1, "Last flash was %d ms ago\n", mssinceflash); + if (mssinceflash < MIN_MS_SINCE_FLASH) { + /* It hasn't been long enough since the last flashook. This is probably a bounce on + hanging up. Hangup both channels now */ + if (p->subs[SUB_THREEWAY].owner) + ast_queue_hangup_with_cause(p->subs[SUB_THREEWAY].owner, AST_CAUSE_NO_ANSWER); + p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV; + ast_debug(1, "Looks like a bounced flash, hanging up both calls on %d\n", p->channel); + ast_channel_unlock(p->subs[SUB_THREEWAY].owner); + } else if ((ast->pbx) || (ast->_state == AST_STATE_UP)) { + if (p->transfer) { + /* In any case this isn't a threeway call anymore */ + p->subs[SUB_REAL].inthreeway = 0; + p->subs[SUB_THREEWAY].inthreeway = 0; + /* Only attempt transfer if the phone is ringing; why transfer to busy tone eh? */ + if (!p->transfertobusy && ast->_state == AST_STATE_BUSY) { + ast_channel_unlock(p->subs[SUB_THREEWAY].owner); + /* Swap subs and dis-own channel */ + swap_subs(p, SUB_THREEWAY, SUB_REAL); + p->owner = NULL; + /* Ring the phone */ + dahdi_ring_phone(p); + } else { + if ((res = attempt_transfer(p)) < 0) { + p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV; + if (p->subs[SUB_THREEWAY].owner) + ast_channel_unlock(p->subs[SUB_THREEWAY].owner); + } else if (res) { + /* Don't actually hang up at this point */ + if (p->subs[SUB_THREEWAY].owner) + ast_channel_unlock(p->subs[SUB_THREEWAY].owner); + break; } - } else { - p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV; - if (p->subs[SUB_THREEWAY].owner) - ast_channel_unlock(p->subs[SUB_THREEWAY].owner); } } else { - ast_channel_unlock(p->subs[SUB_THREEWAY].owner); - /* Swap subs and dis-own channel */ - swap_subs(p, SUB_THREEWAY, SUB_REAL); - p->owner = NULL; - /* Ring the phone */ - dahdi_ring_phone(p); + p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV; + if (p->subs[SUB_THREEWAY].owner) + ast_channel_unlock(p->subs[SUB_THREEWAY].owner); } + } else { + ast_channel_unlock(p->subs[SUB_THREEWAY].owner); + /* Swap subs and dis-own channel */ + swap_subs(p, SUB_THREEWAY, SUB_REAL); + p->owner = NULL; + /* Ring the phone */ + dahdi_ring_phone(p); } - } else { - ast_log(LOG_WARNING, "Got a hangup and my index is %d?\n", idx); } - /* Fall through */ - default: - dahdi_disable_ec(p); - return NULL; + } else { + ast_log(LOG_WARNING, "Got a hangup and my index is %d?\n", idx); } - break; - case DAHDI_EVENT_RINGOFFHOOK: - if (p->inalarm) break; - if (p->oprmode < 0) + /* Fall through */ + default: + dahdi_disable_ec(p); + return NULL; + } + break; + case DAHDI_EVENT_RINGOFFHOOK: + if (p->inalarm) break; + if (p->oprmode < 0) + { + if ((p->sig == SIG_FXOLS) || (p->sig == SIG_FXOKS) || (p->sig == SIG_FXOGS)) { - if ((p->sig == SIG_FXOLS) || (p->sig == SIG_FXOKS) || (p->sig == SIG_FXOGS)) - { - /* Make sure it stops ringing */ - dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_RINGOFF); - tone_zone_play_tone(p->oprpeer->subs[SUB_REAL].dfd, -1); - restore_conference(p->oprpeer); - } - break; + /* Make sure it stops ringing */ + dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_RINGOFF); + tone_zone_play_tone(p->oprpeer->subs[SUB_REAL].dfd, -1); + restore_conference(p->oprpeer); } - if (p->radio) - { - p->subs[idx].f.frametype = AST_FRAME_CONTROL; - p->subs[idx].f.subclass = AST_CONTROL_RADIO_KEY; - break; - } - /* for E911, its supposed to wait for offhook then dial - the second half of the dial string */ - if (((mysig == SIG_E911) || (mysig == SIG_FGC_CAMA) || (mysig == SIG_FGC_CAMAMF)) && (ast->_state == AST_STATE_DIALING_OFFHOOK)) { - c = strchr(p->dialdest, '/'); - if (c) - c++; - else - c = p->dialdest; - if (*c) snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*0%s#", c); - else ast_copy_string(p->dop.dialstr,"M*2#", sizeof(p->dop.dialstr)); - if (strlen(p->dop.dialstr) > 4) { - memset(p->echorest, 'w', sizeof(p->echorest) - 1); - strcpy(p->echorest + (p->echotraining / 401) + 1, p->dop.dialstr + strlen(p->dop.dialstr) - 2); - p->echorest[sizeof(p->echorest) - 1] = '\0'; - p->echobreak = 1; - p->dop.dialstr[strlen(p->dop.dialstr)-2] = '\0'; - } else - p->echobreak = 0; - if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop)) { - int saveerr = errno; + break; + } + if (p->radio) + { + p->subs[idx].f.frametype = AST_FRAME_CONTROL; + p->subs[idx].f.subclass = AST_CONTROL_RADIO_KEY; + break; + } + /* for E911, its supposed to wait for offhook then dial + the second half of the dial string */ + if (((mysig == SIG_E911) || (mysig == SIG_FGC_CAMA) || (mysig == SIG_FGC_CAMAMF)) && (ast->_state == AST_STATE_DIALING_OFFHOOK)) { + c = strchr(p->dialdest, '/'); + if (c) + c++; + else + c = p->dialdest; + if (*c) snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*0%s#", c); + else ast_copy_string(p->dop.dialstr,"M*2#", sizeof(p->dop.dialstr)); + if (strlen(p->dop.dialstr) > 4) { + memset(p->echorest, 'w', sizeof(p->echorest) - 1); + strcpy(p->echorest + (p->echotraining / 401) + 1, p->dop.dialstr + strlen(p->dop.dialstr) - 2); + p->echorest[sizeof(p->echorest) - 1] = '\0'; + p->echobreak = 1; + p->dop.dialstr[strlen(p->dop.dialstr)-2] = '\0'; + } else + p->echobreak = 0; + if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop)) { + int saveerr = errno; - x = DAHDI_ONHOOK; - ioctl(p->subs[SUB_REAL].dfd, DAHDI_HOOK, &x); - ast_log(LOG_WARNING, "Dialing failed on channel %d: %s\n", p->channel, strerror(saveerr)); - return NULL; - } - p->dialing = 1; - return &p->subs[idx].f; - } - switch (p->sig) { - case SIG_FXOLS: - case SIG_FXOGS: - case SIG_FXOKS: - p->fxsoffhookstate = 1; - switch (ast->_state) { - case AST_STATE_RINGING: - dahdi_enable_ec(p); - dahdi_train_ec(p); - p->subs[idx].f.frametype = AST_FRAME_CONTROL; - p->subs[idx].f.subclass = AST_CONTROL_ANSWER; - /* Make sure it stops ringing */ - dahdi_set_hook(p->subs[idx].dfd, DAHDI_OFFHOOK); - ast_debug(1, "channel %d answered\n", p->channel); - if (p->cidspill) { - /* Cancel any running CallerID spill */ - ast_free(p->cidspill); - p->cidspill = NULL; - } - p->dialing = 0; - p->callwaitcas = 0; - if (p->confirmanswer) { - /* Ignore answer if "confirm answer" is enabled */ - p->subs[idx].f.frametype = AST_FRAME_NULL; - p->subs[idx].f.subclass = 0; - } else if (!ast_strlen_zero(p->dop.dialstr)) { - /* nick@dccinc.com 4/3/03 - fxo should be able to do deferred dialing */ - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop); - if (res < 0) { - ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", p->channel, strerror(errno)); - p->dop.dialstr[0] = '\0'; - return NULL; - } else { - ast_debug(1, "Sent FXO deferred digit string: %s\n", p->dop.dialstr); - p->subs[idx].f.frametype = AST_FRAME_NULL; - p->subs[idx].f.subclass = 0; - p->dialing = 1; - } - p->dop.dialstr[0] = '\0'; - ast_setstate(ast, AST_STATE_DIALING); - } else - ast_setstate(ast, AST_STATE_UP); - return &p->subs[idx].f; - case AST_STATE_DOWN: - ast_setstate(ast, AST_STATE_RING); - ast->rings = 1; - p->subs[idx].f.frametype = AST_FRAME_CONTROL; - p->subs[idx].f.subclass = AST_CONTROL_OFFHOOK; - ast_debug(1, "channel %d picked up\n", p->channel); - return &p->subs[idx].f; - case AST_STATE_UP: - /* Make sure it stops ringing */ - dahdi_set_hook(p->subs[idx].dfd, DAHDI_OFFHOOK); - /* Okay -- probably call waiting*/ - if (ast_bridged_channel(p->owner)) - ast_queue_control(p->owner, AST_CONTROL_UNHOLD); - p->subs[idx].needunhold = 1; - break; - case AST_STATE_RESERVED: - /* Start up dialtone */ - if (has_voicemail(p)) - res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_STUTTER); - else - res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_DIALTONE); - break; - default: - ast_log(LOG_WARNING, "FXO phone off hook in weird state %d??\n", ast->_state); + x = DAHDI_ONHOOK; + ioctl(p->subs[SUB_REAL].dfd, DAHDI_HOOK, &x); + ast_log(LOG_WARNING, "Dialing failed on channel %d: %s\n", p->channel, strerror(saveerr)); + return NULL; } - break; - case SIG_FXSLS: - case SIG_FXSGS: - case SIG_FXSKS: - if (ast->_state == AST_STATE_RING) { - p->ringt = p->ringt_base; + p->dialing = 1; + return &p->subs[idx].f; + } + switch (p->sig) { + case SIG_FXOLS: + case SIG_FXOGS: + case SIG_FXOKS: + p->fxsoffhookstate = 1; + switch (ast->_state) { + case AST_STATE_RINGING: + dahdi_enable_ec(p); + dahdi_train_ec(p); + p->subs[idx].f.frametype = AST_FRAME_CONTROL; + p->subs[idx].f.subclass = AST_CONTROL_ANSWER; + /* Make sure it stops ringing */ + dahdi_set_hook(p->subs[idx].dfd, DAHDI_OFFHOOK); + ast_debug(1, "channel %d answered\n", p->channel); + if (p->cidspill) { + /* Cancel any running CallerID spill */ + ast_free(p->cidspill); + p->cidspill = NULL; } - - /* If we get a ring then we cannot be in - * reversed polarity. So we reset to idle */ - ast_debug(1, "Setting IDLE polarity due " - "to ring. Old polarity was %d\n", - p->polarity); - p->polarity = POLARITY_IDLE; - - /* Fall through */ - case SIG_EM: - case SIG_EM_E1: - case SIG_EMWINK: - case SIG_FEATD: - case SIG_FEATDMF: - case SIG_FEATDMF_TA: - case SIG_E911: - case SIG_FGC_CAMA: - case SIG_FGC_CAMAMF: - case SIG_FEATB: - case SIG_SF: - case SIG_SFWINK: - case SIG_SF_FEATD: - case SIG_SF_FEATDMF: - case SIG_SF_FEATB: - if (ast->_state == AST_STATE_PRERING) - ast_setstate(ast, AST_STATE_RING); - if ((ast->_state == AST_STATE_DOWN) || (ast->_state == AST_STATE_RING)) { - ast_debug(1, "Ring detected\n"); - p->subs[idx].f.frametype = AST_FRAME_CONTROL; - p->subs[idx].f.subclass = AST_CONTROL_RING; - } else if (p->outgoing && ((ast->_state == AST_STATE_RINGING) || (ast->_state == AST_STATE_DIALING))) { - ast_debug(1, "Line answered\n"); - if (p->confirmanswer) { + p->dialing = 0; + p->callwaitcas = 0; + if (p->confirmanswer) { + /* Ignore answer if "confirm answer" is enabled */ + p->subs[idx].f.frametype = AST_FRAME_NULL; + p->subs[idx].f.subclass = 0; + } else if (!ast_strlen_zero(p->dop.dialstr)) { + /* nick@dccinc.com 4/3/03 - fxo should be able to do deferred dialing */ + res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop); + if (res < 0) { + ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", p->channel, strerror(errno)); + p->dop.dialstr[0] = '\0'; + return NULL; + } else { + ast_debug(1, "Sent FXO deferred digit string: %s\n", p->dop.dialstr); p->subs[idx].f.frametype = AST_FRAME_NULL; p->subs[idx].f.subclass = 0; - } else { - p->subs[idx].f.frametype = AST_FRAME_CONTROL; - p->subs[idx].f.subclass = AST_CONTROL_ANSWER; - ast_setstate(ast, AST_STATE_UP); + p->dialing = 1; } - } else if (ast->_state != AST_STATE_RING) - ast_log(LOG_WARNING, "Ring/Off-hook in strange state %d on channel %d\n", ast->_state, p->channel); + p->dop.dialstr[0] = '\0'; + ast_setstate(ast, AST_STATE_DIALING); + } else + ast_setstate(ast, AST_STATE_UP); + return &p->subs[idx].f; + case AST_STATE_DOWN: + ast_setstate(ast, AST_STATE_RING); + ast->rings = 1; + p->subs[idx].f.frametype = AST_FRAME_CONTROL; + p->subs[idx].f.subclass = AST_CONTROL_OFFHOOK; + ast_debug(1, "channel %d picked up\n", p->channel); + return &p->subs[idx].f; + case AST_STATE_UP: + /* Make sure it stops ringing */ + dahdi_set_hook(p->subs[idx].dfd, DAHDI_OFFHOOK); + /* Okay -- probably call waiting*/ + if (ast_bridged_channel(p->owner)) + ast_queue_control(p->owner, AST_CONTROL_UNHOLD); + p->subs[idx].needunhold = 1; break; + case AST_STATE_RESERVED: + /* Start up dialtone */ + if (has_voicemail(p)) + res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_STUTTER); + else + res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_DIALTONE); + break; default: - ast_log(LOG_WARNING, "Don't know how to handle ring/off hook for signalling %d\n", p->sig); + ast_log(LOG_WARNING, "FXO phone off hook in weird state %d??\n", ast->_state); } break; - case DAHDI_EVENT_RINGBEGIN: - switch (p->sig) { - case SIG_FXSLS: - case SIG_FXSGS: - case SIG_FXSKS: - if (ast->_state == AST_STATE_RING) { - p->ringt = p->ringt_base; + case SIG_FXSLS: + case SIG_FXSGS: + case SIG_FXSKS: + if (ast->_state == AST_STATE_RING) { + p->ringt = p->ringt_base; + } + + /* If we get a ring then we cannot be in + * reversed polarity. So we reset to idle */ + ast_debug(1, "Setting IDLE polarity due " + "to ring. Old polarity was %d\n", + p->polarity); + p->polarity = POLARITY_IDLE; + + /* Fall through */ + case SIG_EM: + case SIG_EM_E1: + case SIG_EMWINK: + case SIG_FEATD: + case SIG_FEATDMF: + case SIG_FEATDMF_TA: + case SIG_E911: + case SIG_FGC_CAMA: + case SIG_FGC_CAMAMF: + case SIG_FEATB: + case SIG_SF: + case SIG_SFWINK: + case SIG_SF_FEATD: + case SIG_SF_FEATDMF: + case SIG_SF_FEATB: + if (ast->_state == AST_STATE_PRERING) + ast_setstate(ast, AST_STATE_RING); + if ((ast->_state == AST_STATE_DOWN) || (ast->_state == AST_STATE_RING)) { + ast_debug(1, "Ring detected\n"); + p->subs[idx].f.frametype = AST_FRAME_CONTROL; + p->subs[idx].f.subclass = AST_CONTROL_RING; + } else if (p->outgoing && ((ast->_state == AST_STATE_RINGING) || (ast->_state == AST_STATE_DIALING))) { + ast_debug(1, "Line answered\n"); + if (p->confirmanswer) { + p->subs[idx].f.frametype = AST_FRAME_NULL; + p->subs[idx].f.subclass = 0; + } else { + p->subs[idx].f.frametype = AST_FRAME_CONTROL; + p->subs[idx].f.subclass = AST_CONTROL_ANSWER; + ast_setstate(ast, AST_STATE_UP); } - break; - } + } else if (ast->_state != AST_STATE_RING) + ast_log(LOG_WARNING, "Ring/Off-hook in strange state %d on channel %d\n", ast->_state, p->channel); break; - case DAHDI_EVENT_RINGEROFF: - if (p->inalarm) break; - if ((p->radio || (p->oprmode < 0))) break; - ast->rings++; - if ((ast->rings > p->cidrings) && (p->cidspill)) { - ast_log(LOG_WARNING, "Didn't finish Caller-ID spill. Cancelling.\n"); - ast_free(p->cidspill); - p->cidspill = NULL; - p->callwaitcas = 0; + default: + ast_log(LOG_WARNING, "Don't know how to handle ring/off hook for signalling %d\n", p->sig); + } + break; + case DAHDI_EVENT_RINGBEGIN: + switch (p->sig) { + case SIG_FXSLS: + case SIG_FXSGS: + case SIG_FXSKS: + if (ast->_state == AST_STATE_RING) { + p->ringt = p->ringt_base; } - p->subs[idx].f.frametype = AST_FRAME_CONTROL; - p->subs[idx].f.subclass = AST_CONTROL_RINGING; break; - case DAHDI_EVENT_RINGERON: - break; - case DAHDI_EVENT_NOALARM: - p->inalarm = 0; + } + break; + case DAHDI_EVENT_RINGEROFF: + if (p->inalarm) break; + if ((p->radio || (p->oprmode < 0))) break; + ast->rings++; + if ((ast->rings > p->cidrings) && (p->cidspill)) { + ast_log(LOG_WARNING, "Didn't finish Caller-ID spill. Cancelling.\n"); + ast_free(p->cidspill); + p->cidspill = NULL; + p->callwaitcas = 0; + } + p->subs[idx].f.frametype = AST_FRAME_CONTROL; + p->subs[idx].f.subclass = AST_CONTROL_RINGING; + break; + case DAHDI_EVENT_RINGERON: + break; + case DAHDI_EVENT_NOALARM: + p->inalarm = 0; #ifdef HAVE_PRI - /* Extremely unlikely but just in case */ - if (p->bearer) - p->bearer->inalarm = 0; + /* Extremely unlikely but just in case */ + if (p->bearer) + p->bearer->inalarm = 0; #endif - ast_log(LOG_NOTICE, "Alarm cleared on channel %d\n", p->channel); - manager_event(EVENT_FLAG_SYSTEM, "AlarmClear", - "Channel: %d\r\n", p->channel); - break; - case DAHDI_EVENT_WINKFLASH: - if (p->inalarm) break; - if (p->radio) break; - if (p->oprmode < 0) break; - if (p->oprmode > 1) + ast_log(LOG_NOTICE, "Alarm cleared on channel %d\n", p->channel); + manager_event(EVENT_FLAG_SYSTEM, "AlarmClear", + "Channel: %d\r\n", p->channel); + break; + case DAHDI_EVENT_WINKFLASH: + if (p->inalarm) break; + if (p->radio) break; + if (p->oprmode < 0) break; + if (p->oprmode > 1) + { + struct dahdi_params par; + + memset(&par, 0, sizeof(par)); + if (ioctl(p->oprpeer->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &par) != -1) { - struct dahdi_params par; - - memset(&par, 0, sizeof(par)); - if (ioctl(p->oprpeer->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &par) != -1) + if (!par.rxisoffhook) { - if (!par.rxisoffhook) - { - /* Make sure it stops ringing */ - dahdi_set_hook(p->oprpeer->subs[SUB_REAL].dfd, DAHDI_RINGOFF); - dahdi_set_hook(p->oprpeer->subs[SUB_REAL].dfd, DAHDI_RING); - save_conference(p); - tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE); - } + /* Make sure it stops ringing */ + dahdi_set_hook(p->oprpeer->subs[SUB_REAL].dfd, DAHDI_RINGOFF); + dahdi_set_hook(p->oprpeer->subs[SUB_REAL].dfd, DAHDI_RING); + save_conference(p); + tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE); } - break; } - /* Remember last time we got a flash-hook */ - p->flashtime = ast_tvnow(); - switch (mysig) { - case SIG_FXOLS: - case SIG_FXOGS: - case SIG_FXOKS: - ast_debug(1, "Winkflash, index: %d, normal: %d, callwait: %d, thirdcall: %d\n", - idx, p->subs[SUB_REAL].dfd, p->subs[SUB_CALLWAIT].dfd, p->subs[SUB_THREEWAY].dfd); - p->callwaitcas = 0; + break; + } + /* Remember last time we got a flash-hook */ + p->flashtime = ast_tvnow(); + switch (mysig) { + case SIG_FXOLS: + case SIG_FXOGS: + case SIG_FXOKS: + ast_debug(1, "Winkflash, index: %d, normal: %d, callwait: %d, thirdcall: %d\n", + idx, p->subs[SUB_REAL].dfd, p->subs[SUB_CALLWAIT].dfd, p->subs[SUB_THREEWAY].dfd); + p->callwaitcas = 0; - if (idx != SUB_REAL) { - ast_log(LOG_WARNING, "Got flash hook with index %d on channel %d?!?\n", idx, p->channel); + if (idx != SUB_REAL) { + ast_log(LOG_WARNING, "Got flash hook with index %d on channel %d?!?\n", idx, p->channel); + goto winkflashdone; + } + + if (p->subs[SUB_CALLWAIT].owner) { + /* Swap to call-wait */ + swap_subs(p, SUB_REAL, SUB_CALLWAIT); + tone_zone_play_tone(p->subs[SUB_REAL].dfd, -1); + p->owner = p->subs[SUB_REAL].owner; + ast_debug(1, "Making %s the new owner\n", p->owner->name); + if (p->owner->_state == AST_STATE_RINGING) { + ast_setstate(p->owner, AST_STATE_UP); + p->subs[SUB_REAL].needanswer = 1; + } + p->callwaitingrepeat = 0; + p->cidcwexpire = 0; + /* Start music on hold if appropriate */ + if (!p->subs[SUB_CALLWAIT].inthreeway && ast_bridged_channel(p->subs[SUB_CALLWAIT].owner)) { + ast_queue_control_data(p->subs[SUB_CALLWAIT].owner, AST_CONTROL_HOLD, + S_OR(p->mohsuggest, NULL), + !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); + } + p->subs[SUB_CALLWAIT].needhold = 1; + if (ast_bridged_channel(p->subs[SUB_REAL].owner)) { + ast_queue_control_data(p->subs[SUB_REAL].owner, AST_CONTROL_HOLD, + S_OR(p->mohsuggest, NULL), + !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); + } + p->subs[SUB_REAL].needunhold = 1; + } else if (!p->subs[SUB_THREEWAY].owner) { + if (!p->threewaycalling) { + /* Just send a flash if no 3-way calling */ + p->subs[SUB_REAL].needflash = 1; goto winkflashdone; - } + } else if (!check_for_conference(p)) { + char cid_num[256]; + char cid_name[256]; - if (p->subs[SUB_CALLWAIT].owner) { - /* Swap to call-wait */ - swap_subs(p, SUB_REAL, SUB_CALLWAIT); - tone_zone_play_tone(p->subs[SUB_REAL].dfd, -1); - p->owner = p->subs[SUB_REAL].owner; - ast_debug(1, "Making %s the new owner\n", p->owner->name); - if (p->owner->_state == AST_STATE_RINGING) { - ast_setstate(p->owner, AST_STATE_UP); - p->subs[SUB_REAL].needanswer = 1; + cid_num[0] = 0; + cid_name[0] = 0; + if (p->dahditrcallerid && p->owner) { + if (p->owner->cid.cid_num) + ast_copy_string(cid_num, p->owner->cid.cid_num, sizeof(cid_num)); + if (p->owner->cid.cid_name) + ast_copy_string(cid_name, p->owner->cid.cid_name, sizeof(cid_name)); } - p->callwaitingrepeat = 0; - p->cidcwexpire = 0; - /* Start music on hold if appropriate */ - if (!p->subs[SUB_CALLWAIT].inthreeway && ast_bridged_channel(p->subs[SUB_CALLWAIT].owner)) { - ast_queue_control_data(p->subs[SUB_CALLWAIT].owner, AST_CONTROL_HOLD, - S_OR(p->mohsuggest, NULL), - !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); + /* XXX This section needs much more error checking!!! XXX */ + /* Start a 3-way call if feasible */ + if (!((ast->pbx) || + (ast->_state == AST_STATE_UP) || + (ast->_state == AST_STATE_RING))) { + ast_debug(1, "Flash when call not up or ringing\n"); + goto winkflashdone; } - p->subs[SUB_CALLWAIT].needhold = 1; - if (ast_bridged_channel(p->subs[SUB_REAL].owner)) { - ast_queue_control_data(p->subs[SUB_REAL].owner, AST_CONTROL_HOLD, - S_OR(p->mohsuggest, NULL), - !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); + if (alloc_sub(p, SUB_THREEWAY)) { + ast_log(LOG_WARNING, "Unable to allocate three-way subchannel\n"); + goto winkflashdone; } - p->subs[SUB_REAL].needunhold = 1; - } else if (!p->subs[SUB_THREEWAY].owner) { - if (!p->threewaycalling) { - /* Just send a flash if no 3-way calling */ - p->subs[SUB_REAL].needflash = 1; - goto winkflashdone; - } else if (!check_for_conference(p)) { - char cid_num[256]; - char cid_name[256]; + /* Make new channel */ + chan = dahdi_new(p, AST_STATE_RESERVED, 0, SUB_THREEWAY, 0, 0); + if (p->dahditrcallerid) { + if (!p->origcid_num) + p->origcid_num = ast_strdup(p->cid_num); + if (!p->origcid_name) + p->origcid_name = ast_strdup(p->cid_name); + ast_copy_string(p->cid_num, cid_num, sizeof(p->cid_num)); + ast_copy_string(p->cid_name, cid_name, sizeof(p->cid_name)); + } + /* Swap things around between the three-way and real call */ + swap_subs(p, SUB_THREEWAY, SUB_REAL); + /* Disable echo canceller for better dialing */ + dahdi_disable_ec(p); + res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_DIALRECALL); + if (res) + ast_log(LOG_WARNING, "Unable to start dial recall tone on channel %d\n", p->channel); + p->owner = chan; + if (!chan) { + ast_log(LOG_WARNING, "Cannot allocate new structure on channel %d\n", p->channel); + } else if (ast_pthread_create_detached(&threadid, NULL, analog_ss_thread, chan)) { + ast_log(LOG_WARNING, "Unable to start simple switch on channel %d\n", p->channel); + res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_CONGESTION); + dahdi_enable_ec(p); + ast_hangup(chan); + } else { + struct ast_channel *other = ast_bridged_channel(p->subs[SUB_THREEWAY].owner); + int way3bridge = 0, cdr3way = 0; - cid_num[0] = 0; - cid_name[0] = 0; - if (p->dahditrcallerid && p->owner) { - if (p->owner->cid.cid_num) - ast_copy_string(cid_num, p->owner->cid.cid_num, sizeof(cid_num)); - if (p->owner->cid.cid_name) - ast_copy_string(cid_name, p->owner->cid.cid_name, sizeof(cid_name)); - } - /* XXX This section needs much more error checking!!! XXX */ - /* Start a 3-way call if feasible */ - if (!((ast->pbx) || - (ast->_state == AST_STATE_UP) || - (ast->_state == AST_STATE_RING))) { - ast_debug(1, "Flash when call not up or ringing\n"); - goto winkflashdone; - } - if (alloc_sub(p, SUB_THREEWAY)) { - ast_log(LOG_WARNING, "Unable to allocate three-way subchannel\n"); - goto winkflashdone; - } - /* Make new channel */ - chan = dahdi_new(p, AST_STATE_RESERVED, 0, SUB_THREEWAY, 0, 0); - if (p->dahditrcallerid) { - if (!p->origcid_num) - p->origcid_num = ast_strdup(p->cid_num); - if (!p->origcid_name) - p->origcid_name = ast_strdup(p->cid_name); - ast_copy_string(p->cid_num, cid_num, sizeof(p->cid_num)); - ast_copy_string(p->cid_name, cid_name, sizeof(p->cid_name)); - } - /* Swap things around between the three-way and real call */ - swap_subs(p, SUB_THREEWAY, SUB_REAL); - /* Disable echo canceller for better dialing */ - dahdi_disable_ec(p); - res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_DIALRECALL); - if (res) - ast_log(LOG_WARNING, "Unable to start dial recall tone on channel %d\n", p->channel); - p->owner = chan; - if (!chan) { - ast_log(LOG_WARNING, "Cannot allocate new structure on channel %d\n", p->channel); - } else if (ast_pthread_create_detached(&threadid, NULL, ss_thread, chan)) { - ast_log(LOG_WARNING, "Unable to start simple switch on channel %d\n", p->channel); - res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_CONGESTION); - dahdi_enable_ec(p); - ast_hangup(chan); - } else { - struct ast_channel *other = ast_bridged_channel(p->subs[SUB_THREEWAY].owner); - int way3bridge = 0, cdr3way = 0; + if (!other) { + other = ast_bridged_channel(p->subs[SUB_REAL].owner); + } else + way3bridge = 1; - if (!other) { - other = ast_bridged_channel(p->subs[SUB_REAL].owner); - } else - way3bridge = 1; + if (p->subs[SUB_THREEWAY].owner->cdr) + cdr3way = 1; - if (p->subs[SUB_THREEWAY].owner->cdr) - cdr3way = 1; + ast_verb(3, "Started three way call on channel %d\n", p->channel); - ast_verb(3, "Started three way call on channel %d\n", p->channel); - - /* Start music on hold if appropriate */ - if (ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) { - ast_queue_control_data(p->subs[SUB_THREEWAY].owner, AST_CONTROL_HOLD, - S_OR(p->mohsuggest, NULL), - !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); - } - p->subs[SUB_THREEWAY].needhold = 1; + /* Start music on hold if appropriate */ + if (ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) { + ast_queue_control_data(p->subs[SUB_THREEWAY].owner, AST_CONTROL_HOLD, + S_OR(p->mohsuggest, NULL), + !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); } + p->subs[SUB_THREEWAY].needhold = 1; } + } + } else { + /* Already have a 3 way call */ + if (p->subs[SUB_THREEWAY].inthreeway) { + /* Call is already up, drop the last person */ + ast_debug(1, "Got flash with three way call up, dropping last call on %d\n", p->channel); + /* If the primary call isn't answered yet, use it */ + if ((p->subs[SUB_REAL].owner->_state != AST_STATE_UP) && (p->subs[SUB_THREEWAY].owner->_state == AST_STATE_UP)) { + /* Swap back -- we're dropping the real 3-way that isn't finished yet*/ + swap_subs(p, SUB_THREEWAY, SUB_REAL); + p->owner = p->subs[SUB_REAL].owner; + } + /* Drop the last call and stop the conference */ + ast_verb(3, "Dropping three-way call on %s\n", p->subs[SUB_THREEWAY].owner->name); + p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV; + p->subs[SUB_REAL].inthreeway = 0; + p->subs[SUB_THREEWAY].inthreeway = 0; } else { - /* Already have a 3 way call */ - if (p->subs[SUB_THREEWAY].inthreeway) { - /* Call is already up, drop the last person */ - ast_debug(1, "Got flash with three way call up, dropping last call on %d\n", p->channel); - /* If the primary call isn't answered yet, use it */ - if ((p->subs[SUB_REAL].owner->_state != AST_STATE_UP) && (p->subs[SUB_THREEWAY].owner->_state == AST_STATE_UP)) { - /* Swap back -- we're dropping the real 3-way that isn't finished yet*/ - swap_subs(p, SUB_THREEWAY, SUB_REAL); - p->owner = p->subs[SUB_REAL].owner; - } - /* Drop the last call and stop the conference */ - ast_verb(3, "Dropping three-way call on %s\n", p->subs[SUB_THREEWAY].owner->name); - p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV; - p->subs[SUB_REAL].inthreeway = 0; - p->subs[SUB_THREEWAY].inthreeway = 0; - } else { - /* Lets see what we're up to */ - if (((ast->pbx) || (ast->_state == AST_STATE_UP)) && - (p->transfertobusy || (ast->_state != AST_STATE_BUSY))) { - int otherindex = SUB_THREEWAY; - struct ast_channel *other = ast_bridged_channel(p->subs[SUB_THREEWAY].owner); - int way3bridge = 0, cdr3way = 0; + /* Lets see what we're up to */ + if (((ast->pbx) || (ast->_state == AST_STATE_UP)) && + (p->transfertobusy || (ast->_state != AST_STATE_BUSY))) { + int otherindex = SUB_THREEWAY; + struct ast_channel *other = ast_bridged_channel(p->subs[SUB_THREEWAY].owner); + int way3bridge = 0, cdr3way = 0; - if (!other) { - other = ast_bridged_channel(p->subs[SUB_REAL].owner); - } else - way3bridge = 1; + if (!other) { + other = ast_bridged_channel(p->subs[SUB_REAL].owner); + } else + way3bridge = 1; - if (p->subs[SUB_THREEWAY].owner->cdr) - cdr3way = 1; + if (p->subs[SUB_THREEWAY].owner->cdr) + cdr3way = 1; - ast_verb(3, "Building conference on call on %s and %s\n", p->subs[SUB_THREEWAY].owner->name, p->subs[SUB_REAL].owner->name); - /* Put them in the threeway, and flip */ - p->subs[SUB_THREEWAY].inthreeway = 1; - p->subs[SUB_REAL].inthreeway = 1; - if (ast->_state == AST_STATE_UP) { - swap_subs(p, SUB_THREEWAY, SUB_REAL); - otherindex = SUB_REAL; - } - if (p->subs[otherindex].owner && ast_bridged_channel(p->subs[otherindex].owner)) - ast_queue_control(p->subs[otherindex].owner, AST_CONTROL_UNHOLD); - p->subs[otherindex].needunhold = 1; - p->owner = p->subs[SUB_REAL].owner; - if (ast->_state == AST_STATE_RINGING) { - ast_debug(1, "Enabling ringtone on real and threeway\n"); - res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE); - res = tone_zone_play_tone(p->subs[SUB_THREEWAY].dfd, DAHDI_TONE_RINGTONE); - } - } else { - ast_verb(3, "Dumping incomplete call on on %s\n", p->subs[SUB_THREEWAY].owner->name); + ast_verb(3, "Building conference on call on %s and %s\n", p->subs[SUB_THREEWAY].owner->name, p->subs[SUB_REAL].owner->name); + /* Put them in the threeway, and flip */ + p->subs[SUB_THREEWAY].inthreeway = 1; + p->subs[SUB_REAL].inthreeway = 1; + if (ast->_state == AST_STATE_UP) { swap_subs(p, SUB_THREEWAY, SUB_REAL); - p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV; - p->owner = p->subs[SUB_REAL].owner; - if (p->subs[SUB_REAL].owner && ast_bridged_channel(p->subs[SUB_REAL].owner)) - ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_UNHOLD); - p->subs[SUB_REAL].needunhold = 1; - dahdi_enable_ec(p); + otherindex = SUB_REAL; } + if (p->subs[otherindex].owner && ast_bridged_channel(p->subs[otherindex].owner)) + ast_queue_control(p->subs[otherindex].owner, AST_CONTROL_UNHOLD); + p->subs[otherindex].needunhold = 1; + p->owner = p->subs[SUB_REAL].owner; + if (ast->_state == AST_STATE_RINGING) { + ast_debug(1, "Enabling ringtone on real and threeway\n"); + res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE); + res = tone_zone_play_tone(p->subs[SUB_THREEWAY].dfd, DAHDI_TONE_RINGTONE); + } + } else { + ast_verb(3, "Dumping incomplete call on on %s\n", p->subs[SUB_THREEWAY].owner->name); + swap_subs(p, SUB_THREEWAY, SUB_REAL); + p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV; + p->owner = p->subs[SUB_REAL].owner; + if (p->subs[SUB_REAL].owner && ast_bridged_channel(p->subs[SUB_REAL].owner)) + ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_UNHOLD); + p->subs[SUB_REAL].needunhold = 1; + dahdi_enable_ec(p); } } + } winkflashdone: - update_conf(p); - break; - case SIG_EM: - case SIG_EM_E1: - case SIG_EMWINK: - case SIG_FEATD: - case SIG_SF: - case SIG_SFWINK: - case SIG_SF_FEATD: - case SIG_FXSLS: - case SIG_FXSGS: - if (option_debug) { - if (p->dialing) - ast_debug(1, "Ignoring wink on channel %d\n", p->channel); - else - ast_debug(1, "Got wink in weird state %d on channel %d\n", ast->_state, p->channel); - } - break; - case SIG_FEATDMF_TA: - switch (p->whichwink) { - case 0: - ast_debug(1, "ANI2 set to '%d' and ANI is '%s'\n", p->owner->cid.cid_ani2, p->owner->cid.cid_ani); - snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*%d%s#", p->owner->cid.cid_ani2, p->owner->cid.cid_ani); - break; - case 1: - ast_copy_string(p->dop.dialstr, p->finaldial, sizeof(p->dop.dialstr)); - break; - case 2: - ast_log(LOG_WARNING, "Received unexpected wink on channel of type SIG_FEATDMF_TA\n"); - return NULL; - } - p->whichwink++; - /* Fall through */ - case SIG_FEATDMF: - case SIG_E911: - case SIG_FGC_CAMAMF: - case SIG_FGC_CAMA: - case SIG_FEATB: - case SIG_SF_FEATDMF: - case SIG_SF_FEATB: - /* FGD MF *Must* wait for wink */ - if (!ast_strlen_zero(p->dop.dialstr)) { - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop); - if (res < 0) { - ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", p->channel, strerror(errno)); - p->dop.dialstr[0] = '\0'; - return NULL; - } else - ast_debug(1, "Sent deferred digit string: %s\n", p->dop.dialstr); - } - p->dop.dialstr[0] = '\0'; - break; - default: - ast_log(LOG_WARNING, "Don't know how to handle ring/off hoook for signalling %d\n", p->sig); - } + update_conf(p); break; - case DAHDI_EVENT_HOOKCOMPLETE: - if (p->inalarm) break; - if ((p->radio || (p->oprmode < 0))) break; - if (p->waitingfordt.tv_sec) break; - switch (mysig) { - case SIG_FXSLS: /* only interesting for FXS */ - case SIG_FXSGS: - case SIG_FXSKS: - case SIG_EM: - case SIG_EM_E1: - case SIG_EMWINK: - case SIG_FEATD: - case SIG_SF: - case SIG_SFWINK: - case SIG_SF_FEATD: - if (!ast_strlen_zero(p->dop.dialstr)) { - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop); - if (res < 0) { - ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", p->channel, strerror(errno)); - p->dop.dialstr[0] = '\0'; - return NULL; - } else - ast_debug(1, "Sent deferred digit string: %s\n", p->dop.dialstr); - } - p->dop.dialstr[0] = '\0'; - p->dop.op = DAHDI_DIAL_OP_REPLACE; + case SIG_EM: + case SIG_EM_E1: + case SIG_EMWINK: + case SIG_FEATD: + case SIG_SF: + case SIG_SFWINK: + case SIG_SF_FEATD: + case SIG_FXSLS: + case SIG_FXSGS: + if (p->dialing) + ast_debug(1, "Ignoring wink on channel %d\n", p->channel); + else + ast_debug(1, "Got wink in weird state %d on channel %d\n", ast->_state, p->channel); + break; + case SIG_FEATDMF_TA: + switch (p->whichwink) { + case 0: + ast_debug(1, "ANI2 set to '%d' and ANI is '%s'\n", p->owner->cid.cid_ani2, p->owner->cid.cid_ani); + snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*%d%s#", p->owner->cid.cid_ani2, p->owner->cid.cid_ani); break; - case SIG_FEATDMF: - case SIG_FEATDMF_TA: - case SIG_E911: - case SIG_FGC_CAMA: - case SIG_FGC_CAMAMF: - case SIG_FEATB: - case SIG_SF_FEATDMF: - case SIG_SF_FEATB: - ast_debug(1, "Got hook complete in MF FGD, waiting for wink now on channel %d\n",p->channel); + case 1: + ast_copy_string(p->dop.dialstr, p->finaldial, sizeof(p->dop.dialstr)); break; - default: - break; + case 2: + ast_log(LOG_WARNING, "Received unexpected wink on channel of type SIG_FEATDMF_TA\n"); + return NULL; } - break; - case DAHDI_EVENT_POLARITY: - /* - * If we get a Polarity Switch event, check to see - * if we should change the polarity state and - * mark the channel as UP or if this is an indication - * of remote end disconnect. - */ - if (p->polarity == POLARITY_IDLE) { - p->polarity = POLARITY_REV; - if (p->answeronpolarityswitch && - ((ast->_state == AST_STATE_DIALING) || - (ast->_state == AST_STATE_RINGING))) { - ast_debug(1, "Answering on polarity switch!\n"); - ast_setstate(p->owner, AST_STATE_UP); - if (p->hanguponpolarityswitch) { - p->polaritydelaytv = ast_tvnow(); - } + p->whichwink++; + /* Fall through */ + case SIG_FEATDMF: + case SIG_E911: + case SIG_FGC_CAMAMF: + case SIG_FGC_CAMA: + case SIG_FEATB: + case SIG_SF_FEATDMF: + case SIG_SF_FEATB: + /* FGD MF *Must* wait for wink */ + if (!ast_strlen_zero(p->dop.dialstr)) { + res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop); + if (res < 0) { + ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", p->channel, strerror(errno)); + p->dop.dialstr[0] = '\0'; + return NULL; } else - ast_debug(1, "Ignore switch to REVERSED Polarity on channel %d, state %d\n", p->channel, ast->_state); + ast_debug(1, "Sent deferred digit string: %s\n", p->dop.dialstr); } - /* Removed else statement from here as it was preventing hangups from ever happening*/ - /* Added AST_STATE_RING in if statement below to deal with calling party hangups that take place when ringing */ - if (p->hanguponpolarityswitch && - (p->polarityonanswerdelay > 0) && - (p->polarity == POLARITY_REV) && - ((ast->_state == AST_STATE_UP) || (ast->_state == AST_STATE_RING)) ) { - /* Added log_debug information below to provide a better indication of what is going on */ - ast_debug(1, "Polarity Reversal event occured - DEBUG 1: channel %d, state %d, pol= %d, aonp= %d, honp= %d, pdelay= %d, tv= %d\n", p->channel, ast->_state, p->polarity, p->answeronpolarityswitch, p->hanguponpolarityswitch, p->polarityonanswerdelay, ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) ); - - if (ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) > p->polarityonanswerdelay) { - ast_debug(1, "Polarity Reversal detected and now Hanging up on channel %d\n", p->channel); - ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT); - p->polarity = POLARITY_IDLE; + p->dop.dialstr[0] = '\0'; + break; + default: + ast_log(LOG_WARNING, "Don't know how to handle ring/off hoook for signalling %d\n", p->sig); + } + break; + case DAHDI_EVENT_HOOKCOMPLETE: + if (p->inalarm) break; + if ((p->radio || (p->oprmode < 0))) break; + if (p->waitingfordt.tv_sec) break; + switch (mysig) { + case SIG_FXSLS: /* only interesting for FXS */ + case SIG_FXSGS: + case SIG_FXSKS: + case SIG_EM: + case SIG_EM_E1: + case SIG_EMWINK: + case SIG_FEATD: + case SIG_SF: + case SIG_SFWINK: + case SIG_SF_FEATD: + if (!ast_strlen_zero(p->dop.dialstr)) { + res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop); + if (res < 0) { + ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", p->channel, strerror(errno)); + p->dop.dialstr[0] = '\0'; + return NULL; } else - ast_debug(1, "Polarity Reversal detected but NOT hanging up (too close to answer event) on channel %d, state %d\n", p->channel, ast->_state); - - } else { - p->polarity = POLARITY_IDLE; - ast_debug(1, "Ignoring Polarity switch to IDLE on channel %d, state %d\n", p->channel, ast->_state); + ast_debug(1, "Sent deferred digit string: %s\n", p->dop.dialstr); } - /* Added more log_debug information below to provide a better indication of what is going on */ - ast_debug(1, "Polarity Reversal event occured - DEBUG 2: channel %d, state %d, pol= %d, aonp= %d, honp= %d, pdelay= %d, tv= %d\n", p->channel, ast->_state, p->polarity, p->answeronpolarityswitch, p->hanguponpolarityswitch, p->polarityonanswerdelay, ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) ); + p->dop.dialstr[0] = '\0'; + p->dop.op = DAHDI_DIAL_OP_REPLACE; break; + case SIG_FEATDMF: + case SIG_FEATDMF_TA: + case SIG_E911: + case SIG_FGC_CAMA: + case SIG_FGC_CAMAMF: + case SIG_FEATB: + case SIG_SF_FEATDMF: + case SIG_SF_FEATB: + ast_debug(1, "Got hook complete in MF FGD, waiting for wink now on channel %d\n",p->channel); + break; default: - ast_debug(1, "Dunno what to do with event %d on channel %d\n", res, p->channel); + break; + } + break; + case DAHDI_EVENT_POLARITY: + /* + * If we get a Polarity Switch event, check to see + * if we should change the polarity state and + * mark the channel as UP or if this is an indication + * of remote end disconnect. + */ + if (p->polarity == POLARITY_IDLE) { + p->polarity = POLARITY_REV; + if (p->answeronpolarityswitch && + ((ast->_state == AST_STATE_DIALING) || + (ast->_state == AST_STATE_RINGING))) { + ast_debug(1, "Answering on polarity switch!\n"); + ast_setstate(p->owner, AST_STATE_UP); + if (p->hanguponpolarityswitch) { + p->polaritydelaytv = ast_tvnow(); + } + } else + ast_debug(1, "Ignore switch to REVERSED Polarity on channel %d, state %d\n", p->channel, ast->_state); + } + /* Removed else statement from here as it was preventing hangups from ever happening*/ + /* Added AST_STATE_RING in if statement below to deal with calling party hangups that take place when ringing */ + if (p->hanguponpolarityswitch && + (p->polarityonanswerdelay > 0) && + (p->polarity == POLARITY_REV) && + ((ast->_state == AST_STATE_UP) || (ast->_state == AST_STATE_RING)) ) { + /* Added log_debug information below to provide a better indication of what is going on */ + ast_debug(1, "Polarity Reversal event occured - DEBUG 1: channel %d, state %d, pol= %d, aonp= %d, honp= %d, pdelay= %d, tv= %d\n", p->channel, ast->_state, p->polarity, p->answeronpolarityswitch, p->hanguponpolarityswitch, p->polarityonanswerdelay, ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) ); + + if (ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) > p->polarityonanswerdelay) { + ast_debug(1, "Polarity Reversal detected and now Hanging up on channel %d\n", p->channel); + ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT); + p->polarity = POLARITY_IDLE; + } else + ast_debug(1, "Polarity Reversal detected but NOT hanging up (too close to answer event) on channel %d, state %d\n", p->channel, ast->_state); + + } else { + p->polarity = POLARITY_IDLE; + ast_debug(1, "Ignoring Polarity switch to IDLE on channel %d, state %d\n", p->channel, ast->_state); + } + /* Added more log_debug information below to provide a better indication of what is going on */ + ast_debug(1, "Polarity Reversal event occured - DEBUG 2: channel %d, state %d, pol= %d, aonp= %d, honp= %d, pdelay= %d, tv= %d\n", p->channel, ast->_state, p->polarity, p->answeronpolarityswitch, p->hanguponpolarityswitch, p->polarityonanswerdelay, ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) ); + break; + default: + ast_debug(1, "Dunno what to do with event %d on channel %d\n", res, p->channel); } return &p->subs[idx].f; } @@ -6651,7 +7904,12 @@ struct dahdi_pvt *p = ast->tech_pvt; struct ast_frame *f; ast_mutex_lock(&p->lock); - f = __dahdi_exception(ast); + if (analog_lib_handles(p->sig, p->radio, p->oprmode)) { + struct analog_pvt *analog_p = p->sig_pvt; + f = analog_exception(analog_p, ast); + } else { + f = __dahdi_exception(ast); + } ast_mutex_unlock(&p->lock); return f; } @@ -6899,7 +8157,7 @@ p->subs[idx].f.datalen = READ_SIZE; /* Handle CallerID Transmission */ - if ((p->owner == ast) && p->cidspill &&((ast->_state == AST_STATE_UP) || (ast->rings == p->cidrings))) { + if ((p->owner == ast) && p->cidspill) { send_callerid(p); } @@ -6912,7 +8170,9 @@ #if 0 ast_debug(1, "Read %d of voice on %s\n", p->subs[idx].f.datalen, ast->name); #endif - if (p->dialing || /* Transmitting something */ + { + struct analog_pvt *ap = p->sig_pvt; + if ((analog_lib_handles(p->sig ,p->radio, p->oprmode) && ap->dialing) || p->dialing || p->radio || /* Transmitting something */ (idx && (ast->_state != AST_STATE_UP)) || /* Three-way or callwait that isn't up */ ((idx == SUB_CALLWAIT) && !p->subs[SUB_CALLWAIT].inthreeway) /* Inactive and non-confed call-wait */ ) { @@ -6926,6 +8186,7 @@ p->subs[idx].f.data.ptr = NULL; p->subs[idx].f.datalen= 0; } + } if (p->dsp && (!p->ignoredtmf || p->callwaitcas || p->busydetect || p->callprogress || p->waitingfordt.tv_sec) && !idx) { /* Perform busy detection etc on the dahdi line */ int mute; @@ -6992,8 +8253,12 @@ } else f = &p->subs[idx].f; - if (f && (f->frametype == AST_FRAME_DTMF)) - dahdi_handle_dtmfup(ast, idx, &f); + if (f && (f->frametype == AST_FRAME_DTMF)) { + if (analog_lib_handles(p->sig, p->radio, p->oprmode)) { + analog_handle_dtmfup(p->sig_pvt, ast, idx, &f); + } else + dahdi_handle_dtmfup(ast, idx, &f); + } /* If we have a fake_event, trigger exception to handle it */ if (p->fake_event) @@ -7064,6 +8329,12 @@ ast_log(LOG_WARNING, "Cannot handle frames in %d format\n", frame->subclass); return -1; } + if (analog_lib_handles(p->sig, p->radio, p->oprmode)) { + struct analog_pvt *ap = p->sig_pvt; + + if (ap->dialing) + return 0; + } if (p->dialing) { ast_debug(1, "Dropping frame since I'm still dialing on %s...\n",ast->name); return 0; @@ -7610,7 +8881,7 @@ on? "enabled" : "disabled"); } -static void *ss_thread(void *data) +static void *analog_ss_thread(void *data) { struct ast_channel *chan = data; struct dahdi_pvt *p = chan->tech_pvt; @@ -8769,25 +10040,9 @@ if (cs) callerid_free(cs); - /* If the CID had Message waiting payload, assume that this for MWI only and hangup the call */ - if (flags & CID_MSGWAITING) { - ast_log(LOG_NOTICE, "MWI: Channel %d message waiting!\n", p->channel); - notify_message(p->mailbox, 1); - /* If generated using Ring Pulse Alert, then ring has been answered as a call and needs to be hungup */ - if (p->mwimonitor_rpas) { - ast_hangup(chan); - return NULL; - } - } else if (flags & CID_NOMSGWAITING) { - ast_log(LOG_NOTICE, "MWI: Channel %d no message waiting!\n", p->channel); - notify_message(p->mailbox, 0); - /* If generated using Ring Pulse Alert, then ring has been answered as a call and needs to be hungup */ - if (p->mwimonitor_rpas) { - ast_hangup(chan); - return NULL; - } - } + my_handle_notify_message(chan, p, flags, -1); + ast_setstate(chan, AST_STATE_RING); chan->rings = 1; p->ringt = p->ringt_base; @@ -8890,14 +10145,20 @@ handle_alarms(mtd->pvt, res); break; /* What to do on channel alarm ???? -- fall thru intentionally?? */ default: - ast_log(LOG_NOTICE, "Got event %d (%s)... Passing along to ss_thread\n", res, event2str(res)); + ast_log(LOG_NOTICE, "Got event %d (%s)... Passing along to analog_ss_thread\n", res, event2str(res)); callerid_free(cs); restore_gains(mtd->pvt); mtd->pvt->ringt = mtd->pvt->ringt_base; if ((chan = dahdi_new(mtd->pvt, AST_STATE_RING, 0, SUB_REAL, 0, 0))) { - if (ast_pthread_create_detached(&threadid, NULL, ss_thread, chan)) { + int result; + if (analog_lib_handles(mtd->pvt->sig, mtd->pvt->radio, mtd->pvt->oprmode)) { + result = analog_ss_thread_start(mtd->pvt->sig_pvt, chan); + } else { + result = ast_pthread_create_detached(&threadid, NULL, analog_ss_thread, chan); + } + if (result) { ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", mtd->pvt->channel); res = tone_zone_play_tone(mtd->pvt->subs[SUB_REAL].dfd, DAHDI_TONE_CONGESTION); if (res < 0) @@ -9017,8 +10278,8 @@ #ifdef HAVE_DAHDI_LINEREVERSE_VMWI if (pvt->mwisend_fsk) { #endif - pvt->cidlen = vmwi_generate(pvt->cidspill, has_voicemail(pvt), CID_MWI_TYPE_MDMF_FULL, - AST_LAW(pvt), pvt->cid_name, pvt->cid_num, 0); + pvt->cidlen = ast_callerid_vmwi_generate(pvt->cidspill, has_voicemail(pvt), CID_MWI_TYPE_MDMF_FULL, + AST_LAW(pvt), pvt->cid_name, pvt->cid_num, 0); pvt->cidpos = 0; #ifdef HAVE_DAHDI_LINEREVERSE_VMWI } @@ -9168,7 +10429,7 @@ while (tmp) { if (tmp->channel == channel) { int x = DAHDI_FLASH; - ioctl(tmp->subs[SUB_REAL].dfd, DAHDI_HOOK, &x); /* important to create an event for dahdi_wait_event to register so that all ss_threads terminate */ + ioctl(tmp->subs[SUB_REAL].dfd, DAHDI_HOOK, &x); /* important to create an event for dahdi_wait_event to register so that all analog_ss_threads terminate */ destroy_channel(prev, tmp, 1); ast_module_unref(ast_module_info->self); return RESULT_SUCCESS; @@ -9232,7 +10493,7 @@ res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, DAHDI_TONE_DIALTONE); if (res < 0) ast_log(LOG_WARNING, "Unable to play dialtone on channel %d, do you have defaultzone and loadzone defined?\n", i->channel); - if (ast_pthread_create_detached(&threadid, NULL, ss_thread, chan)) { + if (ast_pthread_create_detached(&threadid, NULL, analog_ss_thread, chan)) { ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel); res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, DAHDI_TONE_CONGESTION); if (res < 0) @@ -9274,7 +10535,7 @@ if (!chan) { ast_log(LOG_WARNING, "Cannot allocate new structure on channel %d\n", i->channel); - } else if (ast_pthread_create_detached(&threadid, NULL, ss_thread, chan)) { + } else if (ast_pthread_create_detached(&threadid, NULL, analog_ss_thread, chan)) { ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel); res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, DAHDI_TONE_CONGESTION); if (res < 0) { @@ -9380,7 +10641,7 @@ chan = dahdi_new(i, AST_STATE_PRERING, 0, SUB_REAL, 0, 0); if (!chan) { ast_log(LOG_WARNING, "Cannot allocate new structure on channel %d\n", i->channel); - } else if (ast_pthread_create_detached(&threadid, NULL, ss_thread, chan)) { + } else if (ast_pthread_create_detached(&threadid, NULL, analog_ss_thread, chan)) { ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel); } else { thread_spawned = 1; @@ -9458,19 +10719,39 @@ count = 0; i = iflist; while (i) { + ast_mutex_lock(&i->lock); if ((i->subs[SUB_REAL].dfd > -1) && i->sig && (!i->radio) && !(i->sig & SIG_MFCR2)) { - if (!i->owner && !i->subs[SUB_REAL].owner && !i->mwimonitoractive ) { - /* This needs to be watched, as it lacks an owner */ - pfds[count].fd = i->subs[SUB_REAL].dfd; - pfds[count].events = POLLPRI; - pfds[count].revents = 0; - /* If we are monitoring for VMWI or sending CID, we need to - read from the channel as well */ - if (i->cidspill || i->mwisendactive || i->mwimonitor_fsk) - pfds[count].events |= POLLIN; - count++; + if (analog_lib_handles(i->sig, i->radio, i->oprmode)) { + struct analog_pvt *p = i->sig_pvt; + + if (!p) + ast_log(LOG_ERROR, "No sig_pvt?\n"); + + if (!p->owner && !p->subs[SUB_REAL].owner) { + /* This needs to be watched, as it lacks an owner */ + pfds[count].fd = i->subs[SUB_REAL].dfd; + pfds[count].events = POLLPRI; + pfds[count].revents = 0; + /* Message waiting or r2 channels also get watched for reading */ + if (i->cidspill || i->mwisendactive || i->mwimonitor_fsk) + pfds[count].events |= POLLIN; + count++; + } + } else { + if (!i->owner && !i->subs[SUB_REAL].owner && !i->mwimonitoractive ) { + /* This needs to be watched, as it lacks an owner */ + pfds[count].fd = i->subs[SUB_REAL].dfd; + pfds[count].events = POLLPRI; + pfds[count].revents = 0; + /* If we are monitoring for VMWI or sending CID, we need to + read from the channel as well */ + if (i->cidspill || i->mwisendactive || i->mwimonitor_fsk) + pfds[count].events |= POLLIN; + count++; + } } } + ast_mutex_unlock(&i->lock); i = i->next; } /* Okay, now that we know what to do, release the interface lock */ @@ -9535,7 +10816,10 @@ ast_debug(1, "Monitor doohicky got event %s on radio channel %d\n", event2str(res), i->channel); /* Don't hold iflock while handling init events */ ast_mutex_unlock(&iflock); - handle_init_event(i, res); + if (analog_lib_handles(i->sig, i->radio, i->oprmode)) + analog_handle_init_event(i->sig_pvt, dahdievent_to_analogevent(res)); + else + handle_init_event(i, res); ast_mutex_lock(&iflock); } i = i->next; @@ -9601,7 +10885,10 @@ /* Don't hold iflock while handling init events */ ast_mutex_unlock(&iflock); if (0 == i->mwisendactive || 0 == mwi_send_process_event(i, res)) { - handle_init_event(i, res); + if (analog_lib_handles(i->sig, i->radio, i->oprmode)) + analog_handle_init_event(i->sig_pvt, dahdievent_to_analogevent(res)); + else + handle_init_event(i, res); } ast_mutex_lock(&iflock); } @@ -9855,6 +11142,9 @@ } openr2_context_set_log_level(r2_link->protocol_context, conf->mfcr2.loglevel); openr2_context_set_ani_first(r2_link->protocol_context, conf->mfcr2.get_ani_first); +#if defined(OR2_LIB_INTERFACE) && OR2_LIB_INTERFACE > 1 + openr2_context_set_skip_category_request(r2_link->protocol_context, conf->mfcr2.skip_category_request); +#endif openr2_context_set_mf_threshold(r2_link->protocol_context, threshold); openr2_context_set_mf_back_timeout(r2_link->protocol_context, conf->mfcr2.mfback_timeout); openr2_context_set_metering_pulse_timeout(r2_link->protocol_context, conf->mfcr2.metering_pulse_timeout); @@ -9912,6 +11202,7 @@ int x; struct dahdi_pvt **wlist; struct dahdi_pvt **wend; + struct analog_pvt *analog_p = NULL; struct dahdi_params p; wlist = &iflist; @@ -9944,8 +11235,6 @@ if (!here && reloading != 1) { if (!(tmp = ast_calloc(1, sizeof(*tmp)))) { - if (tmp) - free(tmp); return NULL; } ast_mutex_init(&tmp->lock); @@ -9999,6 +11288,20 @@ return NULL; } } + + if (analog_lib_handles(chan_sig, tmp->radio, tmp->oprmode)) { + if (tmp->sig_pvt) { + ast_log(LOG_WARNING, "Private already exists!\n"); + analog_p = tmp->sig_pvt; + } else + analog_p = analog_new(dahdisig_to_analogsig(chan_sig), &dahdi_analog_callbacks, tmp); + if (!analog_p) { + destroy_dahdi_pvt(&tmp); + return NULL; + } + tmp->sig_pvt = analog_p; + + } #ifdef HAVE_SS7 if (chan_sig == SIG_SS7) { struct dahdi_ss7 *ss7; @@ -10063,7 +11366,8 @@ tmp->subs[SUB_REAL].dfd, NULL, NULL); if (!tmp->r2chan) { - ast_log(LOG_ERROR, "Cannot create OpenR2 channel.\n"); + openr2_liberr_t err = openr2_context_get_last_error(r2_link->protocol_context); + ast_log(LOG_ERROR, "Cannot create OpenR2 channel: %s\n", openr2_context_error_string(err)); destroy_dahdi_pvt(&tmp); return NULL; } @@ -10135,7 +11439,6 @@ } } } - offset = p.chanpos; if (!matchesdchan) { if (pris[span].nodetype && (pris[span].nodetype != conf->pri.nodetype)) { ast_log(LOG_ERROR, "Span %d is already a %s node\n", span + 1, pri_node2str(pris[span].nodetype)); @@ -10191,6 +11494,9 @@ pris[span].overlapdial = conf->pri.overlapdial; pris[span].qsigchannelmapping = conf->pri.qsigchannelmapping; pris[span].discardremoteholdretrieval = conf->pri.discardremoteholdretrieval; +#ifdef HAVE_PRI_SERVICE_MESSAGES + pris[span].enable_service_message_support = conf->pri.enable_service_message_support; +#endif #ifdef HAVE_PRI_INBANDDISCONNECT pris[span].inbanddisconnect = conf->pri.inbanddisconnect; #endif @@ -10205,7 +11511,11 @@ pris[span].resetinterval = conf->pri.resetinterval; tmp->pri = &pris[span]; - tmp->prioffset = offset; + if (si.spanno != span + 1) { /* in another trunkgroup */ + tmp->prioffset = pris[span].numchans; + } else { + tmp->prioffset = p.chanpos; + } tmp->call = NULL; } else { ast_log(LOG_ERROR, "Channel %d is reserved for D-channel.\n", offset); @@ -10484,11 +11794,57 @@ tmp->sendcalleridafter = conf->chan.sendcalleridafter; if (!here) { tmp->locallyblocked = tmp->remotelyblocked = 0; - if ((chan_sig == SIG_PRI) || (chan_sig == SIG_BRI) || (chan_sig == SIG_BRI_PTMP) || (chan_sig == SIG_SS7)) + if ((chan_sig == SIG_PRI) || (chan_sig == SIG_BRI) || (chan_sig == SIG_BRI_PTMP) || (chan_sig == SIG_SS7)) { tmp->inservice = 0; - else /* We default to in service on protocols that don't have a reset */ +#ifdef HAVE_PRI_SERVICE_MESSAGES + if (chan_sig == SIG_PRI) { + char db_chan_name[20], db_answer[5]; + + snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, tmp->span, tmp->channel); + if (ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) { + snprintf(db_answer, sizeof(db_answer), "%s:%d", SRVST_TYPE_OOS, SRVST_INITIALIZED); + ast_db_put(db_chan_name, SRVST_DBKEY, db_answer); + } + } +#endif + } else { + /* We default to in service on protocols that don't have a reset */ tmp->inservice = 1; + } } + + analog_p = tmp->sig_pvt; + if (analog_p) { + analog_p->channel = tmp->channel; + analog_p->polarityonanswerdelay = conf->chan.polarityonanswerdelay; + analog_p->answeronpolarityswitch = conf->chan.answeronpolarityswitch; + analog_p->hanguponpolarityswitch = conf->chan.hanguponpolarityswitch; + analog_p->sendcalleridafter = conf->chan.sendcalleridafter; + analog_p->permcallwaiting = 1; + analog_p->callreturn = conf->chan.callreturn; + analog_p->cancallforward = conf->chan.cancallforward; + analog_p->canpark = conf->chan.canpark; + analog_p->dahditrcallerid = conf->chan.dahditrcallerid; + analog_p->immediate = conf->chan.immediate; + analog_p->permhidecallerid = conf->chan.permhidecallerid; + analog_p->pulse = conf->chan.pulse; + analog_p->threewaycalling = conf->chan.threewaycalling; + analog_p->transfer = conf->chan.transfer; + analog_p->transfertobusy = conf->chan.transfertobusy; + analog_p->use_callerid = conf->chan.use_callerid; + analog_p->outsigmod = ANALOG_SIG_NONE; + analog_p->echotraining = conf->chan.echotraining; + analog_p->cid_signalling = conf->chan.cid_signalling; + analog_p->stripmsd = conf->chan.stripmsd; + analog_p->cid_start = ANALOG_CID_START_RING; + tmp->callwaitingcallerid = analog_p->callwaitingcallerid = 1; + + ast_copy_string(analog_p->mohsuggest, conf->chan.mohsuggest, sizeof(analog_p->mohsuggest)); + ast_copy_string(analog_p->cid_num, conf->chan.cid_num, sizeof(analog_p->cid_num)); + ast_copy_string(analog_p->cid_name, conf->chan.cid_name, sizeof(analog_p->cid_name)); + + analog_config_complete(analog_p); + } } if (tmp && !here) { /* nothing on the iflist */ @@ -10537,7 +11893,7 @@ return tmp; } -static inline int available(struct dahdi_pvt *p, int channelmatch, ast_group_t groupmatch, int *busy, int *channelmatched, int *groupmatched) +static inline int available(struct dahdi_pvt *p, int channelmatch, ast_group_t groupmatch, int *reason, int *channelmatched, int *groupmatched) { int res; struct dahdi_params par; @@ -10554,10 +11910,14 @@ return 0; *channelmatched = 1; } + + if (analog_lib_handles(p->sig, p->radio, p->oprmode)) + return analog_available(p->sig_pvt, channelmatch, groupmatch, reason, channelmatched, groupmatched); + /* We're at least busy at this point */ - if (busy) { + if (reason) { if ((p->sig == SIG_FXOKS) || (p->sig == SIG_FXOLS) || (p->sig == SIG_FXOGS)) - *busy = 1; + *reason = AST_CAUSE_BUSY; } /* If do not disturb, definitely not */ if (p->dnd) @@ -10574,10 +11934,25 @@ #ifdef HAVE_PRI /* Trust PRI */ if (p->pri) { - if (p->resetting || p->call) +#ifdef HAVE_PRI_SERVICE_MESSAGES + char db_chan_name[20], db_answer[5], state; + int why = 0; + + snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, p->span, p->channel); + if (!ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) { + sscanf(db_answer, "%c:%d", &state, &why); + } + if ((p->resetting || p->call) || (why)) { + if (why) { + *reason = AST_CAUSE_REQUESTED_CHAN_UNAVAIL; + } +#else + if (p->resetting || p->call) { +#endif return 0; - else + } else { return 1; + } } #endif #ifdef HAVE_SS7 @@ -10737,7 +12112,7 @@ int channelmatch = -1; int roundrobin = 0; int callwait = 0; - int busy = 0; + int unavailreason = 0; struct dahdi_pvt *p; struct ast_channel *tmp = NULL; char *dest=NULL; @@ -10866,7 +12241,7 @@ ast_verbose("name = %s, %d, %d, %d\n",p->owner ? p->owner->name : "", p->channel, channelmatch, groupmatch); #endif - if (p && available(p, channelmatch, groupmatch, &busy, &channelmatched, &groupmatched)) { + if (p && available(p, channelmatch, groupmatch, &unavailreason, &channelmatched, &groupmatched)) { ast_debug(1, "Using channel %d\n", p->channel); if (p->inalarm) goto next; @@ -10922,7 +12297,10 @@ } } p->outgoing = 1; - tmp = dahdi_new(p, AST_STATE_RESERVED, 0, p->owner ? SUB_CALLWAIT : SUB_REAL, 0, 0); + if (analog_lib_handles(p->sig, p->radio, p->oprmode)) { + tmp = analog_request(p->sig_pvt, &callwait); + } else + tmp = dahdi_new(p, AST_STATE_RESERVED, 0, p->owner ? SUB_CALLWAIT : SUB_REAL, 0, 0); #ifdef HAVE_PRI if (p->bearer) { /* Log owner to bearer channel, too */ @@ -10974,10 +12352,10 @@ *cause = AST_CAUSE_BUSY; else if (!tmp) { if (channelmatched) { - if (busy) + if (unavailreason) *cause = AST_CAUSE_BUSY; } else if (groupmatched) { - *cause = AST_CAUSE_CONGESTION; + *cause = (unavailreason) ? unavailreason : AST_CAUSE_CONGESTION; } } @@ -10987,7 +12365,11 @@ #if defined(HAVE_PRI) || defined(HAVE_SS7) static int dahdi_setlaw(int dfd, int law) { - return ioctl(dfd, DAHDI_SETLAW, &law); + int res; + res = ioctl(dfd, DAHDI_SETLAW, &law); + if (res) + return res; + return 0; } #endif /* defined(HAVE_PRI) || defined(HAVE_SS7) */ @@ -12002,9 +13384,13 @@ new->owner = old->owner; old->owner = NULL; if (new->owner) { - ast_string_field_build(new->owner, name, - "DAHDI/%d:%d-%d", pri->trunkgroup, - new->channel, 1); + char newname[AST_CHANNEL_NAME]; + + snprintf(newname, sizeof(newname), + "DAHDI/%d:%d-%d", pri->trunkgroup, new->channel, 1); + + ast_change_name(new->owner, newname); + new->owner->tech_pvt = new; ast_channel_set_fd(new->owner, 0, new->subs[SUB_REAL].dfd); new->subs[SUB_REAL].owner = old->subs[SUB_REAL].owner; @@ -12190,6 +13576,9 @@ #if defined(HAVE_PRI) static int pri_check_restart(struct dahdi_pri *pri) { +#ifdef HAVE_PRI_SERVICE_MESSAGES +tryanotherpos: +#endif do { pri->resetpos++; } while ((pri->resetpos < pri->numchans) && @@ -12197,6 +13586,26 @@ pri->pvts[pri->resetpos]->call || pri->pvts[pri->resetpos]->resetting)); if (pri->resetpos < pri->numchans) { +#ifdef HAVE_PRI_SERVICE_MESSAGES + char db_chan_name[20], db_answer[5], state; + int why; + + /* check if the channel is out of service */ + ast_mutex_lock(&pri->pvts[pri->resetpos]->lock); + snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, pri->pvts[pri->resetpos]->span, pri->pvts[pri->resetpos]->channel); + ast_mutex_unlock(&pri->pvts[pri->resetpos]->lock); + + /* if so, try next channel */ + if (!ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) { + sscanf(db_answer, "%c:%d", &state, &why); + if (why) { + ast_log(LOG_NOTICE, "span '%d' channel '%d' out-of-service (reason: %s), not sending RESTART\n", pri->span, + pri->pvts[pri->resetpos]->channel, (why & SRVST_FAREND) ? (why & SRVST_NEAREND) ? "both ends" : "far end" : "near end"); + goto tryanotherpos; + } + } +#endif + /* Mark the channel as resetting and restart it */ pri->pvts[pri->resetpos]->resetting = 1; pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[pri->resetpos])); @@ -12581,13 +13990,36 @@ ast_log(LOG_WARNING, "Restart requested on odd/unavailable channel number %d/%d on span %d\n", PRI_SPAN(e->restart.channel), PRI_CHANNEL(e->restart.channel), pri->span); else { - ast_verb(3, "B-channel %d/%d restarted on span %d\n", - PRI_SPAN(e->restart.channel), PRI_CHANNEL(e->restart.channel), pri->span); +#ifdef HAVE_PRI_SERVICE_MESSAGES + char db_chan_name[20], db_answer[5], state; + int why, skipit = 0; + ast_mutex_lock(&pri->pvts[chanpos]->lock); - if (pri->pvts[chanpos]->call) { - pri_destroycall(pri->pri, pri->pvts[chanpos]->call); - pri->pvts[chanpos]->call = NULL; + snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, pri->pvts[chanpos]->span, pri->pvts[chanpos]->channel); + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + + if (!ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) { + sscanf(db_answer, "%c:%d", &state, &why); + if (why) { + ast_log(LOG_NOTICE, "span '%d' channel '%d' out-of-service (reason: %s), ignoring RESTART\n", pri->span, + e->restart.channel, (why & SRVST_FAREND) ? (why & SRVST_NEAREND) ? "both ends" : "far end" : "near end"); + skipit = 1; + } else { + ast_db_del(db_chan_name, SRVST_DBKEY); + } } + if (!skipit) { +#endif + ast_verb(3, "B-channel %d/%d restarted on span %d\n", + PRI_SPAN(e->restart.channel), PRI_CHANNEL(e->restart.channel), pri->span); + ast_mutex_lock(&pri->pvts[chanpos]->lock); + if (pri->pvts[chanpos]->call) { + pri_destroycall(pri->pri, pri->pvts[chanpos]->call); + pri->pvts[chanpos]->call = NULL; + } +#ifdef HAVE_PRI_SERVICE_MESSAGES + } +#endif /* Force soft hangup if appropriate */ if (pri->pvts[chanpos]->realcall) pri_hangup_all(pri->pvts[chanpos]->realcall, pri); @@ -12667,6 +14099,62 @@ } } break; +#ifdef HAVE_PRI_SERVICE_MESSAGES + case PRI_EVENT_SERVICE: + chanpos = pri_find_principle(pri, e->service.channel); + if (chanpos < 0) { + ast_log(LOG_WARNING, "Received service change status %d on unconfigured channel %d/%d span %d\n", + e->service_ack.changestatus, PRI_SPAN(e->service_ack.channel), PRI_CHANNEL(e->service_ack.channel), pri->span); + } else { + char db_chan_name[20], db_answer[5], state; + int ch, why = -1; + + ast_mutex_lock(&pri->pvts[chanpos]->lock); + ch = pri->pvts[chanpos]->channel; + ast_mutex_unlock(&pri->pvts[chanpos]->lock); + + snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, pri->pvts[chanpos]->span, ch); + if (!ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) { + sscanf(db_answer, "%c:%d", &state, &why); + ast_db_del(db_chan_name, SRVST_DBKEY); + } + switch (e->service.changestatus) { + case 0: /* in-service */ + if (why > -1) { + if (why & SRVST_NEAREND) { + snprintf(db_answer, sizeof(db_answer), "%s:%d", SRVST_TYPE_OOS, SRVST_NEAREND); + ast_db_put(db_chan_name, SRVST_DBKEY, db_answer); + ast_debug(2, "channel '%d' service state { near: out-of-service, far: in-service }\n", ch); + } + } + break; + case 2: /* out-of-service */ + if (why == -1) { + why = SRVST_FAREND; + } else { + why |= SRVST_FAREND; + } + snprintf(db_answer, sizeof(db_answer), "%s:%d", SRVST_TYPE_OOS, why); + ast_db_put(db_chan_name, SRVST_DBKEY, db_answer); + break; + default: + ast_log(LOG_ERROR, "Huh? changestatus is: %d\n", e->service.changestatus); + } + ast_log(LOG_NOTICE, "Channel %d/%d span %d (logical: %d) received a change of service message, status '%d'\n", + PRI_SPAN(e->service.channel), PRI_CHANNEL(e->service.channel), pri->span, ch, e->service.changestatus); + } + break; + case PRI_EVENT_SERVICE_ACK: + chanpos = pri_find_principle(pri, e->service_ack.channel); + if (chanpos < 0) { + ast_log(LOG_WARNING, "Received service acknowledge change status '%d' on unconfigured channel %d/%d span %d\n", + e->service_ack.changestatus, PRI_SPAN(e->service_ack.channel), PRI_CHANNEL(e->service_ack.channel), pri->span); + } else { + ast_debug(2, "Channel %d/%d span %d received a change os service acknowledgement message, status '%d'\n", + PRI_SPAN(e->service_ack.channel), PRI_CHANNEL(e->service_ack.channel), pri->span, e->service_ack.changestatus); + } + break; +#endif case PRI_EVENT_RING: crv = NULL; if (e->ring.channel == -1) @@ -12842,7 +14330,7 @@ ast_mutex_lock(&pri->pvts[chanpos]->lock); ast_mutex_lock(&pri->lock); - if (c && !ast_pthread_create_detached(&threadid, NULL, ss_thread, c)) { + if (c && !ast_pthread_create_detached(&threadid, NULL, analog_ss_thread, c)) { ast_verb(3, "Accepting overlap call from '%s' to '%s' on channel %d/%d, span %d\n", plancallingnum, S_OR(pri->pvts[chanpos]->exten, ""), pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span); @@ -13464,6 +14952,11 @@ break; default: pri->dchans[i] = pri_new(pri->fds[i], pri->nodetype, pri->switchtype); +#ifdef HAVE_PRI_SERVICE_MESSAGES + if (pri->enable_service_message_support) { + pri_set_service_message_support(pri->dchans[i], 1); + } +#endif break; } /* Force overlap dial if we're doing GR-303! */ @@ -13638,6 +15131,149 @@ } #endif /* defined(HAVE_PRI) */ +#ifdef HAVE_PRI_SERVICE_MESSAGES +static char *handle_pri_service_generic(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a, int changestatus) +{ + int why; + int channel; + int trunkgroup; + int x, y, fd = a->fd; + int interfaceid = 0; + char *c; + char state; + char db_chan_name[20], db_answer[5]; + struct dahdi_pvt *start, *tmp = NULL; + struct dahdi_pri *pri = NULL; + ast_mutex_t *lock; + + lock = &iflock; + start = iflist; + + if (a->argc < 5 || a->argc > 6) + return CLI_SHOWUSAGE; + if ((c = strchr(a->argv[4], ':'))) { + if (sscanf(a->argv[4], "%d:%d", &trunkgroup, &channel) != 2) + return CLI_SHOWUSAGE; + if ((trunkgroup < 1) || (channel < 1)) + return CLI_SHOWUSAGE; + for (x=0;xcrvs; + lock = &pri->lock; + } else { + ast_cli(fd, "No such trunk group %d\n", trunkgroup); + return CLI_FAILURE; + } + } else + channel = atoi(a->argv[4]); + + if (a->argc == 6) + interfaceid = atoi(a->argv[5]); + + /* either servicing a D-Channel */ + for (x = 0; x < NUM_SPANS; x++) { + for (y = 0; y < NUM_DCHANS; y++) { + if (pris[x].dchannels[y] == channel) { + pri = pris + x; + pri_maintenance_service(pri->pri, interfaceid, -1, changestatus); + return CLI_SUCCESS; + } + } + } + + /* or servicing a B-Channel */ + ast_mutex_lock(lock); + tmp = start; + while (tmp) { + if (tmp->pri && tmp->channel == channel) { + if (!tmp->pri->enable_service_message_support) { + ast_cli(fd, "\n\tThis operation has not been enabled in chan_dahdi.conf, set 'service_message_support=yes' to use this operation.\n\tNote only 4ESS and 5ESS switch types are supported.\n\n"); + return CLI_SUCCESS; + } + why = -1; + snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, tmp->span, channel); + if (!ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) { + sscanf(db_answer, "%c:%d", &state, &why); + ast_db_del(db_chan_name, SRVST_DBKEY); + } + switch(changestatus) { + case 0: /* enable */ + if (why > -1) { + if (why & SRVST_FAREND) { + why = SRVST_FAREND; + snprintf(db_answer, sizeof(db_answer), "%s:%d", SRVST_TYPE_OOS, why); + ast_db_put(db_chan_name, SRVST_DBKEY, db_answer); + ast_debug(2, "channel '%d' service state { near: in-service, far: out-of-service }\n", channel); + } + } + break; + /* case 1: -- loop */ + case 2: /* disable */ + if (why == -1) { + why = SRVST_NEAREND; + } else { + why |= SRVST_NEAREND; + } + snprintf(db_answer, sizeof(db_answer), "%s:%d", SRVST_TYPE_OOS, why); + ast_db_put(db_chan_name, SRVST_DBKEY, db_answer); + break; + /* case 3: -- continuity */ + /* case 4: -- shutdown */ + default: + ast_log(LOG_WARNING, "Unsupported changestatus: '%d'\n", changestatus); + } + pri_maintenance_service(tmp->pri->pri, PRI_SPAN(PVT_TO_CHANNEL(tmp)), PVT_TO_CHANNEL(tmp), changestatus); + ast_mutex_unlock(lock); + return CLI_SUCCESS; + } + tmp = tmp->next; + } + ast_mutex_unlock(lock); + + ast_cli(fd, "Unable to find given channel %d, possibly not a PRI\n", channel); + return CLI_FAILURE; +} + +static char *handle_pri_service_enable_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "pri service enable channel"; + e->usage = + "Usage: pri service enable channel []\n" + " Send an AT&T / NFAS / CCS ANSI T1.607 maintenance message\n" + " to restore a channel to service, with optional interface id\n" + " as agreed upon with remote switch operator\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + return handle_pri_service_generic(e, cmd, a, 0); +} + +static char *handle_pri_service_disable_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "pri service disable channel"; + e->usage = + "Usage: pri service disable channel []\n" + " Send an AT&T / NFAS / CCS ANSI T1.607 maintenance message\n" + " to remove a channel from service, with optional interface id\n" + " as agreed upon with remote switch operator\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + return handle_pri_service_generic(e, cmd, a, 2); +} +#endif + #if defined(HAVE_PRI) static void build_status(char *s, size_t len, int status, int active) { @@ -13814,6 +15450,10 @@ #if defined(HAVE_PRI) static struct ast_cli_entry dahdi_pri_cli[] = { AST_CLI_DEFINE(handle_pri_debug, "Enables PRI debugging on a span"), +#ifdef HAVE_PRI_SERVICE_MESSAGES + AST_CLI_DEFINE(handle_pri_service_enable_channel, "Return a channel to service"), + AST_CLI_DEFINE(handle_pri_service_disable_channel, "Remove a channel from service"), +#endif AST_CLI_DEFINE(handle_pri_show_spans, "Displays PRI Information"), AST_CLI_DEFINE(handle_pri_show_span, "Displays PRI Information"), AST_CLI_DEFINE(handle_pri_show_debug, "Displays current PRI debug settings"), @@ -14259,14 +15899,14 @@ ast_mutex_lock(&ss_thread_lock); while (ss_thread_count > 0) { /* let ss_threads finish and run dahdi_hangup before dahvi_pvts are destroyed */ int x = DAHDI_FLASH; - ast_debug(3, "Waiting on %d ss_thread(s) to finish\n", ss_thread_count); + ast_debug(3, "Waiting on %d analog_ss_thread(s) to finish\n", ss_thread_count); for (p = iflist; p; p = p->next) { if (p->owner) - ioctl(p->subs[SUB_REAL].dfd, DAHDI_HOOK, &x); /* important to create an event for dahdi_wait_event to register so that all ss_threads terminate */ + ioctl(p->subs[SUB_REAL].dfd, DAHDI_HOOK, &x); /* important to create an event for dahdi_wait_event to register so that all analog_ss_threads terminate */ } - ast_cond_wait(&ss_thread_complete, &ss_thread_lock); - } + ast_cond_wait(&ss_thread_complete, &ss_thread_lock); + } /* ensure any created channels before monitor threads were stopped are hungup */ dahdi_softhangup_all(); @@ -14619,6 +16259,9 @@ ast_cli(a->fd, "MFC/R2 Max ANI: %d\n", openr2_context_get_max_ani(r2context)); ast_cli(a->fd, "MFC/R2 Max DNIS: %d\n", openr2_context_get_max_dnis(r2context)); ast_cli(a->fd, "MFC/R2 Get ANI First: %s\n", openr2_context_get_ani_first(r2context) ? "Yes" : "No"); +#if defined(OR2_LIB_INTERFACE) && OR2_LIB_INTERFACE > 1 + ast_cli(a->fd, "MFC/R2 Skip Category Request: %s\n", openr2_context_get_skip_category_request(r2context) ? "Yes" : "No"); +#endif ast_cli(a->fd, "MFC/R2 Immediate Accept: %s\n", openr2_context_get_immediate_accept(r2context) ? "Yes" : "No"); ast_cli(a->fd, "MFC/R2 Accept on Offer: %s\n", tmp->mfcr2_accept_on_offer ? "Yes" : "No"); ast_cli(a->fd, "MFC/R2 Charge Calls: %s\n", tmp->mfcr2_charge_calls ? "Yes" : "No"); @@ -15054,14 +16697,14 @@ { if (p) { switch (mode) { - case TRANSFER: - p->fake_event = DAHDI_EVENT_WINKFLASH; - break; - case HANGUP: - p->fake_event = DAHDI_EVENT_ONHOOK; - break; - default: - ast_log(LOG_WARNING, "I don't know how to handle transfer event with this: %d on channel %s\n",mode, p->owner->name); + case TRANSFER: + p->fake_event = DAHDI_EVENT_WINKFLASH; + break; + case HANGUP: + p->fake_event = DAHDI_EVENT_ONHOOK; + break; + default: + ast_log(LOG_WARNING, "I don't know how to handle transfer event with this: %d on channel %s\n",mode, p->owner->name); } } return 0; @@ -15746,11 +17389,11 @@ #endif ast_cli_unregister_multiple(dahdi_cli, ARRAY_LEN(dahdi_cli)); - ast_manager_unregister( "DAHDIDialOffhook" ); - ast_manager_unregister( "DAHDIHangup" ); - ast_manager_unregister( "DAHDITransfer" ); - ast_manager_unregister( "DAHDIDNDoff" ); - ast_manager_unregister( "DAHDIDNDon" ); + ast_manager_unregister("DAHDIDialOffhook"); + ast_manager_unregister("DAHDIHangup"); + ast_manager_unregister("DAHDITransfer"); + ast_manager_unregister("DAHDIDNDoff"); + ast_manager_unregister("DAHDIDNDon"); ast_manager_unregister("DAHDIShowChannels"); ast_manager_unregister("DAHDIRestart"); ast_channel_unregister(&dahdi_tech); @@ -15993,49 +17636,15 @@ return -1; ast_log(LOG_DEBUG, "Channel '%s' configured.\n", v->value); } else if (!strcasecmp(v->name, "buffers")) { - int res; - char policy[21] = ""; - - res = sscanf(v->value, "%d,%20s", &confp->chan.buf_no, policy); - if (res != 2) { - ast_log(LOG_WARNING, "Parsing buffers option data failed, using defaults.\n"); + if (parse_buffers_policy(v->value, &confp->chan.buf_no, &confp->chan.buf_policy)) { + ast_log(LOG_WARNING, "Using default buffer policy.\n"); confp->chan.buf_no = numbufs; - continue; - } - if (confp->chan.buf_no < 0) - confp->chan.buf_no = numbufs; - if (!strcasecmp(policy, "full")) { - confp->chan.buf_policy = DAHDI_POLICY_WHEN_FULL; - } else if (!strcasecmp(policy, "immediate")) { confp->chan.buf_policy = DAHDI_POLICY_IMMEDIATE; -#ifdef HAVE_DAHDI_HALF_FULL - } else if (!strcasecmp(policy, "half_full")) { - confp->chan.buf_policy = DAHDI_POLICY_HALF_FULL; -#endif - } else { - ast_log(LOG_WARNING, "Invalid policy name given (%s).\n", policy); } } else if (!strcasecmp(v->name, "faxbuffers")) { - int res; - char policy[21] = ""; - - res = sscanf(v->value, "%d,%20s", &confp->chan.faxbuf_no, policy); - if (res != 2) { - ast_log(LOG_WARNING, "Parsing faxbuffers option data failed, using defaults.\n"); - confp->chan.faxbuf_no = numbufs; - continue; + if (!parse_buffers_policy(v->value, &confp->chan.faxbuf_no, &confp->chan.faxbuf_policy)) { + confp->chan.usefaxbuffers = 1; } - confp->chan.usefaxbuffers = 1; - if (confp->chan.faxbuf_no < 0) - confp->chan.faxbuf_no = numbufs; - if (!strcasecmp(policy, "full")) { - confp->chan.faxbuf_policy = DAHDI_POLICY_WHEN_FULL; - } else if (!strcasecmp(policy, "immediate")) { - confp->chan.faxbuf_policy = DAHDI_POLICY_IMMEDIATE; - } else { - ast_log(LOG_WARNING, "Invalid policy name given (%s).\n", policy); - confp->chan.usefaxbuffers = 0; - } } else if (!strcasecmp(v->name, "dahdichan")) { ast_copy_string(dahdichan, v->value, sizeof(dahdichan)); } else if (!strcasecmp(v->name, "usedistinctiveringdetection")) { @@ -16597,6 +18206,14 @@ #endif } else if (!strcasecmp(v->name, "discardremoteholdretrieval")) { confp->pri.discardremoteholdretrieval = ast_true(v->value); +#ifdef HAVE_PRI_SERVICE_MESSAGES + } else if (!strcasecmp(v->name, "service_message_support")) { + /* assuming switchtype for this channel group has been configured already */ + if ((confp->pri.switchtype == PRI_SWITCH_ATT4ESS || confp->pri.switchtype == PRI_SWITCH_LUCENT5E) && ast_true(v->value)) + confp->pri.enable_service_message_support = 1; + else + confp->pri.enable_service_message_support = 0; +#endif #ifdef HAVE_PRI_INBANDDISCONNECT } else if (!strcasecmp(v->name, "inbanddisconnect")) { confp->pri.inbanddisconnect = ast_true(v->value); @@ -16619,10 +18236,9 @@ } } else ast_log(LOG_WARNING, "'%s' is not a valid ISDN timer configuration string at line %d.\n", v->value, v->lineno); - +#endif /* PRI_GETSET_TIMERS */ } else if (!strcasecmp(v->name, "facilityenable")) { confp->pri.facilityenable = ast_true(v->value); -#endif /* PRI_GETSET_TIMERS */ #endif /* HAVE_PRI */ #ifdef HAVE_SS7 } else if (!strcasecmp(v->name, "ss7type")) { @@ -17220,13 +18836,13 @@ ast_cli_register_multiple(dahdi_cli, ARRAY_LEN(dahdi_cli)); memset(round_robin, 0, sizeof(round_robin)); - ast_manager_register( "DAHDITransfer", 0, action_transfer, "Transfer DAHDI Channel" ); - ast_manager_register( "DAHDIHangup", 0, action_transferhangup, "Hangup DAHDI Channel" ); - ast_manager_register( "DAHDIDialOffhook", 0, action_dahdidialoffhook, "Dial over DAHDI channel while offhook" ); - ast_manager_register( "DAHDIDNDon", 0, action_dahdidndon, "Toggle DAHDI channel Do Not Disturb status ON" ); - ast_manager_register( "DAHDIDNDoff", 0, action_dahdidndoff, "Toggle DAHDI channel Do Not Disturb status OFF" ); - ast_manager_register("DAHDIShowChannels", 0, action_dahdishowchannels, "Show status DAHDI channels"); - ast_manager_register("DAHDIRestart", 0, action_dahdirestart, "Fully Restart DAHDI channels (terminates calls)"); + ast_manager_register_xml("DAHDITransfer", 0, action_transfer); + ast_manager_register_xml("DAHDIHangup", 0, action_transferhangup); + ast_manager_register_xml("DAHDIDialOffhook", 0, action_dahdidialoffhook); + ast_manager_register_xml("DAHDIDNDon", 0, action_dahdidndon); + ast_manager_register_xml("DAHDIDNDoff", 0, action_dahdidndoff); + ast_manager_register_xml("DAHDIShowChannels", 0, action_dahdishowchannels); + ast_manager_register_xml("DAHDIRestart", 0, action_dahdirestart); ast_cond_init(&ss_thread_complete, NULL); @@ -17345,6 +18961,7 @@ return 0; } + /* This is a workaround so that menuselect displays a proper description * AST_MODULE_INFO(, , "DAHDI Telephony" */ Index: channels/chan_phone.c =================================================================== --- a/channels/chan_phone.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/chan_phone.c (.../trunk) (revision 202568) @@ -303,13 +303,13 @@ snprintf(cid.min, sizeof(cid.min), "%02d", tm.tm_min); } /* the standard format of ast->callerid is: "name" , but not always complete */ - if (ast_strlen_zero(ast->cid.cid_name)) + if (ast_strlen_zero(ast->connected.id.name)) strcpy(cid.name, DEFAULT_CALLER_ID); else - ast_copy_string(cid.name, ast->cid.cid_name, sizeof(cid.name)); + ast_copy_string(cid.name, ast->connected.id.name, sizeof(cid.name)); - if (ast->cid.cid_num) - ast_copy_string(cid.number, ast->cid.cid_num, sizeof(cid.number)); + if (ast->connected.id.number) + ast_copy_string(cid.number, ast->connected.id.number, sizeof(cid.number)); p = ast->tech_pvt; Index: channels/sig_analog.c =================================================================== --- a/channels/sig_analog.c (.../tags/1.6.2.0-beta3) (revision 0) +++ b/channels/sig_analog.c (.../trunk) (revision 202568) @@ -0,0 +1,3262 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2009, Digium, Inc. + * + * Mark Spencer + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Analog signaling module + * + * \author Matthew Fredrickson + */ + +#include "asterisk.h" + +#include +#include + +#include "asterisk/utils.h" +#include "asterisk/options.h" +#include "asterisk/pbx.h" +#include "asterisk/file.h" +#include "asterisk/callerid.h" +#include "asterisk/say.h" +#include "asterisk/manager.h" +#include "asterisk/astdb.h" +#include "asterisk/features.h" + +#include "sig_analog.h" + +#define POLARITY_IDLE 0 +#define POLARITY_REV 1 +#define MIN_MS_SINCE_FLASH ( (2000) ) /*!< 2000 ms */ +static int analog_matchdigittimeout = 3000; +static int analog_gendigittimeout = 8000; +static int analog_firstdigittimeout = 16000; +static char analog_defaultcic[64] = ""; +static char analog_defaultozz[64] = ""; + +static const struct { + enum analog_sigtype sigtype; + const char const *name; +} sigtypes[] = { + { ANALOG_SIG_FXOLS, "fxo_ls" }, + { ANALOG_SIG_FXOKS, "fxo_ks" }, + { ANALOG_SIG_FXOGS, "fxo_gs" }, + { ANALOG_SIG_FXSLS, "fxs_ls" }, + { ANALOG_SIG_FXSKS, "fxs_ks" }, + { ANALOG_SIG_FXSGS, "fxs_gs" }, + { ANALOG_SIG_EMWINK, "em_w" }, + { ANALOG_SIG_EM, "em" }, + { ANALOG_SIG_EM_E1, "em_e1" }, + { ANALOG_SIG_FEATD, "featd" }, + { ANALOG_SIG_FEATDMF, "featdmf" }, + { ANALOG_SIG_FEATDMF_TA, "featdmf_ta" }, + { ANALOG_SIG_FEATB, "featb" }, + { ANALOG_SIG_FGC_CAMA, "fgccama" }, + { ANALOG_SIG_FGC_CAMAMF, "fgccamamf" }, + { ANALOG_SIG_SF, "sf" }, + { ANALOG_SIG_SFWINK, "sf_w" }, + { ANALOG_SIG_SF_FEATD, "sf_featd" }, + { ANALOG_SIG_SF_FEATDMF, "sf_featdmf" }, + { ANALOG_SIG_SF_FEATB, "sf_featb" }, + { ANALOG_SIG_E911, "e911" }, +}; + +static const struct { + unsigned int cid_type; + const char const *name; +} cidtypes[] = { + { CID_SIG_BELL, "bell" }, + { CID_SIG_V23, "v23" }, + { CID_SIG_V23_JP, "v23_jp" }, + { CID_SIG_DTMF, "dtmf" }, + /* "smdi" is intentionally not supported here, as there is a much better + * way to do this in the dialplan now. */ +}; + +enum analog_sigtype analog_str_to_sigtype(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_LEN(sigtypes); i++) { + if (!strcasecmp(sigtypes[i].name, name)) { + return sigtypes[i].sigtype; + } + } + + return 0; +} + +const char *analog_sigtype_to_str(enum analog_sigtype sigtype) +{ + int i; + + for (i = 0; i < ARRAY_LEN(sigtypes); i++) { + if (sigtype == sigtypes[i].sigtype) { + return sigtypes[i].name; + } + } + + return "Unknown"; +} + +unsigned int analog_str_to_cidtype(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_LEN(cidtypes); i++) { + if (!strcasecmp(cidtypes[i].name, name)) { + return cidtypes[i].cid_type; + } + } + + return 0; +} + +const char *analog_cidtype_to_str(unsigned int cid_type) +{ + int i; + + for (i = 0; i < ARRAY_LEN(cidtypes); i++) { + if (cid_type == cidtypes[i].cid_type) { + return cidtypes[i].name; + } + } + + return "Unknown"; +} + +static int analog_start_cid_detect(struct analog_pvt *p, int cid_signalling) +{ + if (p->calls->start_cid_detect) + return p->calls->start_cid_detect(p->chan_pvt, cid_signalling); + else + return -1; +} + +static int analog_stop_cid_detect(struct analog_pvt *p) +{ + if (p->calls->stop_cid_detect) + return p->calls->stop_cid_detect(p->chan_pvt); + else + return -1; +} + +static int analog_get_callerid(struct analog_pvt *p, char *name, char *number, enum analog_event *ev, size_t timeout) +{ + if (p->calls->get_callerid) + return p->calls->get_callerid(p->chan_pvt, name, number, ev, timeout); + else + return -1; +} + +static int analog_get_event(struct analog_pvt *p) +{ + if (p->calls->get_event) + return p->calls->get_event(p->chan_pvt); + else + return -1; +} + +static int analog_wait_event(struct analog_pvt *p) +{ + if (p->calls->wait_event) + return p->calls->wait_event(p->chan_pvt); + else + return -1; +} + +enum analog_cid_start analog_str_to_cidstart(const char *value) +{ + if (!strcasecmp(value, "ring")) { + return ANALOG_CID_START_RING; + } else if (!strcasecmp(value, "polarity")) { + return ANALOG_CID_START_POLARITY; + } else if (!strcasecmp(value, "polarity_in")) { + return ANALOG_CID_START_POLARITY_IN; + } + + return 0; +} + +const char *analog_cidstart_to_str(enum analog_cid_start cid_start) +{ + switch (cid_start) { + case ANALOG_CID_START_RING: + return "Ring"; + case ANALOG_CID_START_POLARITY: + return "Polarity"; + case ANALOG_CID_START_POLARITY_IN: + return "Polarity_In"; + } + + return "Unknown"; +} + +static char *analog_event2str(enum analog_event event) +{ + char *res; + switch (event) { + case ANALOG_EVENT_DIALCOMPLETE: + res = "ANALOG_EVENT_DIALCOMPLETE"; + break; + case ANALOG_EVENT_WINKFLASH: + res = "ANALOG_EVENT_WINKFLASH"; + break; + case ANALOG_EVENT_ONHOOK: + res = "ANALOG_EVENT_ONHOOK"; + break; + case ANALOG_EVENT_RINGOFFHOOK: + res = "ANALOG_EVENT_RINGOFFHOOK"; + break; + case ANALOG_EVENT_ALARM: + res = "ANALOG_EVENT_ALARM"; + break; + case ANALOG_EVENT_NOALARM: + res = "ANALOG_EVENT_NOALARM"; + break; + case ANALOG_EVENT_HOOKCOMPLETE: + res = "ANALOG_EVENT_HOOKCOMPLETE"; + break; + case ANALOG_EVENT_POLARITY: + res = "ANALOG_EVENT_POLARITY"; + break; + case ANALOG_EVENT_RINGERON: + res = "ANALOG_EVENT_RINGERON"; + break; + case ANALOG_EVENT_RINGEROFF: + res = "ANALOG_EVENT_RINGEROFF"; + break; + case ANALOG_EVENT_RINGBEGIN: + res = "ANALOG_EVENT_RINGBEGIN"; + break; + case ANALOG_EVENT_PULSE_START: + res = "ANALOG_EVENT_PULSE_START"; + break; + case ANALOG_EVENT_NEONMWI_ACTIVE: + res = "ANALOG_EVENT_NEONMWI_ACTIVE"; + break; + case ANALOG_EVENT_NEONMWI_INACTIVE: + res = "ANALOG_EVENT_NEONMWI_INACTIVE"; + break; + default: + res = "UNKNOWN/OTHER"; + break; + } + + return res; +} + +static void analog_swap_subs(struct analog_pvt *p, enum analog_sub a, enum analog_sub b) +{ + int tinthreeway; + struct ast_channel *towner; + + ast_debug(1, "Swapping %d and %d\n", a, b); + + towner = p->subs[a].owner; + tinthreeway = p->subs[a].inthreeway; + + p->subs[a].owner = p->subs[b].owner; + p->subs[a].inthreeway = p->subs[b].inthreeway; + + p->subs[b].owner = towner; + p->subs[b].inthreeway = tinthreeway; + + if (p->calls->swap_subs) + p->calls->swap_subs(p->chan_pvt, a, p->subs[a].owner, b, p->subs[b].owner); + +} + +static int analog_alloc_sub(struct analog_pvt *p, enum analog_sub x) +{ + p->subs[x].allocd = 1; + if (p->calls->allocate_sub) + return p->calls->allocate_sub(p->chan_pvt, x); + + return 0; +} + +static int analog_unalloc_sub(struct analog_pvt *p, enum analog_sub x) +{ + p->subs[x].allocd = 0; + p->subs[x].owner = NULL; + if (p->calls->unallocate_sub) + return p->calls->unallocate_sub(p->chan_pvt, x); + + return 0; +} + +static int analog_send_callerid(struct analog_pvt *p, int cwcid, struct ast_callerid *cid) +{ + ast_debug(1, "Sending callerid. CID_NAME: '%s' CID_NUM: '%s'\n", cid->cid_name, cid->cid_num); + + if (cwcid) { + p->callwaitcas = 0; + } + + if (p->calls->send_callerid) + return p->calls->send_callerid(p->chan_pvt, cwcid, cid); + else + return 0; +} + +static int analog_get_index(struct ast_channel *ast, struct analog_pvt *p, int nullok) +{ + int res; + if (p->subs[ANALOG_SUB_REAL].owner == ast) + res = ANALOG_SUB_REAL; + else if (p->subs[ANALOG_SUB_CALLWAIT].owner == ast) + res = ANALOG_SUB_CALLWAIT; + else if (p->subs[ANALOG_SUB_THREEWAY].owner == ast) + res = ANALOG_SUB_THREEWAY; + else { + res = -1; + if (!nullok) + ast_log(LOG_WARNING, "Unable to get index, and nullok is not asserted\n"); + } + return res; +} + +static int analog_dsp_reset_and_flush_digits(struct analog_pvt *p) +{ + if (p->calls->dsp_reset_and_flush_digits) + return p->calls->dsp_reset_and_flush_digits(p->chan_pvt); + else + /* Return 0 since I think this is unnecessary to do in most cases it is used. Mostly only for ast_dsp */ + return 0; +} + +static int analog_play_tone(struct analog_pvt *p, enum analog_sub sub, enum analog_tone tone) +{ + if (p->calls->play_tone) + return p->calls->play_tone(p->chan_pvt, sub, tone); + else + return -1; +} + +static struct ast_channel * analog_new_ast_channel(struct analog_pvt *p, int state, int startpbx, enum analog_sub sub) +{ + struct ast_channel *c; + + if (p->calls->new_ast_channel) + c = p->calls->new_ast_channel(p->chan_pvt, state, startpbx, sub); + else + return NULL; + + p->subs[sub].owner = c; + if (!p->owner) + p->owner = c; + return c; +} + +static int analog_set_echocanceller(struct analog_pvt *p, int enable) +{ + if (p->calls->set_echocanceller) + return p->calls->set_echocanceller(p->chan_pvt, enable); + else + return -1; +} + +static int analog_train_echocanceller(struct analog_pvt *p) +{ + if (p->calls->train_echocanceller) + return p->calls->train_echocanceller(p->chan_pvt); + else + return -1; +} + +static int analog_is_off_hook(struct analog_pvt *p) +{ + if (p->calls->is_off_hook) + return p->calls->is_off_hook(p->chan_pvt); + else + return -1; +} + +static int analog_ring(struct analog_pvt *p) +{ + if (p->calls->ring) + return p->calls->ring(p->chan_pvt); + else + return -1; +} + +static int analog_start(struct analog_pvt *p) +{ + if (p->calls->start) + return p->calls->start(p->chan_pvt); + else + return -1; +} + +static int analog_dial_digits(struct analog_pvt *p, enum analog_sub sub, struct analog_dialoperation *dop) +{ + if (p->calls->dial_digits) + return p->calls->dial_digits(p->chan_pvt, sub, dop); + else + return -1; +} + +static int analog_on_hook(struct analog_pvt *p) +{ + if (p->calls->on_hook) + return p->calls->on_hook(p->chan_pvt); + else + return -1; +} + +static int analog_check_for_conference(struct analog_pvt *p) +{ + if (p->calls->check_for_conference) + return p->calls->check_for_conference(p->chan_pvt); + else + return -1; +} + +static void analog_all_subchannels_hungup(struct analog_pvt *p) +{ + if (p->calls->all_subchannels_hungup) + p->calls->all_subchannels_hungup(p->chan_pvt); +} + +static void analog_unlock_private(struct analog_pvt *p) +{ + if (p->calls->unlock_private) + p->calls->unlock_private(p->chan_pvt); +} + +static void analog_lock_private(struct analog_pvt *p) +{ + if (p->calls->lock_private) + p->calls->lock_private(p->chan_pvt); +} + +static int analog_off_hook(struct analog_pvt *p) +{ + if (p->calls->off_hook) + return p->calls->off_hook(p->chan_pvt); + else + return -1; +} + +static int analog_dsp_set_digitmode(struct analog_pvt *p, enum analog_dsp_digitmode mode) +{ + if (p->calls->dsp_set_digitmode) + return p->calls->dsp_set_digitmode(p->chan_pvt, mode); + else + return -1; +} + +static void analog_cb_handle_dtmfup(struct analog_pvt *p, struct ast_channel *ast, enum analog_sub analog_index, struct ast_frame **dest) +{ + if (p->calls->handle_dtmfup) + p->calls->handle_dtmfup(p->chan_pvt, ast, analog_index, dest); +} + +static int analog_wink(struct analog_pvt *p, enum analog_sub index) +{ + if (p->calls->wink) + return p->calls->wink(p->chan_pvt, index); + else + return -1; +} + +static int analog_has_voicemail(struct analog_pvt *p) +{ + if (p->calls->has_voicemail) + return p->calls->has_voicemail(p->chan_pvt); + else + return -1; +} + +static int analog_is_dialing(struct analog_pvt *p, enum analog_sub index) +{ + if (p->calls->is_dialing) + return p->calls->is_dialing(p->chan_pvt, index); + else + return -1; +} + +static int analog_attempt_transfer(struct analog_pvt *p) +{ + /* In order to transfer, we need at least one of the channels to + actually be in a call bridge. We can't conference two applications + together (but then, why would we want to?) */ + if (ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner)) { + /* The three-way person we're about to transfer to could still be in MOH, so + stop if now if appropriate */ + if (ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)) + ast_queue_control(p->subs[ANALOG_SUB_THREEWAY].owner, AST_CONTROL_UNHOLD); + if (p->subs[ANALOG_SUB_REAL].owner->_state == AST_STATE_RINGING) { + ast_indicate(ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner), AST_CONTROL_RINGING); + } + if (p->subs[ANALOG_SUB_THREEWAY].owner->_state == AST_STATE_RING) { + analog_play_tone(p, ANALOG_SUB_THREEWAY, ANALOG_TONE_RINGTONE); + } + if (ast_channel_masquerade(p->subs[ANALOG_SUB_THREEWAY].owner, ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner))) { + ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n", + ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner)->name, p->subs[ANALOG_SUB_THREEWAY].owner->name); + return -1; + } + /* Orphan the channel after releasing the lock */ + ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); + analog_unalloc_sub(p, ANALOG_SUB_THREEWAY); + } else if (ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)) { + ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_UNHOLD); + if (p->subs[ANALOG_SUB_THREEWAY].owner->_state == AST_STATE_RINGING) { + ast_indicate(ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner), AST_CONTROL_RINGING); + } + if (p->subs[ANALOG_SUB_REAL].owner->_state == AST_STATE_RING) { + analog_play_tone(p, ANALOG_SUB_REAL, ANALOG_TONE_RINGTONE); + } + if (ast_channel_masquerade(p->subs[ANALOG_SUB_REAL].owner, ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner))) { + ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n", + ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)->name, p->subs[ANALOG_SUB_REAL].owner->name); + return -1; + } + /* Three-way is now the REAL */ + analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL); + ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); + analog_unalloc_sub(p, ANALOG_SUB_THREEWAY); + /* Tell the caller not to hangup */ + return 1; + } else { + ast_debug(1, "Neither %s nor %s are in a bridge, nothing to transfer\n", + p->subs[ANALOG_SUB_REAL].owner->name, p->subs[ANALOG_SUB_THREEWAY].owner->name); + ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV); + return -1; + } + return 0; +} + +static int analog_update_conf(struct analog_pvt *p) +{ + int x; + int needconf = 0; + + /* Start with the obvious, general stuff */ + for (x = 0; x < 3; x++) { + /* Look for three way calls */ + if ((p->subs[x].allocd) && p->subs[x].inthreeway) { + if (p->calls->conf_add) + p->calls->conf_add(p->chan_pvt, x); + needconf++; + } else { + if (p->calls->conf_del) + p->calls->conf_del(p->chan_pvt, x); + } + } + ast_debug(1, "Updated conferencing on %d, with %d conference users\n", p->channel, needconf); + + if (p->calls->complete_conference_update) + p->calls->complete_conference_update(p->chan_pvt, needconf); + return 0; +} + +struct ast_channel * analog_request(struct analog_pvt *p, int *callwait) +{ + ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel); + *callwait = (p->owner != NULL); + + if (p->owner) { + if (analog_alloc_sub(p, ANALOG_SUB_CALLWAIT)) { + ast_log(LOG_ERROR, "Unable to alloc subchannel\n"); + return NULL; + } + } + + return analog_new_ast_channel(p, AST_STATE_RESERVED, 0, p->owner ? ANALOG_SUB_CALLWAIT : ANALOG_SUB_REAL); +} + +int analog_available(struct analog_pvt *p, int channelmatch, ast_group_t groupmatch, int *busy, int *channelmatched, int *groupmatched) +{ + int offhook; + + ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel); + /* We're at least busy at this point */ + if (busy) { + if ((p->sig == ANALOG_SIG_FXOKS) || (p->sig == ANALOG_SIG_FXOLS) || (p->sig == ANALOG_SIG_FXOGS)) + *busy = 1; + } + /* If do not disturb, definitely not */ + if (p->dnd) + return 0; + /* If guard time, definitely not */ + if (p->guardtime && (time(NULL) < p->guardtime)) + return 0; + + /* If no owner definitely available */ + if (!p->owner) { + if (p->sig == ANALOG_SIG_FXSLS) + return 1; + + offhook = analog_is_off_hook(p); + + if ((p->sig == ANALOG_SIG_FXSKS) || (p->sig == ANALOG_SIG_FXSGS)) { + /* When "onhook" that means no battery on the line, and thus + it is out of service..., if it's on a TDM card... If it's a channel + bank, there is no telling... */ + if (offhook) + return 1; + else + return 0; + } else if (offhook) { + ast_debug(1, "Channel %d off hook, can't use\n", p->channel); + /* Not available when the other end is off hook */ + return 0; + } + return 1; + } + + /* If it's not an FXO, forget about call wait */ + if ((p->sig != ANALOG_SIG_FXOKS) && (p->sig != ANALOG_SIG_FXOLS) && (p->sig != ANALOG_SIG_FXOGS)) + return 0; + + if (!p->callwaiting) { + /* If they don't have call waiting enabled, then for sure they're unavailable at this point */ + return 0; + } + + if (p->subs[ANALOG_SUB_CALLWAIT].allocd) { + /* If there is already a call waiting call, then we can't take a second one */ + return 0; + } + + if ((p->owner->_state != AST_STATE_UP) && + ((p->owner->_state != AST_STATE_RINGING) || p->outgoing)) { + /* If the current call is not up, then don't allow the call */ + return 0; + } + if ((p->subs[ANALOG_SUB_THREEWAY].owner) && (!p->subs[ANALOG_SUB_THREEWAY].inthreeway)) { + /* Can't take a call wait when the three way calling hasn't been merged yet. */ + return 0; + } + /* We're cool */ + return 1; +} + +static int analog_stop_callwait(struct analog_pvt *p) +{ + if (p->callwaitingcallerid) + p->callwaitcas = 0; + + if (p->calls->stop_callwait) + return p->calls->stop_callwait(p->chan_pvt); + else + return 0; +} + +static int analog_callwait(struct analog_pvt *p) +{ + if (p->callwaitingcallerid) + p->callwaitcas = 1; + if (p->calls->callwait) + return p->calls->callwait(p->chan_pvt); + else + return 0; +} + +int analog_call(struct analog_pvt *p, struct ast_channel *ast, char *rdest, int timeout) +{ + int res, index,mysig; + char *c, *n, *l; + char dest[256]; /* must be same length as p->dialdest */ + + ast_log(LOG_DEBUG, "CALLING CID_NAME: %s CID_NUM:: %s\n", ast->connected.id.name, ast->connected.id.number); + + ast_copy_string(dest, rdest, sizeof(dest)); + ast_copy_string(p->dialdest, rdest, sizeof(p->dialdest)); + + if ((ast->_state == AST_STATE_BUSY)) { + ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_BUSY); + return 0; + } + + if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) { + ast_log(LOG_WARNING, "analog_call called on %s, neither down nor reserved\n", ast->name); + return -1; + } + + p->dialednone = 0; + + mysig = p->sig; + if (p->outsigmod > -1) + mysig = p->outsigmod; + + switch (mysig) { + case ANALOG_SIG_FXOLS: + case ANALOG_SIG_FXOGS: + case ANALOG_SIG_FXOKS: + if (p->owner == ast) { + /* Normal ring, on hook */ + + /* Don't send audio while on hook, until the call is answered */ + p->dialing = 1; + /* XXX */ +#if 0 + /* Choose proper cadence */ + if ((p->distinctivering > 0) && (p->distinctivering <= num_cadence)) { + if (ioctl(p->subs[ANALOG_SUB_REAL].dfd, DAHDI_SETCADENCE, &cadences[p->distinctivering - 1])) + ast_log(LOG_WARNING, "Unable to set distinctive ring cadence %d on '%s': %s\n", p->distinctivering, ast->name, strerror(errno)); + p->cidrings = cidrings[p->distinctivering - 1]; + } else { + if (ioctl(p->subs[ANALOG_SUB_REAL].dfd, DAHDI_SETCADENCE, NULL)) + ast_log(LOG_WARNING, "Unable to reset default ring on '%s': %s\n", ast->name, strerror(errno)); + p->cidrings = p->sendcalleridafter; + } +#endif + p->cidrings = p->sendcalleridafter; + + /* nick@dccinc.com 4/3/03 mods to allow for deferred dialing */ + c = strchr(dest, '/'); + if (c) + c++; + if (c && (strlen(c) < p->stripmsd)) { + ast_log(LOG_WARNING, "Number '%s' is shorter than stripmsd (%d)\n", c, p->stripmsd); + c = NULL; + } + if (c) { + p->dop.op = ANALOG_DIAL_OP_REPLACE; + snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "Tw%s", c); + ast_debug(1, "FXO: setup deferred dialstring: %s\n", c); + } else { + p->dop.dialstr[0] = '\0'; + } + + if (analog_ring(p)) { + ast_log(LOG_WARNING, "Unable to ring phone: %s\n", strerror(errno)); + return -1; + } + p->dialing = 1; + } else { + if (ast->connected.id.number) + ast_copy_string(p->callwait_num, ast->connected.id.number, sizeof(p->callwait_num)); + else + p->callwait_num[0] = '\0'; + if (ast->connected.id.name) + ast_copy_string(p->callwait_name, ast->connected.id.name, sizeof(p->callwait_name)); + else + p->callwait_name[0] = '\0'; + + /* Call waiting tone instead */ + if (analog_callwait(p)) { + return -1; + } + /* Make ring-back */ + if (analog_play_tone(p, ANALOG_SUB_CALLWAIT, ANALOG_TONE_RINGTONE)) + ast_log(LOG_WARNING, "Unable to generate call-wait ring-back on channel %s\n", ast->name); + + } + n = ast->connected.id.name; + l = ast->connected.id.number; + if (l) + ast_copy_string(p->lastcid_num, l, sizeof(p->lastcid_num)); + else + p->lastcid_num[0] = '\0'; + if (n) + ast_copy_string(p->lastcid_name, n, sizeof(p->lastcid_name)); + else + p->lastcid_name[0] = '\0'; + + if (p->use_callerid) { + //p->callwaitcas = 0; + p->cid.cid_name = p->lastcid_name; + p->cid.cid_num = p->lastcid_num; + } + + + ast_setstate(ast, AST_STATE_RINGING); + index = analog_get_index(ast, p, 0); + if (index > -1) { + ast_queue_control(p->subs[index].owner, AST_CONTROL_RINGING); + } + break; + case ANALOG_SIG_FXSLS: + case ANALOG_SIG_FXSGS: + case ANALOG_SIG_FXSKS: + case ANALOG_SIG_EMWINK: + case ANALOG_SIG_EM: + case ANALOG_SIG_EM_E1: + case ANALOG_SIG_FEATD: + case ANALOG_SIG_FEATDMF: + case ANALOG_SIG_E911: + case ANALOG_SIG_FGC_CAMA: + case ANALOG_SIG_FGC_CAMAMF: + case ANALOG_SIG_FEATB: + case ANALOG_SIG_SFWINK: + case ANALOG_SIG_SF: + case ANALOG_SIG_SF_FEATD: + case ANALOG_SIG_SF_FEATDMF: + case ANALOG_SIG_FEATDMF_TA: + case ANALOG_SIG_SF_FEATB: + c = strchr(dest, '/'); + if (c) + c++; + else + c = ""; + if (strlen(c) < p->stripmsd) { + ast_log(LOG_WARNING, "Number '%s' is shorter than stripmsd (%d)\n", c, p->stripmsd); + return -1; + } + res = analog_start(p); + if (res < 0) { + if (errno != EINPROGRESS) { + return -1; + } + } + ast_log(LOG_DEBUG, "Dialing '%s'\n", c); + p->dop.op = ANALOG_DIAL_OP_REPLACE; + + c += p->stripmsd; + + switch (mysig) { + case ANALOG_SIG_FEATD: + l = ast->connected.id.number; + if (l) + snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T*%s*%s*", l, c); + else + snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T**%s*", c); + break; + case ANALOG_SIG_FEATDMF: + l = ast->connected.id.number; + if (l) + snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*00%s#*%s#", l, c); + else + snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*02#*%s#", c); + break; + case ANALOG_SIG_FEATDMF_TA: + { + const char *cic = "", *ozz = ""; + + /* If you have to go through a Tandem Access point you need to use this */ +#ifndef STANDALONE + ozz = pbx_builtin_getvar_helper(p->owner, "FEATDMF_OZZ"); + if (!ozz) + ozz = analog_defaultozz; + cic = pbx_builtin_getvar_helper(p->owner, "FEATDMF_CIC"); + if (!cic) + cic = analog_defaultcic; +#endif + if (!ozz || !cic) { + ast_log(LOG_WARNING, "Unable to dial channel of type feature group D MF tandem access without CIC or OZZ set\n"); + return -1; + } + snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*%s%s#", ozz, cic); + snprintf(p->finaldial, sizeof(p->finaldial), "M*%s#", c); + p->whichwink = 0; + } + break; + case ANALOG_SIG_E911: + ast_copy_string(p->dop.dialstr, "M*911#", sizeof(p->dop.dialstr)); + break; + case ANALOG_SIG_FGC_CAMA: + snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "P%s", c); + break; + case ANALOG_SIG_FGC_CAMAMF: + case ANALOG_SIG_FEATB: + snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*%s#", c); + break; + default: + if (p->pulse) + snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "P%sw", c); + else + snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T%sw", c); + break; + } + + if (p->echotraining && (strlen(p->dop.dialstr) > 4)) { + memset(p->echorest, 'w', sizeof(p->echorest) - 1); + strcpy(p->echorest + (p->echotraining / 400) + 1, p->dop.dialstr + strlen(p->dop.dialstr) - 2); + p->echorest[sizeof(p->echorest) - 1] = '\0'; + p->echobreak = 1; + p->dop.dialstr[strlen(p->dop.dialstr)-2] = '\0'; + } else + p->echobreak = 0; + if (!res) { + if (analog_dial_digits(p, ANALOG_SUB_REAL, &p->dop)) { + int saveerr = errno; + + analog_on_hook(p); + ast_log(LOG_WARNING, "Dialing failed on channel %d: %s\n", p->channel, strerror(saveerr)); + return -1; + } + } else + ast_debug(1, "Deferring dialing...\n"); + p->dialing = 1; + if (ast_strlen_zero(c)) + p->dialednone = 1; + ast_setstate(ast, AST_STATE_DIALING); + break; + default: + ast_debug(1, "not yet implemented\n"); + return -1; + } + return 0; +} + +int analog_hangup(struct analog_pvt *p, struct ast_channel *ast) +{ + int res; + int index, x; + + ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel); + if (!ast->tech_pvt) { + ast_log(LOG_WARNING, "Asked to hangup channel not connected\n"); + return 0; + } + + index = analog_get_index(ast, p, 1); + + x = 0; + if (p->origcid_num) { + ast_copy_string(p->cid_num, p->origcid_num, sizeof(p->cid_num)); + free(p->origcid_num); + p->origcid_num = NULL; + } + if (p->origcid_name) { + ast_copy_string(p->cid_name, p->origcid_name, sizeof(p->cid_name)); + free(p->origcid_name); + p->origcid_name = NULL; + } + + analog_dsp_set_digitmode(p, ANALOG_DIGITMODE_DTMF); + + ast_debug(1, "Hangup: channel: %d index = %d, normal = %d, callwait = %d, thirdcall = %d\n", + p->channel, index, p->subs[ANALOG_SUB_REAL].allocd, p->subs[ANALOG_SUB_CALLWAIT].allocd, p->subs[ANALOG_SUB_THREEWAY].allocd); + if (index > -1) { + /* Real channel, do some fixup */ + p->subs[index].owner = NULL; + p->subs[index].needcallerid = 0; + p->polarity = POLARITY_IDLE; + if (index == ANALOG_SUB_REAL) { + if (p->subs[ANALOG_SUB_CALLWAIT].allocd && p->subs[ANALOG_SUB_THREEWAY].allocd) { + ast_debug(1, "Normal call hung up with both three way call and a call waiting call in place?\n"); + if (p->subs[ANALOG_SUB_CALLWAIT].inthreeway) { + /* We had flipped over to answer a callwait and now it's gone */ + ast_debug(1, "We were flipped over to the callwait, moving back and unowning.\n"); + /* Move to the call-wait, but un-own us until they flip back. */ + analog_swap_subs(p, ANALOG_SUB_CALLWAIT, ANALOG_SUB_REAL); + analog_unalloc_sub(p, ANALOG_SUB_CALLWAIT); + p->owner = NULL; + } else { + /* The three way hung up, but we still have a call wait */ + ast_debug(1, "We were in the threeway and have a callwait still. Ditching the threeway.\n"); + analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL); + analog_unalloc_sub(p, ANALOG_SUB_THREEWAY); + if (p->subs[ANALOG_SUB_REAL].inthreeway) { + /* This was part of a three way call. Immediately make way for + another call */ + ast_debug(1, "Call was complete, setting owner to former third call\n"); + p->owner = p->subs[ANALOG_SUB_REAL].owner; + } else { + /* This call hasn't been completed yet... Set owner to NULL */ + ast_debug(1, "Call was incomplete, setting owner to NULL\n"); + p->owner = NULL; + } + p->subs[ANALOG_SUB_REAL].inthreeway = 0; + } + } else if (p->subs[ANALOG_SUB_CALLWAIT].allocd) { + /* Move to the call-wait and switch back to them. */ + analog_swap_subs(p, ANALOG_SUB_CALLWAIT, ANALOG_SUB_REAL); + analog_unalloc_sub(p, ANALOG_SUB_CALLWAIT); + p->owner = p->subs[ANALOG_SUB_REAL].owner; + if (p->owner->_state != AST_STATE_UP) + ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_ANSWER); + if (ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner)) + ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_UNHOLD); + } else if (p->subs[ANALOG_SUB_THREEWAY].allocd) { + analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL); + analog_unalloc_sub(p, ANALOG_SUB_THREEWAY); + if (p->subs[ANALOG_SUB_REAL].inthreeway) { + /* This was part of a three way call. Immediately make way for + another call */ + ast_debug(1, "Call was complete, setting owner to former third call\n"); + p->owner = p->subs[ANALOG_SUB_REAL].owner; + } else { + /* This call hasn't been completed yet... Set owner to NULL */ + ast_debug(1, "Call was incomplete, setting owner to NULL\n"); + p->owner = NULL; + } + p->subs[ANALOG_SUB_REAL].inthreeway = 0; + } + } else if (index == ANALOG_SUB_CALLWAIT) { + /* Ditch the holding callwait call, and immediately make it availabe */ + if (p->subs[ANALOG_SUB_CALLWAIT].inthreeway) { + /* This is actually part of a three way, placed on hold. Place the third part + on music on hold now */ + if (p->subs[ANALOG_SUB_THREEWAY].owner && ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)) { + ast_queue_control_data(p->subs[ANALOG_SUB_THREEWAY].owner, AST_CONTROL_HOLD, + S_OR(p->mohsuggest, NULL), + !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); + } + p->subs[ANALOG_SUB_THREEWAY].inthreeway = 0; + /* Make it the call wait now */ + analog_swap_subs(p, ANALOG_SUB_CALLWAIT, ANALOG_SUB_THREEWAY); + analog_unalloc_sub(p, ANALOG_SUB_THREEWAY); + } else + analog_unalloc_sub(p, ANALOG_SUB_CALLWAIT); + } else if (index == ANALOG_SUB_THREEWAY) { + if (p->subs[ANALOG_SUB_CALLWAIT].inthreeway) { + /* The other party of the three way call is currently in a call-wait state. + Start music on hold for them, and take the main guy out of the third call */ + if (p->subs[ANALOG_SUB_CALLWAIT].owner && ast_bridged_channel(p->subs[ANALOG_SUB_CALLWAIT].owner)) { + ast_queue_control_data(p->subs[ANALOG_SUB_CALLWAIT].owner, AST_CONTROL_HOLD, + S_OR(p->mohsuggest, NULL), + !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); + } + p->subs[ANALOG_SUB_CALLWAIT].inthreeway = 0; + } + p->subs[ANALOG_SUB_REAL].inthreeway = 0; + /* If this was part of a three way call index, let us make + another three way call */ + analog_unalloc_sub(p, ANALOG_SUB_THREEWAY); + } else { + /* This wasn't any sort of call, but how are we an index? */ + ast_log(LOG_WARNING, "Index found but not any type of call?\n"); + } + } + + if (!p->subs[ANALOG_SUB_REAL].owner && !p->subs[ANALOG_SUB_CALLWAIT].owner && !p->subs[ANALOG_SUB_THREEWAY].owner) { + p->owner = NULL; +#if 0 + p->ringt = 0; +#endif +#if 0 /* Since we set it in _call */ + p->cidrings = 1; +#endif + p->outgoing = 0; + + /* Perform low level hangup if no owner left */ + res = analog_on_hook(p); + if (res < 0) { + ast_log(LOG_WARNING, "Unable to hangup line %s\n", ast->name); + } + switch (p->sig) { + case ANALOG_SIG_FXOGS: + case ANALOG_SIG_FXOLS: + case ANALOG_SIG_FXOKS: + /* If they're off hook, try playing congestion */ + if (analog_is_off_hook(p)) + analog_play_tone(p, ANALOG_SUB_REAL, ANALOG_TONE_CONGESTION); + else + analog_play_tone(p, ANALOG_SUB_REAL, -1); + break; + case ANALOG_SIG_FXSGS: + case ANALOG_SIG_FXSLS: + case ANALOG_SIG_FXSKS: + /* Make sure we're not made available for at least two seconds assuming + we were actually used for an inbound or outbound call. */ + if (ast->_state != AST_STATE_RESERVED) { + time(&p->guardtime); + p->guardtime += 2; + } + break; + default: + analog_play_tone(p, ANALOG_SUB_REAL, -1); + } + + + analog_set_echocanceller(p, 0); + + x = 0; + ast_channel_setoption(ast,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0); + ast_channel_setoption(ast,AST_OPTION_TDD,&x,sizeof(char),0); + p->callwaitcas = 0; + p->callwaiting = p->permcallwaiting; + p->hidecallerid = p->permhidecallerid; + p->dialing = 0; + analog_update_conf(p); + analog_all_subchannels_hungup(p); + } + + analog_stop_callwait(p); + ast->tech_pvt = NULL; + + if (option_verbose > 2) { + ast_verbose(VERBOSE_PREFIX_3 "Hanging up on '%s'\n", ast->name); + } + + return 0; +} + +int analog_answer(struct analog_pvt *p, struct ast_channel *ast) +{ + int res = 0; + int index; + int oldstate = ast->_state; + ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel); + ast_setstate(ast, AST_STATE_UP); + index = analog_get_index(ast, p, 1); + if (index < 0) + index = ANALOG_SUB_REAL; + switch (p->sig) { + case ANALOG_SIG_FXSLS: + case ANALOG_SIG_FXSGS: + case ANALOG_SIG_FXSKS: +#if 0 + p->ringt = 0; +#endif + /* Fall through */ + case ANALOG_SIG_EM: + case ANALOG_SIG_EM_E1: + case ANALOG_SIG_EMWINK: + case ANALOG_SIG_FEATD: + case ANALOG_SIG_FEATDMF: + case ANALOG_SIG_FEATDMF_TA: + case ANALOG_SIG_E911: + case ANALOG_SIG_FGC_CAMA: + case ANALOG_SIG_FGC_CAMAMF: + case ANALOG_SIG_FEATB: + case ANALOG_SIG_SF: + case ANALOG_SIG_SFWINK: + case ANALOG_SIG_SF_FEATD: + case ANALOG_SIG_SF_FEATDMF: + case ANALOG_SIG_SF_FEATB: + case ANALOG_SIG_FXOLS: + case ANALOG_SIG_FXOGS: + case ANALOG_SIG_FXOKS: + /* Pick up the line */ + ast_debug(1, "Took %s off hook\n", ast->name); + if (p->hanguponpolarityswitch) { + gettimeofday(&p->polaritydelaytv, NULL); + } + res = analog_off_hook(p); + analog_play_tone(p, index, -1); + p->dialing = 0; + if ((index == ANALOG_SUB_REAL) && p->subs[ANALOG_SUB_THREEWAY].inthreeway) { + if (oldstate == AST_STATE_RINGING) { + ast_debug(1, "Finally swapping real and threeway\n"); + analog_play_tone(p, ANALOG_SUB_THREEWAY, -1); + analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL); + p->owner = p->subs[ANALOG_SUB_REAL].owner; + } + } + if ((p->sig == ANALOG_SIG_FXSLS) || (p->sig == ANALOG_SIG_FXSKS) || (p->sig == ANALOG_SIG_FXSGS)) { + analog_set_echocanceller(p, 1); + analog_train_echocanceller(p); + } + break; + default: + ast_log(LOG_WARNING, "Don't know how to answer signalling %d (channel %d)\n", p->sig, p->channel); + res = -1; + } + ast_setstate(ast, AST_STATE_UP); + return res; +} + +static int analog_handles_digit(struct ast_frame *f) +{ + char subclass = toupper(f->subclass); + + switch (subclass) { + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '9': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + return 1; + default: + return 0; + } +} + +void analog_handle_dtmfup(struct analog_pvt *p, struct ast_channel *ast, enum analog_sub index, struct ast_frame **dest) +{ + struct ast_frame *f = *dest; + + if (p->callwaitcas) { + if ((f->subclass == 'A') || (f->subclass == 'D')) { + ast_log(LOG_ERROR, "Got some DTMF, but it's for the CAS\n"); + p->cid.cid_name = p->callwait_name; + p->cid.cid_num = p->callwait_num; + analog_send_callerid(p, 1, &p->cid); + } + if (analog_handles_digit(f)) + p->callwaitcas = 0; + p->subs[index].f.frametype = AST_FRAME_NULL; + p->subs[index].f.subclass = 0; + *dest = &p->subs[index].f; + } else { + analog_cb_handle_dtmfup(p, ast, index, dest); + } +} + +static int analog_my_getsigstr(struct ast_channel *chan, char *str, const char *term, int ms) +{ + char c; + + *str = 0; /* start with empty output buffer */ + for (;;) + { + /* Wait for the first digit (up to specified ms). */ + c = ast_waitfordigit(chan, ms); + /* if timeout, hangup or error, return as such */ + if (c < 1) + return c; + *str++ = c; + *str = 0; + if (strchr(term, c)) + return 1; + } +} + +static int analog_handle_notify_message(struct ast_channel *chan, struct analog_pvt *p, int cid_flags, int neon_mwievent) +{ + if (p->calls->handle_notify_message) { + p->calls->handle_notify_message(chan, p->chan_pvt, cid_flags, neon_mwievent); + return 0; + } + else + return -1; +} + +static int analog_increase_ss_count(struct analog_pvt *p) +{ + if (p->calls->increase_ss_count) { + p->calls->increase_ss_count(); + return 0; + } else + return -1; +} + +static int analog_decrease_ss_count(struct analog_pvt *p) +{ + if (p->calls->decrease_ss_count) { + p->calls->decrease_ss_count(); + return 0; + } else + return -1; +} + +#define ANALOG_NEED_MFDETECT(p) (((p)->sig == ANALOG_SIG_FEATDMF) || ((p)->sig == ANALOG_SIG_FEATDMF_TA) || ((p)->sig == ANALOG_SIG_E911) || ((p)->sig == ANALOG_SIG_FGC_CAMA) || ((p)->sig == ANALOG_SIG_FGC_CAMAMF) || ((p)->sig == ANALOG_SIG_FEATB)) + +/* Note by jpeeler: This function has a rather large section of code ifdefed + * away. I'd like to leave the code there until more testing is done and I + * know for sure that nothing got left out. The plan is at the latest for this + * comment and code below to be removed shortly after the merging of sig_pri. + */ +static void *__analog_ss_thread(void *data) +{ + struct analog_pvt *p = data; + struct ast_channel *chan = p->ss_astchan; + char exten[AST_MAX_EXTENSION] = ""; + char exten2[AST_MAX_EXTENSION] = ""; + char dtmfcid[300]; + char dtmfbuf[300]; + char namebuf[ANALOG_MAX_CID]; + char numbuf[ANALOG_MAX_CID]; + struct callerid_state *cs = NULL; + char *name = NULL, *number = NULL; + int flags; +#if 0 + unsigned char buf[256]; + int distMatches; + int curRingData[3]; + int receivedRingT; + int samples = 0; + int counter1; + int counter; + int i; +#endif + int timeout; + int getforward = 0; + char *s1, *s2; + int len = 0; + int res; + int index; + + analog_increase_ss_count(p); + + ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel); + + /* in the bizarre case where the channel has become a zombie before we + even get started here, abort safely + */ + if (!p) { + ast_log(LOG_WARNING, "Channel became a zombie before simple switch could be started (%s)\n", chan->name); + ast_hangup(chan); + goto quit; + } + + ast_verb(3, "Starting simple switch on '%s'\n", chan->name); + index = analog_get_index(chan, p, 1); + if (index < 0) { + ast_log(LOG_WARNING, "Huh?\n"); + ast_hangup(chan); + goto quit; + } + analog_dsp_reset_and_flush_digits(p); + switch (p->sig) { + case ANALOG_SIG_FEATD: + case ANALOG_SIG_FEATDMF: + case ANALOG_SIG_FEATDMF_TA: + case ANALOG_SIG_E911: + case ANALOG_SIG_FGC_CAMAMF: + case ANALOG_SIG_FEATB: + case ANALOG_SIG_EMWINK: + case ANALOG_SIG_SF_FEATD: + case ANALOG_SIG_SF_FEATDMF: + case ANALOG_SIG_SF_FEATB: + case ANALOG_SIG_SFWINK: + if (analog_wink(p, index)) + goto quit; + /* Fall through */ + case ANALOG_SIG_EM: + case ANALOG_SIG_EM_E1: + case ANALOG_SIG_SF: + case ANALOG_SIG_FGC_CAMA: + res = analog_play_tone(p, index, -1); + + analog_dsp_reset_and_flush_digits(p); + + if (ANALOG_NEED_MFDETECT(p)) { + analog_dsp_set_digitmode(p, ANALOG_DIGITMODE_MF); + } else + analog_dsp_set_digitmode(p, ANALOG_DIGITMODE_DTMF); + + memset(dtmfbuf, 0, sizeof(dtmfbuf)); + /* Wait for the first digit only if immediate=no */ + if (!p->immediate) + /* Wait for the first digit (up to 5 seconds). */ + res = ast_waitfordigit(chan, 5000); + else + res = 0; + if (res > 0) { + /* save first char */ + dtmfbuf[0] = res; + switch (p->sig) { + case ANALOG_SIG_FEATD: + case ANALOG_SIG_SF_FEATD: + res = analog_my_getsigstr(chan, dtmfbuf + 1, "*", 3000); + if (res > 0) + res = analog_my_getsigstr(chan, dtmfbuf + strlen(dtmfbuf), "*", 3000); + if (res < 1) + analog_dsp_reset_and_flush_digits(p); + break; + case ANALOG_SIG_FEATDMF_TA: + res = analog_my_getsigstr(chan, dtmfbuf + 1, "#", 3000); + if (res < 1) + analog_dsp_reset_and_flush_digits(p); + if (analog_wink(p, index)) goto quit; + dtmfbuf[0] = 0; + /* Wait for the first digit (up to 5 seconds). */ + res = ast_waitfordigit(chan, 5000); + if (res <= 0) break; + dtmfbuf[0] = res; + /* fall through intentionally */ + case ANALOG_SIG_FEATDMF: + case ANALOG_SIG_E911: + case ANALOG_SIG_FGC_CAMAMF: + case ANALOG_SIG_SF_FEATDMF: + res = analog_my_getsigstr(chan, dtmfbuf + 1, "#", 3000); + /* if international caca, do it again to get real ANO */ + if ((p->sig == ANALOG_SIG_FEATDMF) && (dtmfbuf[1] != '0') && (strlen(dtmfbuf) != 14)) + { + if (analog_wink(p, index)) goto quit; + dtmfbuf[0] = 0; + /* Wait for the first digit (up to 5 seconds). */ + res = ast_waitfordigit(chan, 5000); + if (res <= 0) break; + dtmfbuf[0] = res; + res = analog_my_getsigstr(chan, dtmfbuf + 1, "#", 3000); + } + if (res > 0) { + /* if E911, take off hook */ + if (p->sig == ANALOG_SIG_E911) + analog_off_hook(p); + res = analog_my_getsigstr(chan, dtmfbuf + strlen(dtmfbuf), "#", 3000); + } + if (res < 1) + analog_dsp_reset_and_flush_digits(p); + break; + case ANALOG_SIG_FEATB: + case ANALOG_SIG_SF_FEATB: + res = analog_my_getsigstr(chan, dtmfbuf + 1, "#", 3000); + if (res < 1) + analog_dsp_reset_and_flush_digits(p); + break; + case ANALOG_SIG_EMWINK: + /* if we received a '*', we are actually receiving Feature Group D + dial syntax, so use that mode; otherwise, fall through to normal + mode + */ + if (res == '*') { + res = analog_my_getsigstr(chan, dtmfbuf + 1, "*", 3000); + if (res > 0) + res = analog_my_getsigstr(chan, dtmfbuf + strlen(dtmfbuf), "*", 3000); + if (res < 1) + analog_dsp_reset_and_flush_digits(p); + break; + } + default: + /* If we got the first digit, get the rest */ + len = 1; + dtmfbuf[len] = '\0'; + while ((len < AST_MAX_EXTENSION-1) && ast_matchmore_extension(chan, chan->context, dtmfbuf, 1, p->cid_num)) { + if (ast_exists_extension(chan, chan->context, dtmfbuf, 1, p->cid_num)) { + timeout = analog_matchdigittimeout; + } else { + timeout = analog_gendigittimeout; + } + res = ast_waitfordigit(chan, timeout); + if (res < 0) { + ast_debug(1, "waitfordigit returned < 0...\n"); + ast_hangup(chan); + goto quit; + } else if (res) { + dtmfbuf[len++] = res; + dtmfbuf[len] = '\0'; + } else { + break; + } + } + break; + } + } + if (res == -1) { + ast_log(LOG_WARNING, "getdtmf on channel %d: %s\n", p->channel, strerror(errno)); + ast_hangup(chan); + goto quit; + } else if (res < 0) { + ast_debug(1, "Got hung up before digits finished\n"); + ast_hangup(chan); + goto quit; + } + + if (p->sig == ANALOG_SIG_FGC_CAMA) { + char anibuf[100]; + + if (ast_safe_sleep(chan,1000) == -1) { + ast_hangup(chan); + goto quit; + } + analog_off_hook(p); + analog_dsp_set_digitmode(p, ANALOG_DIGITMODE_MF); + res = analog_my_getsigstr(chan, anibuf, "#", 10000); + if ((res > 0) && (strlen(anibuf) > 2)) { + if (anibuf[strlen(anibuf) - 1] == '#') + anibuf[strlen(anibuf) - 1] = 0; + ast_set_callerid(chan, anibuf + 2, NULL, anibuf + 2); + } + analog_dsp_set_digitmode(p, ANALOG_DIGITMODE_DTMF); + } + + ast_copy_string(exten, dtmfbuf, sizeof(exten)); + if (ast_strlen_zero(exten)) + ast_copy_string(exten, "s", sizeof(exten)); + if (p->sig == ANALOG_SIG_FEATD || p->sig == ANALOG_SIG_EMWINK) { + /* Look for Feature Group D on all E&M Wink and Feature Group D trunks */ + if (exten[0] == '*') { + char *stringp=NULL; + ast_copy_string(exten2, exten, sizeof(exten2)); + /* Parse out extension and callerid */ + stringp=exten2 +1; + s1 = strsep(&stringp, "*"); + s2 = strsep(&stringp, "*"); + if (s2) { + if (!ast_strlen_zero(p->cid_num)) + ast_set_callerid(chan, p->cid_num, NULL, p->cid_num); + else + ast_set_callerid(chan, s1, NULL, s1); + ast_copy_string(exten, s2, sizeof(exten)); + } else + ast_copy_string(exten, s1, sizeof(exten)); + } else if (p->sig == ANALOG_SIG_FEATD) + ast_log(LOG_WARNING, "Got a non-Feature Group D input on channel %d. Assuming E&M Wink instead\n", p->channel); + } + if ((p->sig == ANALOG_SIG_FEATDMF) || (p->sig == ANALOG_SIG_FEATDMF_TA)) { + if (exten[0] == '*') { + char *stringp=NULL; + ast_copy_string(exten2, exten, sizeof(exten2)); + /* Parse out extension and callerid */ + stringp=exten2 +1; + s1 = strsep(&stringp, "#"); + s2 = strsep(&stringp, "#"); + if (s2) { + if (!ast_strlen_zero(p->cid_num)) + ast_set_callerid(chan, p->cid_num, NULL, p->cid_num); + else + if (*(s1 + 2)) + ast_set_callerid(chan, s1 + 2, NULL, s1 + 2); + ast_copy_string(exten, s2 + 1, sizeof(exten)); + } else + ast_copy_string(exten, s1 + 2, sizeof(exten)); + } else + ast_log(LOG_WARNING, "Got a non-Feature Group D input on channel %d. Assuming E&M Wink instead\n", p->channel); + } + if ((p->sig == ANALOG_SIG_E911) || (p->sig == ANALOG_SIG_FGC_CAMAMF)) { + if (exten[0] == '*') { + char *stringp=NULL; + ast_copy_string(exten2, exten, sizeof(exten2)); + /* Parse out extension and callerid */ + stringp=exten2 +1; + s1 = strsep(&stringp, "#"); + s2 = strsep(&stringp, "#"); + if (s2 && (*(s2 + 1) == '0')) { + if (*(s2 + 2)) + ast_set_callerid(chan, s2 + 2, NULL, s2 + 2); + } + if (s1) ast_copy_string(exten, s1, sizeof(exten)); + else ast_copy_string(exten, "911", sizeof(exten)); + } else + ast_log(LOG_WARNING, "Got a non-E911/FGC CAMA input on channel %d. Assuming E&M Wink instead\n", p->channel); + } + if (p->sig == ANALOG_SIG_FEATB) { + if (exten[0] == '*') { + char *stringp=NULL; + ast_copy_string(exten2, exten, sizeof(exten2)); + /* Parse out extension and callerid */ + stringp=exten2 +1; + s1 = strsep(&stringp, "#"); + ast_copy_string(exten, exten2 + 1, sizeof(exten)); + } else + ast_log(LOG_WARNING, "Got a non-Feature Group B input on channel %d. Assuming E&M Wink instead\n", p->channel); + } + if ((p->sig == ANALOG_SIG_FEATDMF) || (p->sig == ANALOG_SIG_FEATDMF_TA)) { + analog_wink(p, index); + /* some switches require a minimum guard time between + the last FGD wink and something that answers + immediately. This ensures it */ + if (ast_safe_sleep(chan,100)) goto quit; + } + analog_set_echocanceller(p, 1); + + analog_dsp_set_digitmode(p, ANALOG_DIGITMODE_DTMF); + + if (ast_exists_extension(chan, chan->context, exten, 1, chan->cid.cid_num)) { + ast_copy_string(chan->exten, exten, sizeof(chan->exten)); + analog_dsp_reset_and_flush_digits(p); + res = ast_pbx_run(chan); + if (res) { + ast_log(LOG_WARNING, "PBX exited non-zero\n"); + res = analog_play_tone(p, index, ANALOG_TONE_CONGESTION); + } + goto quit; + } else { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_2 "Unknown extension '%s' in context '%s' requested\n", exten, chan->context); + sleep(2); + res = analog_play_tone(p, index, ANALOG_TONE_INFO); + if (res < 0) + ast_log(LOG_WARNING, "Unable to start special tone on %d\n", p->channel); + else + sleep(1); + res = ast_streamfile(chan, "ss-noservice", chan->language); + if (res >= 0) + ast_waitstream(chan, ""); + res = analog_play_tone(p, index, ANALOG_TONE_CONGESTION); + ast_hangup(chan); + goto quit; + } + break; + case ANALOG_SIG_FXOLS: + case ANALOG_SIG_FXOGS: + case ANALOG_SIG_FXOKS: + /* Read the first digit */ + timeout = analog_firstdigittimeout; + /* If starting a threeway call, never timeout on the first digit so someone + can use flash-hook as a "hold" feature */ + if (p->subs[ANALOG_SUB_THREEWAY].owner) + timeout = 999999; + while (len < AST_MAX_EXTENSION-1) { + /* Read digit unless it's supposed to be immediate, in which case the + only answer is 's' */ + if (p->immediate) + res = 's'; + else + res = ast_waitfordigit(chan, timeout); + timeout = 0; + if (res < 0) { + ast_debug(1, "waitfordigit returned < 0...\n"); + res = analog_play_tone(p, index, -1); + ast_hangup(chan); + goto quit; + } else if (res) { + exten[len++]=res; + exten[len] = '\0'; + } + if (!ast_ignore_pattern(chan->context, exten)) + analog_play_tone(p, index, -1); + else + analog_play_tone(p, index, ANALOG_TONE_DIALTONE); + if (ast_exists_extension(chan, chan->context, exten, 1, p->cid_num) && strcmp(exten, ast_parking_ext())) { + if (!res || !ast_matchmore_extension(chan, chan->context, exten, 1, p->cid_num)) { + if (getforward) { + /* Record this as the forwarding extension */ + ast_copy_string(p->call_forward, exten, sizeof(p->call_forward)); + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Setting call forward to '%s' on channel %d\n", p->call_forward, p->channel); + res = analog_play_tone(p, index, ANALOG_TONE_DIALRECALL); + if (res) + break; + usleep(500000); + res = analog_play_tone(p, index, -1); + sleep(1); + memset(exten, 0, sizeof(exten)); + res = analog_play_tone(p, index, ANALOG_TONE_DIALTONE); + len = 0; + getforward = 0; + } else { + res = analog_play_tone(p, index, -1); + ast_copy_string(chan->exten, exten, sizeof(chan->exten)); + if (!ast_strlen_zero(p->cid_num)) { + if (!p->hidecallerid) + ast_set_callerid(chan, p->cid_num, NULL, p->cid_num); + else + ast_set_callerid(chan, NULL, NULL, p->cid_num); + } + if (!ast_strlen_zero(p->cid_name)) { + if (!p->hidecallerid) + ast_set_callerid(chan, NULL, p->cid_name, NULL); + } + ast_setstate(chan, AST_STATE_RING); + analog_set_echocanceller(p, 1); + res = ast_pbx_run(chan); + if (res) { + ast_log(LOG_WARNING, "PBX exited non-zero\n"); + res = analog_play_tone(p, index, ANALOG_TONE_CONGESTION); + } + goto quit; + } + } else { + /* It's a match, but they just typed a digit, and there is an ambiguous match, + so just set the timeout to analog_matchdigittimeout and wait some more */ + timeout = analog_matchdigittimeout; + } + } else if (res == 0) { + ast_debug(1, "not enough digits (and no ambiguous match)...\n"); + res = analog_play_tone(p, index, ANALOG_TONE_CONGESTION); + analog_wait_event(p); + ast_hangup(chan); + goto quit; + } else if (p->callwaiting && !strcmp(exten, "*70")) { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Disabling call waiting on %s\n", chan->name); + /* Disable call waiting if enabled */ + p->callwaiting = 0; + res = analog_play_tone(p, index, ANALOG_TONE_DIALRECALL); + if (res) { + ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n", + chan->name, strerror(errno)); + } + len = 0; + memset(exten, 0, sizeof(exten)); + timeout = analog_firstdigittimeout; + + } else if (!strcmp(exten,ast_pickup_ext())) { + /* Scan all channels and see if there are any + * ringing channels that have call groups + * that equal this channels pickup group + */ + if (index == ANALOG_SUB_REAL) { + /* Switch us from Third call to Call Wait */ + if (p->subs[ANALOG_SUB_THREEWAY].owner) { + /* If you make a threeway call and the *8# a call, it should actually + look like a callwait */ + analog_alloc_sub(p, ANALOG_SUB_CALLWAIT); + analog_swap_subs(p, ANALOG_SUB_CALLWAIT, ANALOG_SUB_THREEWAY); + analog_unalloc_sub(p, ANALOG_SUB_THREEWAY); + } + analog_set_echocanceller(p, 1); + if (ast_pickup_call(chan)) { + ast_debug(1, "No call pickup possible...\n"); + res = analog_play_tone(p, index, ANALOG_TONE_CONGESTION); + analog_wait_event(p); + } + ast_hangup(chan); + goto quit; + } else { + ast_log(LOG_WARNING, "Huh? Got *8# on call not on real\n"); + ast_hangup(chan); + goto quit; + } + } else if (!p->hidecallerid && !strcmp(exten, "*67")) { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Disabling Caller*ID on %s\n", chan->name); + /* Disable Caller*ID if enabled */ + p->hidecallerid = 1; + if (chan->cid.cid_num) + free(chan->cid.cid_num); + chan->cid.cid_num = NULL; + if (chan->cid.cid_name) + free(chan->cid.cid_name); + chan->cid.cid_name = NULL; + res = analog_play_tone(p, index, ANALOG_TONE_DIALRECALL); + if (res) { + ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n", + chan->name, strerror(errno)); + } + len = 0; + memset(exten, 0, sizeof(exten)); + timeout = analog_firstdigittimeout; + } else if (p->callreturn && !strcmp(exten, "*69")) { + res = 0; + if (!ast_strlen_zero(p->lastcid_num)) { + res = ast_say_digit_str(chan, p->lastcid_num, "", chan->language); + } + if (!res) + res = analog_play_tone(p, index, ANALOG_TONE_DIALRECALL); + break; + } else if (!strcmp(exten, "*78")) { + /* Do not disturb */ + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Enabled DND on channel %d\n", p->channel); +#if 0 + manager_event(EVENT_FLAG_SYSTEM, "DNDState", + "Channel: %s/%d\r\n" + "Status: enabled\r\n", dahdi_chan_name, p->channel); +#endif + res = analog_play_tone(p, index, ANALOG_TONE_DIALRECALL); + p->dnd = 1; + getforward = 0; + memset(exten, 0, sizeof(exten)); + len = 0; + } else if (!strcmp(exten, "*79")) { + /* Do not disturb */ + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Disabled DND on channel %d\n", p->channel); +#if 0 + manager_event(EVENT_FLAG_SYSTEM, "DNDState", + "Channel: %s/%d\r\n" + "Status: disabled\r\n", dahdi_chan_name, p->channel); +#endif + res = analog_play_tone(p, index, ANALOG_TONE_DIALRECALL); + p->dnd = 0; + getforward = 0; + memset(exten, 0, sizeof(exten)); + len = 0; + } else if (p->cancallforward && !strcmp(exten, "*72")) { + res = analog_play_tone(p, index, ANALOG_TONE_DIALRECALL); + getforward = 1; + memset(exten, 0, sizeof(exten)); + len = 0; + } else if (p->cancallforward && !strcmp(exten, "*73")) { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Cancelling call forwarding on channel %d\n", p->channel); + res = analog_play_tone(p, index, ANALOG_TONE_DIALRECALL); + memset(p->call_forward, 0, sizeof(p->call_forward)); + getforward = 0; + memset(exten, 0, sizeof(exten)); + len = 0; + } else if ((p->transfer || p->canpark) && !strcmp(exten, ast_parking_ext()) && + p->subs[ANALOG_SUB_THREEWAY].owner && + ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)) { + /* This is a three way call, the main call being a real channel, + and we're parking the first call. */ + ast_masq_park_call(ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner), chan, 0, NULL); + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Parking call to '%s'\n", chan->name); + break; + } else if (!ast_strlen_zero(p->lastcid_num) && !strcmp(exten, "*60")) { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Blacklisting number %s\n", p->lastcid_num); + res = ast_db_put("blacklist", p->lastcid_num, "1"); + if (!res) { + res = analog_play_tone(p, index, ANALOG_TONE_DIALRECALL); + memset(exten, 0, sizeof(exten)); + len = 0; + } + } else if (p->hidecallerid && !strcmp(exten, "*82")) { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Enabling Caller*ID on %s\n", chan->name); + /* Enable Caller*ID if enabled */ + p->hidecallerid = 0; + if (chan->cid.cid_num) + free(chan->cid.cid_num); + chan->cid.cid_num = NULL; + if (chan->cid.cid_name) + free(chan->cid.cid_name); + chan->cid.cid_name = NULL; + ast_set_callerid(chan, p->cid_num, p->cid_name, NULL); + res = analog_play_tone(p, index, ANALOG_TONE_DIALRECALL); + if (res) { + ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n", + chan->name, strerror(errno)); + } + len = 0; + memset(exten, 0, sizeof(exten)); + timeout = analog_firstdigittimeout; + } else if (!strcmp(exten, "*0")) { +#ifdef XXX + struct ast_channel *nbridge = p->subs[ANALOG_SUB_THREEWAY].owner; + struct dahdi_pvt *pbridge = NULL; + /* set up the private struct of the bridged one, if any */ + if (nbridge && ast_bridged_channel(nbridge)) + pbridge = ast_bridged_channel(nbridge)->tech_pvt; + if (nbridge && pbridge && + (nbridge->tech == chan_tech) && + (ast_bridged_channel(nbridge)->tech == chan_tech) && + ISTRUNK(pbridge)) { + int func = DAHDI_FLASH; + /* Clear out the dial buffer */ + p->dop.dialstr[0] = '\0'; + /* flash hookswitch */ + if ((ioctl(pbridge->subs[ANALOG_SUB_REAL].dfd,DAHDI_HOOK,&func) == -1) && (errno != EINPROGRESS)) { + ast_log(LOG_WARNING, "Unable to flash external trunk on channel %s: %s\n", + nbridge->name, strerror(errno)); + } + analog_swap_subs(p, ANALOG_SUB_REAL, ANALOG_SUB_THREEWAY); + analog_unalloc_sub(p, ANALOG_SUB_THREEWAY); + p->owner = p->subs[ANALOG_SUB_REAL].owner; + if (ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner)) + ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_UNHOLD); + ast_hangup(chan); + goto quit; + } else { + analog_play_tone(p, index, ANALOG_TONE_CONGESTION); + analog_wait_event(p); + analog_play_tone(p, index, -1); + analog_swap_subs(p, ANALOG_SUB_REAL, ANALOG_SUB_THREEWAY); + analog_unalloc_sub(p, ANALOG_SUB_THREEWAY); + p->owner = p->subs[ANALOG_SUB_REAL].owner; + ast_hangup(chan); + goto quit; + } +#endif + } else if (!ast_canmatch_extension(chan, chan->context, exten, 1, chan->cid.cid_num) && + ((exten[0] != '*') || (strlen(exten) > 2))) { + ast_debug(1, "Can't match %s from '%s' in context %s\n", exten, chan->cid.cid_num ? chan->cid.cid_num : "", chan->context); + break; + } + if (!timeout) + timeout = analog_gendigittimeout; + if (len && !ast_ignore_pattern(chan->context, exten)) + analog_play_tone(p, index, -1); + } + break; + case ANALOG_SIG_FXSLS: + case ANALOG_SIG_FXSGS: + case ANALOG_SIG_FXSKS: + + /* If we want caller id, we're in a prering state due to a polarity reversal + * and we're set to use a polarity reversal to trigger the start of caller id, + * grab the caller id and wait for ringing to start... */ + if (p->use_callerid && (chan->_state == AST_STATE_PRERING && (p->cid_start == ANALOG_CID_START_POLARITY || p->cid_start == ANALOG_CID_START_POLARITY_IN))) { + /* If set to use DTMF CID signalling, listen for DTMF */ + if (p->cid_signalling == CID_SIG_DTMF) { + int i = 0; + cs = NULL; + ast_debug(1, "Receiving DTMF cid on " + "channel %s\n", chan->name); +#if 0 + dahdi_setlinear(p->subs[index].dfd, 0); +#endif + res = 2000; + for (;;) { + struct ast_frame *f; + res = ast_waitfor(chan, res); + if (res <= 0) { + ast_log(LOG_WARNING, "DTMFCID timed out waiting for ring. " + "Exiting simple switch\n"); + ast_hangup(chan); + goto quit; + } + f = ast_read(chan); + if (!f) + break; + if (f->frametype == AST_FRAME_DTMF) { + dtmfbuf[i++] = f->subclass; + ast_debug(1, "CID got digit '%c'\n", f->subclass); + res = 2000; + } + ast_frfree(f); + if (chan->_state == AST_STATE_RING || + chan->_state == AST_STATE_RINGING) + break; /* Got ring */ + } + dtmfbuf[i] = '\0'; +#if 0 + dahdi_setlinear(p->subs[index].dfd, p->subs[index].linear); +#endif + /* Got cid and ring. */ + ast_debug(1, "CID got string '%s'\n", dtmfbuf); + callerid_get_dtmf(dtmfbuf, dtmfcid, &flags); + ast_debug(1, "CID is '%s', flags %d\n", + dtmfcid, flags); + /* If first byte is NULL, we have no cid */ + if (!ast_strlen_zero(dtmfcid)) + number = dtmfcid; + else + number = NULL; +#if 0 + /* If set to use V23 Signalling, launch our FSK gubbins and listen for it */ + } else if ((p->cid_signalling == CID_SIG_V23) || (p->cid_signalling == CID_SIG_V23_JP)) { + cs = callerid_new(p->cid_signalling); + if (cs) { + samples = 0; +#if 1 + bump_gains(p); +#endif + /* Take out of linear mode for Caller*ID processing */ + dahdi_setlinear(p->subs[index].dfd, 0); + + /* First we wait and listen for the Caller*ID */ + for (;;) { + i = DAHDI_IOMUX_READ | DAHDI_IOMUX_SIGEVENT; + if ((res = ioctl(p->subs[index].dfd, DAHDI_IOMUX, &i))) { + ast_log(LOG_WARNING, "I/O MUX failed: %s\n", strerror(errno)); + callerid_free(cs); + ast_hangup(chan); + goto quit; + } + if (i & DAHDI_IOMUX_SIGEVENT) { + res = dahdi_get_event(p->subs[index].dfd); + ast_log(LOG_NOTICE, "Got event %d (%s)...\n", res, event2str(res)); + + if (p->cid_signalling == CID_SIG_V23_JP) { +#ifdef DAHDI_EVENT_RINGBEGIN + if (res == ANALOG_EVENT_RINGBEGIN) { + res = analog_off_hook(p); + usleep(1); + } +#endif + } else { + res = 0; + break; + } + } else if (i & DAHDI_IOMUX_READ) { + res = read(p->subs[index].dfd, buf, sizeof(buf)); + if (res < 0) { + if (errno != ELAST) { + ast_log(LOG_WARNING, "read returned error: %s\n", strerror(errno)); + callerid_free(cs); + ast_hangup(chan); + goto quit; + } + break; + } + samples += res; + + if (p->cid_signalling == CID_SIG_V23_JP) { + res = callerid_feed_jp(cs, buf, res, AST_LAW(p)); + } else { + res = callerid_feed(cs, buf, res, AST_LAW(p)); + } + + if (res < 0) { + ast_log(LOG_WARNING, "CallerID feed failed on channel '%s'\n", chan->name); + break; + } else if (res) + break; + else if (samples > (8000 * 10)) + break; + } + } + if (res == 1) { + callerid_get(cs, &name, &number, &flags); + ast_log(LOG_NOTICE, "CallerID number: %s, name: %s, flags=%d\n", number, name, flags); + } + + if (p->cid_signalling == CID_SIG_V23_JP) { + res = analog_on_hook(p); + usleep(1); + res = 4000; + } else { + + /* Finished with Caller*ID, now wait for a ring to make sure there really is a call coming */ + res = 2000; + } + + for (;;) { + struct ast_frame *f; + res = ast_waitfor(chan, res); + if (res <= 0) { + ast_log(LOG_WARNING, "CID timed out waiting for ring. " + "Exiting simple switch\n"); + ast_hangup(chan); + goto quit; + } + if (!(f = ast_read(chan))) { + ast_log(LOG_WARNING, "Hangup received waiting for ring. Exiting simple switch\n"); + ast_hangup(chan); + goto quit; + } + ast_frfree(f); + if (chan->_state == AST_STATE_RING || + chan->_state == AST_STATE_RINGING) + break; /* Got ring */ + } + + /* Restore linear mode (if appropriate) for Caller*ID processing */ + dahdi_setlinear(p->subs[index].dfd, p->subs[index].linear); +#if 1 + restore_gains(p); +#endif + } else + ast_log(LOG_WARNING, "Unable to get caller ID space\n"); +#endif + } else { + ast_log(LOG_WARNING, "Channel %s in prering " + "state, but I have nothing to do. " + "Terminating simple switch, should be " + "restarted by the actual ring.\n", + chan->name); + ast_hangup(chan); + goto quit; + } + } else if (p->use_callerid && p->cid_start == ANALOG_CID_START_RING) { + int timeout = 10000; /* Ten seconds */ + struct timeval start = ast_tvnow(); + enum analog_event ev; + + namebuf[0] = 0; + numbuf[0] = 0; + + if (!analog_start_cid_detect(p, p->cid_signalling)) { + while (1) { + res = analog_get_callerid(p, namebuf, numbuf, &ev, timeout - ast_tvdiff_ms(ast_tvnow(), start)); + + if (res == 0) { + break; + } + + if (res == 1) { + if (ev == ANALOG_EVENT_POLARITY && p->hanguponpolarityswitch && p->polarity == POLARITY_REV) { + ast_debug(1, "Hanging up due to polarity reversal on channel %d while detecting callerid\n", p->channel); + p->polarity = POLARITY_IDLE; + ast_hangup(chan); + goto quit; + } else if (ev != ANALOG_EVENT_NONE) { + break; + } + } + + if (ast_tvdiff_ms(ast_tvnow(), start) > timeout) + break; + + } + name = namebuf; + number = numbuf; + + analog_stop_cid_detect(p); + +#if 0 + /* XXX */ + if (strcmp(p->context,p->defcontext) != 0) { + ast_copy_string(p->context, p->defcontext, sizeof(p->context)); + ast_copy_string(chan->context,p->defcontext,sizeof(chan->context)); + } + + analog_get_callerid(p, name, number); + /* FSK Bell202 callerID */ + cs = callerid_new(p->cid_signalling); + if (cs) { +#if 1 + bump_gains(p); +#endif + samples = 0; + len = 0; + distMatches = 0; + /* Clear the current ring data array so we dont have old data in it. */ + for (receivedRingT = 0; receivedRingT < (sizeof(curRingData) / sizeof(curRingData[0])); receivedRingT++) + curRingData[receivedRingT] = 0; + receivedRingT = 0; + counter = 0; + counter1 = 0; + /* Check to see if context is what it should be, if not set to be. */ + + /* Take out of linear mode for Caller*ID processing */ + dahdi_setlinear(p->subs[index].dfd, 0); + for (;;) { + i = DAHDI_IOMUX_READ | DAHDI_IOMUX_SIGEVENT; + if ((res = ioctl(p->subs[index].dfd, DAHDI_IOMUX, &i))) { + ast_log(LOG_WARNING, "I/O MUX failed: %s\n", strerror(errno)); + callerid_free(cs); + ast_hangup(chan); + goto quit; + } + if (i & DAHDI_IOMUX_SIGEVENT) { + res = dahdi_get_event(p->subs[index].dfd); + ast_log(LOG_NOTICE, "Got event %d (%s)...\n", res, event2str(res)); + /* If we get a PR event, they hung up while processing calerid */ + if ( res == ANALOG_EVENT_POLARITY && p->hanguponpolarityswitch && p->polarity == POLARITY_REV) { + ast_debug(1, "Hanging up due to polarity reversal on channel %d while detecting callerid\n", p->channel); + p->polarity = POLARITY_IDLE; + callerid_free(cs); + ast_hangup(chan); + goto quit; + } + res = 0; + /* Let us detect callerid when the telco uses distinctive ring */ + + curRingData[receivedRingT] = p->ringt; + + if (p->ringt < p->ringt_base/2) + break; + /* Increment the ringT counter so we can match it against + values in chan_dahdi.conf for distinctive ring */ + if (++receivedRingT == (sizeof(curRingData) / sizeof(curRingData[0]))) + break; + } else if (i & DAHDI_IOMUX_READ) { + res = read(p->subs[index].dfd, buf, sizeof(buf)); + if (res < 0) { + if (errno != ELAST) { + ast_log(LOG_WARNING, "read returned error: %s\n", strerror(errno)); + callerid_free(cs); + ast_hangup(chan); + goto quit; + } + break; + } + if (p->ringt) + p->ringt--; + if (p->ringt == 1) { + res = -1; + break; + } + samples += res; + res = callerid_feed(cs, buf, res, AST_LAW(p)); + if (res < 0) { + ast_log(LOG_WARNING, "CallerID feed failed: %s\n", strerror(errno)); + break; + } else if (res) + break; + else if (samples > (8000 * 10)) + break; + } + } + if (res == 1) { + callerid_get(cs, &name, &number, &flags); + ast_debug(1, "CallerID number: %s, name: %s, flags=%d\n", number, name, flags); + } + /* Restore linear mode (if appropriate) for Caller*ID processing */ + dahdi_setlinear(p->subs[index].dfd, p->subs[index].linear); +#if 1 + restore_gains(p); +#endif + if (res < 0) { + ast_log(LOG_WARNING, "CallerID returned with error on channel '%s'\n", chan->name); + } + } else + ast_log(LOG_WARNING, "Unable to get caller ID space\n"); +#endif + } else + ast_log(LOG_WARNING, "Unable to get caller ID space\n"); + } + else + cs = NULL; + + if (number) + ast_shrink_phone_number(number); + ast_set_callerid(chan, number, name, number); + + if (cs) + callerid_free(cs); + + analog_handle_notify_message(chan, p, flags, -1); + + ast_setstate(chan, AST_STATE_RING); + chan->rings = 1; +#if 0 + p->ringt = p->ringt_base; +#endif + res = ast_pbx_run(chan); + if (res) { + ast_hangup(chan); + ast_log(LOG_WARNING, "PBX exited non-zero\n"); + } + goto quit; + default: + ast_log(LOG_WARNING, "Don't know how to handle simple switch with signalling %s on channel %d\n", analog_sigtype_to_str(p->sig), p->channel); + res = analog_play_tone(p, index, ANALOG_TONE_CONGESTION); + if (res < 0) + ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", p->channel); + } + res = analog_play_tone(p, index, ANALOG_TONE_CONGESTION); + if (res < 0) + ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", p->channel); + ast_hangup(chan); +quit: + analog_decrease_ss_count(p); + return NULL; +} + +int analog_ss_thread_start(struct analog_pvt *p, struct ast_channel *chan) +{ + pthread_t threadid; + return ast_pthread_create_detached(&threadid, NULL, __analog_ss_thread, chan); +} + +static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_channel *ast) +{ + int res, x; + int mysig; + enum analog_sub index; + char *c; + pthread_t threadid; + pthread_attr_t attr; + struct ast_channel *chan; + struct ast_frame *f; + ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel); + + index = analog_get_index(ast, p, 0); + mysig = p->sig; + if (p->outsigmod > -1) + mysig = p->outsigmod; + p->subs[index].f.frametype = AST_FRAME_NULL; + p->subs[index].f.subclass = 0; + p->subs[index].f.datalen = 0; + p->subs[index].f.samples = 0; + p->subs[index].f.mallocd = 0; + p->subs[index].f.offset = 0; + p->subs[index].f.src = "dahdi_handle_event"; + p->subs[index].f.data.ptr = NULL; + f = &p->subs[index].f; + + if (index < 0) + return &p->subs[index].f; + + if (index != ANALOG_SUB_REAL) { + ast_log(LOG_ERROR, "We got an event on a non real sub. Fix it!\n"); + } + + res = analog_get_event(p); + + ast_debug(1, "Got event %s(%d) on channel %d (index %d)\n", analog_event2str(res), res, p->channel, index); + + switch (res) { +#ifdef ANALOG_EVENT_EC_DISABLED + case ANALOG_EVENT_EC_DISABLED: + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Channel %d echo canceler disabled due to CED detection\n", p->channel); + p->echocanon = 0; + break; +#endif + case ANALOG_EVENT_PULSE_START: + /* Stop tone if there's a pulse start and the PBX isn't started */ + if (!ast->pbx) + analog_play_tone(p, ANALOG_SUB_REAL, -1); + break; + case ANALOG_EVENT_DIALCOMPLETE: + if (p->inalarm) break; + x = analog_is_dialing(p, index); + if (!x) { /* if not still dialing in driver */ + analog_set_echocanceller(p, 1); + if (p->echobreak) { + analog_train_echocanceller(p); + ast_copy_string(p->dop.dialstr, p->echorest, sizeof(p->dop.dialstr)); + p->dop.op = ANALOG_DIAL_OP_REPLACE; + analog_dial_digits(p, ANALOG_SUB_REAL, &p->dop); + p->echobreak = 0; + } else { + p->dialing = 0; + if ((mysig == ANALOG_SIG_E911) || (mysig == ANALOG_SIG_FGC_CAMA) || (mysig == ANALOG_SIG_FGC_CAMAMF)) { + /* if thru with dialing after offhook */ + if (ast->_state == AST_STATE_DIALING_OFFHOOK) { + ast_setstate(ast, AST_STATE_UP); + p->subs[index].f.frametype = AST_FRAME_CONTROL; + p->subs[index].f.subclass = AST_CONTROL_ANSWER; + break; + } else { /* if to state wait for offhook to dial rest */ + /* we now wait for off hook */ + ast_setstate(ast,AST_STATE_DIALING_OFFHOOK); + } + } + if (ast->_state == AST_STATE_DIALING) { + if ((!p->dialednone && ((mysig == ANALOG_SIG_EM) || (mysig == ANALOG_SIG_EM_E1) || (mysig == ANALOG_SIG_EMWINK) || (mysig == ANALOG_SIG_FEATD) || (mysig == ANALOG_SIG_FEATDMF_TA) || (mysig == ANALOG_SIG_FEATDMF) || (mysig == ANALOG_SIG_E911) || (mysig == ANALOG_SIG_FGC_CAMA) || (mysig == ANALOG_SIG_FGC_CAMAMF) || (mysig == ANALOG_SIG_FEATB) || (mysig == ANALOG_SIG_SF) || (mysig == ANALOG_SIG_SFWINK) || (mysig == ANALOG_SIG_SF_FEATD) || (mysig == ANALOG_SIG_SF_FEATDMF) || (mysig == ANALOG_SIG_SF_FEATB)))) { + ast_setstate(ast, AST_STATE_RINGING); + } else if (!p->answeronpolarityswitch) { + ast_setstate(ast, AST_STATE_UP); + p->subs[index].f.frametype = AST_FRAME_CONTROL; + p->subs[index].f.subclass = AST_CONTROL_ANSWER; + /* If aops=0 and hops=1, this is necessary */ + p->polarity = POLARITY_REV; + } else { + /* Start clean, so we can catch the change to REV polarity when party answers */ + p->polarity = POLARITY_IDLE; + } + } + } + } + break; + case ANALOG_EVENT_ALARM: + p->inalarm = 1; +#if 0 + res = get_alarms(p); + handle_alarms(p, res); +#endif + case ANALOG_EVENT_ONHOOK: + switch (p->sig) { + case ANALOG_SIG_FXOLS: + case ANALOG_SIG_FXOGS: + case ANALOG_SIG_FXOKS: + /* Check for some special conditions regarding call waiting */ + if (index == ANALOG_SUB_REAL) { + /* The normal line was hung up */ + if (p->subs[ANALOG_SUB_CALLWAIT].owner) { + /* There's a call waiting call, so ring the phone, but make it unowned in the mean time */ + analog_swap_subs(p, ANALOG_SUB_CALLWAIT, ANALOG_SUB_REAL); + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Channel %d still has (callwait) call, ringing phone\n", p->channel); + analog_unalloc_sub(p, ANALOG_SUB_CALLWAIT); + analog_stop_callwait(p); + p->owner = NULL; + /* Don't start streaming audio yet if the incoming call isn't up yet */ + if (p->subs[ANALOG_SUB_REAL].owner->_state != AST_STATE_UP) + p->dialing = 1; + analog_ring(p); + } else if (p->subs[ANALOG_SUB_THREEWAY].owner) { + unsigned int mssinceflash; + /* Here we have to retain the lock on both the main channel, the 3-way channel, and + the private structure -- not especially easy or clean */ + while (p->subs[ANALOG_SUB_THREEWAY].owner && ast_channel_trylock(p->subs[ANALOG_SUB_THREEWAY].owner)) { + /* Yuck, didn't get the lock on the 3-way, gotta release everything and re-grab! */ + analog_unlock_private(p); + CHANNEL_DEADLOCK_AVOIDANCE(ast); + /* We can grab ast and p in that order, without worry. We should make sure + nothing seriously bad has happened though like some sort of bizarre double + masquerade! */ + analog_lock_private(p); + if (p->owner != ast) { + ast_log(LOG_WARNING, "This isn't good...\n"); + return NULL; + } + } + if (!p->subs[ANALOG_SUB_THREEWAY].owner) { + ast_log(LOG_NOTICE, "Whoa, threeway disappeared kinda randomly.\n"); + return NULL; + } + mssinceflash = ast_tvdiff_ms(ast_tvnow(), p->flashtime); + ast_debug(1, "Last flash was %d ms ago\n", mssinceflash); + if (mssinceflash < MIN_MS_SINCE_FLASH) { + /* It hasn't been long enough since the last flashook. This is probably a bounce on + hanging up. Hangup both channels now */ + if (p->subs[ANALOG_SUB_THREEWAY].owner) + ast_queue_hangup(p->subs[ANALOG_SUB_THREEWAY].owner); + ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV); + ast_debug(1, "Looks like a bounced flash, hanging up both calls on %d\n", p->channel); + ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); + } else if ((ast->pbx) || (ast->_state == AST_STATE_UP)) { + if (p->transfer) { + /* In any case this isn't a threeway call anymore */ + p->subs[ANALOG_SUB_REAL].inthreeway = 0; + p->subs[ANALOG_SUB_THREEWAY].inthreeway = 0; + /* Only attempt transfer if the phone is ringing; why transfer to busy tone eh? */ + if (!p->transfertobusy && ast->_state == AST_STATE_BUSY) { + ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); + /* Swap subs and dis-own channel */ + analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL); + p->owner = NULL; + /* Ring the phone */ + analog_ring(p); + } else { + if ((res = analog_attempt_transfer(p)) < 0) { + ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV); + if (p->subs[ANALOG_SUB_THREEWAY].owner) + ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); + } else if (res) { + /* Don't actually hang up at this point */ + if (p->subs[ANALOG_SUB_THREEWAY].owner) + ast_channel_unlock(&p->subs[ANALOG_SUB_THREEWAY].owner); + break; + } + } + } else { + ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV); + if (p->subs[ANALOG_SUB_THREEWAY].owner) + ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); + } + } else { + ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); + /* Swap subs and dis-own channel */ + analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL); + p->owner = NULL; + /* Ring the phone */ + analog_ring(p); + } + } + } else { + ast_log(LOG_WARNING, "Got a hangup and my index is %d?\n", index); + } + /* Fall through */ + default: + analog_set_echocanceller(p, 0); + return NULL; + } + break; + case ANALOG_EVENT_RINGOFFHOOK: + if (p->inalarm) break; + /* for E911, its supposed to wait for offhook then dial + the second half of the dial string */ + if (((mysig == ANALOG_SIG_E911) || (mysig == ANALOG_SIG_FGC_CAMA) || (mysig == ANALOG_SIG_FGC_CAMAMF)) && (ast->_state == AST_STATE_DIALING_OFFHOOK)) { + c = strchr(p->dialdest, '/'); + if (c) + c++; + else + c = p->dialdest; + if (*c) snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*0%s#", c); + else ast_copy_string(p->dop.dialstr,"M*2#", sizeof(p->dop.dialstr)); + if (strlen(p->dop.dialstr) > 4) { + memset(p->echorest, 'w', sizeof(p->echorest) - 1); + strcpy(p->echorest + (p->echotraining / 401) + 1, p->dop.dialstr + strlen(p->dop.dialstr) - 2); + p->echorest[sizeof(p->echorest) - 1] = '\0'; + p->echobreak = 1; + p->dop.dialstr[strlen(p->dop.dialstr)-2] = '\0'; + } else + p->echobreak = 0; + if (analog_dial_digits(p, ANALOG_SUB_REAL, &p->dop)) { + int saveerr = errno; + analog_on_hook(p); + ast_log(LOG_WARNING, "Dialing failed on channel %d: %s\n", p->channel, strerror(saveerr)); + return NULL; + } + p->dialing = 1; + return &p->subs[index].f; + } + switch (p->sig) { + case ANALOG_SIG_FXOLS: + case ANALOG_SIG_FXOGS: + case ANALOG_SIG_FXOKS: + switch (ast->_state) { + case AST_STATE_RINGING: + analog_set_echocanceller(p, 1); + analog_train_echocanceller(p); + p->subs[index].f.frametype = AST_FRAME_CONTROL; + p->subs[index].f.subclass = AST_CONTROL_ANSWER; + /* Make sure it stops ringing */ + analog_off_hook(p); + ast_debug(1, "channel %d answered\n", p->channel); + p->dialing = 0; + p->callwaitcas = 0; + if (!ast_strlen_zero(p->dop.dialstr)) { + /* nick@dccinc.com 4/3/03 - fxo should be able to do deferred dialing */ + res = analog_dial_digits(p, ANALOG_SUB_REAL, &p->dop); + if (res < 0) { + ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", p->channel, strerror(errno)); + p->dop.dialstr[0] = '\0'; + return NULL; + } else { + ast_debug(1, "Sent FXO deferred digit string: %s\n", p->dop.dialstr); + p->subs[index].f.frametype = AST_FRAME_NULL; + p->subs[index].f.subclass = 0; + p->dialing = 1; + } + p->dop.dialstr[0] = '\0'; + ast_setstate(ast, AST_STATE_DIALING); + } else + ast_setstate(ast, AST_STATE_UP); + return &p->subs[index].f; + case AST_STATE_DOWN: + ast_setstate(ast, AST_STATE_RING); + ast->rings = 1; + p->subs[index].f.frametype = AST_FRAME_CONTROL; + p->subs[index].f.subclass = AST_CONTROL_OFFHOOK; + ast_debug(1, "channel %d picked up\n", p->channel); + return &p->subs[index].f; + case AST_STATE_UP: + /* Make sure it stops ringing */ + analog_off_hook(p); + /* Okay -- probably call waiting*/ + if (ast_bridged_channel(p->owner)) + ast_queue_control(p->owner, AST_CONTROL_UNHOLD); + break; + case AST_STATE_RESERVED: + /* Start up dialtone */ + if (analog_has_voicemail(p)) + res = analog_play_tone(p, ANALOG_SUB_REAL, ANALOG_TONE_STUTTER); + else + res = analog_play_tone(p, ANALOG_SUB_REAL, ANALOG_TONE_DIALTONE); + break; + default: + ast_log(LOG_WARNING, "FXO phone off hook in weird state %d??\n", ast->_state); + } + break; + case ANALOG_SIG_FXSLS: + case ANALOG_SIG_FXSGS: + case ANALOG_SIG_FXSKS: +#if 0 + if (ast->_state == AST_STATE_RING) { + p->ringt = p->ringt_base; + } +#endif + + /* Fall through */ + case ANALOG_SIG_EM: + case ANALOG_SIG_EM_E1: + case ANALOG_SIG_EMWINK: + case ANALOG_SIG_FEATD: + case ANALOG_SIG_FEATDMF: + case ANALOG_SIG_FEATDMF_TA: + case ANALOG_SIG_E911: + case ANALOG_SIG_FGC_CAMA: + case ANALOG_SIG_FGC_CAMAMF: + case ANALOG_SIG_FEATB: + case ANALOG_SIG_SF: + case ANALOG_SIG_SFWINK: + case ANALOG_SIG_SF_FEATD: + case ANALOG_SIG_SF_FEATDMF: + case ANALOG_SIG_SF_FEATB: + if (ast->_state == AST_STATE_PRERING) + ast_setstate(ast, AST_STATE_RING); + if ((ast->_state == AST_STATE_DOWN) || (ast->_state == AST_STATE_RING)) { + ast_debug(1, "Ring detected\n"); + p->subs[index].f.frametype = AST_FRAME_CONTROL; + p->subs[index].f.subclass = AST_CONTROL_RING; + } else if (p->outgoing && ((ast->_state == AST_STATE_RINGING) || (ast->_state == AST_STATE_DIALING))) { + ast_debug(1, "Line answered\n"); + p->subs[index].f.frametype = AST_FRAME_CONTROL; + p->subs[index].f.subclass = AST_CONTROL_ANSWER; + ast_setstate(ast, AST_STATE_UP); + } else if (ast->_state != AST_STATE_RING) + ast_log(LOG_WARNING, "Ring/Off-hook in strange state %d on channel %d\n", ast->_state, p->channel); + break; + default: + ast_log(LOG_WARNING, "Don't know how to handle ring/off hook for signalling %d\n", p->sig); + } + break; +#ifdef ANALOG_EVENT_RINGBEGIN + case ANALOG_EVENT_RINGBEGIN: + switch (p->sig) { + case ANALOG_SIG_FXSLS: + case ANALOG_SIG_FXSGS: + case ANALOG_SIG_FXSKS: +#if 0 + if (ast->_state == AST_STATE_RING) { + p->ringt = p->ringt_base; + } +#endif + break; + } + break; +#endif + case ANALOG_EVENT_RINGEROFF: + if (p->inalarm) break; + ast->rings++; + if (ast->rings == p->cidrings) { + analog_send_callerid(p, 0, &p->cid); + } + + if (ast->rings > p->cidrings) { + p->callwaitcas = 0; + } + p->subs[index].f.frametype = AST_FRAME_CONTROL; + p->subs[index].f.subclass = AST_CONTROL_RINGING; + break; + case ANALOG_EVENT_RINGERON: + break; + case ANALOG_EVENT_NOALARM: + p->inalarm = 0; + if (!p->unknown_alarm) { + ast_log(LOG_NOTICE, "Alarm cleared on channel %d\n", p->channel); + manager_event(EVENT_FLAG_SYSTEM, "AlarmClear", + "Channel: %d\r\n", p->channel); + } else { + p->unknown_alarm = 0; + } + break; + case ANALOG_EVENT_WINKFLASH: + if (p->inalarm) break; + /* Remember last time we got a flash-hook */ + gettimeofday(&p->flashtime, NULL); + switch (mysig) { + case ANALOG_SIG_FXOLS: + case ANALOG_SIG_FXOGS: + case ANALOG_SIG_FXOKS: +#if 0 + ast_debug(1, "Winkflash, index: %d, normal: %d, callwait: %d, thirdcall: %d\n", + index, p->subs[ANALOG_SUB_REAL].dfd, p->subs[ANALOG_SUB_CALLWAIT].dfd, p->subs[ANALOG_SUB_THREEWAY].dfd); +#endif + p->callwaitcas = 0; + + if (index != ANALOG_SUB_REAL) { + ast_log(LOG_WARNING, "Got flash hook with index %d on channel %d?!?\n", index, p->channel); + goto winkflashdone; + } + + if (p->subs[ANALOG_SUB_CALLWAIT].owner) { + /* Swap to call-wait */ + int previous_state = p->subs[ANALOG_SUB_CALLWAIT].owner->_state; + if (p->subs[ANALOG_SUB_CALLWAIT].owner->_state == AST_STATE_RINGING) { + ast_setstate(p->subs[ANALOG_SUB_CALLWAIT].owner, AST_STATE_UP); + } + analog_swap_subs(p, ANALOG_SUB_REAL, ANALOG_SUB_CALLWAIT); + analog_play_tone(p, ANALOG_SUB_REAL, -1); + p->owner = p->subs[ANALOG_SUB_REAL].owner; + ast_debug(1, "Making %s the new owner\n", p->owner->name); + if (previous_state == AST_STATE_RINGING) { + ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_ANSWER); + } + analog_stop_callwait(p); + /* Start music on hold if appropriate */ + if (!p->subs[ANALOG_SUB_CALLWAIT].inthreeway && ast_bridged_channel(p->subs[ANALOG_SUB_CALLWAIT].owner)) { + ast_queue_control_data(p->subs[ANALOG_SUB_CALLWAIT].owner, AST_CONTROL_HOLD, + S_OR(p->mohsuggest, NULL), + !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); + } + ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_ANSWER); + if (ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner)) { + ast_queue_control_data(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_HOLD, + S_OR(p->mohsuggest, NULL), + !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); + } + ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_UNHOLD); + } else if (!p->subs[ANALOG_SUB_THREEWAY].owner) { + char cid_num[256]; + char cid_name[256]; + + if (!p->threewaycalling) { + /* Just send a flash if no 3-way calling */ + ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_FLASH); + goto winkflashdone; + } else if (!analog_check_for_conference(p)) { + if (p->dahditrcallerid && p->owner) { + if (p->owner->cid.cid_num) + ast_copy_string(cid_num, p->owner->cid.cid_num, sizeof(cid_num)); + if (p->owner->cid.cid_name) + ast_copy_string(cid_name, p->owner->cid.cid_name, sizeof(cid_name)); + } + /* XXX This section needs much more error checking!!! XXX */ + /* Start a 3-way call if feasible */ + if (!((ast->pbx) || + (ast->_state == AST_STATE_UP) || + (ast->_state == AST_STATE_RING))) { + ast_debug(1, "Flash when call not up or ringing\n"); + goto winkflashdone; + } + if (analog_alloc_sub(p, ANALOG_SUB_THREEWAY)) { + ast_log(LOG_WARNING, "Unable to allocate three-way subchannel\n"); + goto winkflashdone; + } + /* Make new channel */ + chan = analog_new_ast_channel(p, AST_STATE_RESERVED, 0, ANALOG_SUB_THREEWAY); + if (p->dahditrcallerid) { + if (!p->origcid_num) + p->origcid_num = ast_strdup(p->cid_num); + if (!p->origcid_name) + p->origcid_name = ast_strdup(p->cid_name); + ast_copy_string(p->cid_num, cid_num, sizeof(p->cid_num)); + ast_copy_string(p->cid_name, cid_name, sizeof(p->cid_name)); + } + /* Swap things around between the three-way and real call */ + analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL); + /* Disable echo canceller for better dialing */ + analog_set_echocanceller(p, 0); + res = analog_play_tone(p, ANALOG_SUB_REAL, ANALOG_TONE_DIALRECALL); + if (res) + ast_log(LOG_WARNING, "Unable to start dial recall tone on channel %d\n", p->channel); + p->ss_astchan = p->owner = chan; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + if (!chan) { + ast_log(LOG_WARNING, "Cannot allocate new structure on channel %d\n", p->channel); + } else if (ast_pthread_create(&threadid, &attr, __analog_ss_thread, p)) { + ast_log(LOG_WARNING, "Unable to start simple switch on channel %d\n", p->channel); + res = analog_play_tone(p, ANALOG_SUB_REAL, ANALOG_TONE_CONGESTION); + analog_set_echocanceller(p, 1); + ast_hangup(chan); + } else { + struct ast_channel *other = ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner); + int way3bridge = 0, cdr3way = 0; + + if (!other) { + other = ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner); + } else + way3bridge = 1; + + if (p->subs[ANALOG_SUB_THREEWAY].owner->cdr) + cdr3way = 1; + + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Started three way call on channel %d\n", p->channel); + /* Start music on hold if appropriate */ + if (ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)) { + ast_queue_control_data(p->subs[ANALOG_SUB_THREEWAY].owner, AST_CONTROL_HOLD, + S_OR(p->mohsuggest, NULL), + !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); + } + } + pthread_attr_destroy(&attr); + } + } else { + /* Already have a 3 way call */ + if (p->subs[ANALOG_SUB_THREEWAY].inthreeway) { + /* Call is already up, drop the last person */ + ast_debug(1, "Got flash with three way call up, dropping last call on %d\n", p->channel); + /* If the primary call isn't answered yet, use it */ + if ((p->subs[ANALOG_SUB_REAL].owner->_state != AST_STATE_UP) && (p->subs[ANALOG_SUB_THREEWAY].owner->_state == AST_STATE_UP)) { + /* Swap back -- we're dropping the real 3-way that isn't finished yet*/ + analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL); + p->owner = p->subs[ANALOG_SUB_REAL].owner; + } + /* Drop the last call and stop the conference */ + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Dropping three-way call on %s\n", p->subs[ANALOG_SUB_THREEWAY].owner->name); + ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV); + p->subs[ANALOG_SUB_REAL].inthreeway = 0; + p->subs[ANALOG_SUB_THREEWAY].inthreeway = 0; + } else { + /* Lets see what we're up to */ + if (((ast->pbx) || (ast->_state == AST_STATE_UP)) && + (p->transfertobusy || (ast->_state != AST_STATE_BUSY))) { + int otherindex = ANALOG_SUB_THREEWAY; + struct ast_channel *other = ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner); + int way3bridge = 0, cdr3way = 0; + + if (!other) { + other = ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner); + } else + way3bridge = 1; + + if (p->subs[ANALOG_SUB_THREEWAY].owner->cdr) + cdr3way = 1; + + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Building conference on call on %s and %s\n", p->subs[ANALOG_SUB_THREEWAY].owner->name, p->subs[ANALOG_SUB_REAL].owner->name); + /* Put them in the threeway, and flip */ + p->subs[ANALOG_SUB_THREEWAY].inthreeway = 1; + p->subs[ANALOG_SUB_REAL].inthreeway = 1; + if (ast->_state == AST_STATE_UP) { + analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL); + otherindex = ANALOG_SUB_REAL; + } + if (p->subs[otherindex].owner && ast_bridged_channel(p->subs[otherindex].owner)) + ast_queue_control(p->subs[otherindex].owner, AST_CONTROL_UNHOLD); + p->owner = p->subs[ANALOG_SUB_REAL].owner; + if (ast->_state == AST_STATE_RINGING) { + ast_debug(1, "Enabling ringtone on real and threeway\n"); + analog_play_tone(p, ANALOG_SUB_REAL, ANALOG_TONE_RINGTONE); + analog_play_tone(p, ANALOG_SUB_THREEWAY, ANALOG_TONE_RINGTONE); + } + } else { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Dumping incomplete call on on %s\n", p->subs[ANALOG_SUB_THREEWAY].owner->name); + analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL); + ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV); + p->owner = p->subs[ANALOG_SUB_REAL].owner; + if (p->subs[ANALOG_SUB_REAL].owner && ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner)) + ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_UNHOLD); + analog_set_echocanceller(p, 1); + } + } + } + winkflashdone: + analog_update_conf(p); + break; + case ANALOG_SIG_EM: + case ANALOG_SIG_EM_E1: + case ANALOG_SIG_EMWINK: + case ANALOG_SIG_FEATD: + case ANALOG_SIG_SF: + case ANALOG_SIG_SFWINK: + case ANALOG_SIG_SF_FEATD: + case ANALOG_SIG_FXSLS: + case ANALOG_SIG_FXSGS: + if (p->dialing) + ast_debug(1, "Ignoring wink on channel %d\n", p->channel); + else + ast_debug(1, "Got wink in weird state %d on channel %d\n", ast->_state, p->channel); + break; + case ANALOG_SIG_FEATDMF_TA: + switch (p->whichwink) { + case 0: + ast_debug(1, "ANI2 set to '%d' and ANI is '%s'\n", p->owner->cid.cid_ani2, p->owner->cid.cid_ani); + snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*%d%s#", p->owner->cid.cid_ani2, p->owner->cid.cid_ani); + break; + case 1: + ast_copy_string(p->dop.dialstr, p->finaldial, sizeof(p->dop.dialstr)); + break; + case 2: + ast_log(LOG_WARNING, "Received unexpected wink on channel of type ANALOG_SIG_FEATDMF_TA\n"); + return NULL; + } + p->whichwink++; + /* Fall through */ + case ANALOG_SIG_FEATDMF: + case ANALOG_SIG_E911: + case ANALOG_SIG_FGC_CAMAMF: + case ANALOG_SIG_FGC_CAMA: + case ANALOG_SIG_FEATB: + case ANALOG_SIG_SF_FEATDMF: + case ANALOG_SIG_SF_FEATB: + /* FGD MF *Must* wait for wink */ + if (!ast_strlen_zero(p->dop.dialstr)) { + res = analog_dial_digits(p, ANALOG_SUB_REAL, &p->dop); + if (res < 0) { + ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", p->channel, strerror(errno)); + p->dop.dialstr[0] = '\0'; + return NULL; + } else + ast_debug(1, "Sent deferred digit string on channel %d: %s\n", p->channel, p->dop.dialstr); + } + p->dop.dialstr[0] = '\0'; + break; + default: + ast_log(LOG_WARNING, "Don't know how to handle ring/off hoook for signalling %d\n", p->sig); + } + break; + case ANALOG_EVENT_HOOKCOMPLETE: + if (p->inalarm) break; + switch (mysig) { + case ANALOG_SIG_FXSLS: /* only interesting for FXS */ + case ANALOG_SIG_FXSGS: + case ANALOG_SIG_FXSKS: + case ANALOG_SIG_EM: + case ANALOG_SIG_EM_E1: + case ANALOG_SIG_EMWINK: + case ANALOG_SIG_FEATD: + case ANALOG_SIG_SF: + case ANALOG_SIG_SFWINK: + case ANALOG_SIG_SF_FEATD: + if (!ast_strlen_zero(p->dop.dialstr)) { + res = analog_dial_digits(p, ANALOG_SUB_REAL, &p->dop); + if (res < 0) { + ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", p->channel, strerror(errno)); + p->dop.dialstr[0] = '\0'; + return NULL; + } else + ast_debug(1, "Sent deferred digit string on channel %d: %s\n", p->channel, p->dop.dialstr); + } + p->dop.dialstr[0] = '\0'; + p->dop.op = ANALOG_DIAL_OP_REPLACE; + break; + case ANALOG_SIG_FEATDMF: + case ANALOG_SIG_FEATDMF_TA: + case ANALOG_SIG_E911: + case ANALOG_SIG_FGC_CAMA: + case ANALOG_SIG_FGC_CAMAMF: + case ANALOG_SIG_FEATB: + case ANALOG_SIG_SF_FEATDMF: + case ANALOG_SIG_SF_FEATB: + ast_debug(1, "Got hook complete in MF FGD, waiting for wink now on channel %d\n",p->channel); + break; + default: + break; + } + break; + case ANALOG_EVENT_POLARITY: + /* + * If we get a Polarity Switch event, check to see + * if we should change the polarity state and + * mark the channel as UP or if this is an indication + * of remote end disconnect. + */ + if (p->polarity == POLARITY_IDLE) { + p->polarity = POLARITY_REV; + if (p->answeronpolarityswitch && + ((ast->_state == AST_STATE_DIALING) || + (ast->_state == AST_STATE_RINGING))) { + ast_debug(1, "Answering on polarity switch!\n"); + ast_setstate(p->owner, AST_STATE_UP); + if (p->hanguponpolarityswitch) { + gettimeofday(&p->polaritydelaytv, NULL); + } + } else + ast_debug(1, "Ignore switch to REVERSED Polarity on channel %d, state %d\n", p->channel, ast->_state); + } + /* Removed else statement from here as it was preventing hangups from ever happening*/ + /* Added AST_STATE_RING in if statement below to deal with calling party hangups that take place when ringing */ + if (p->hanguponpolarityswitch && + (p->polarityonanswerdelay > 0) && + (p->polarity == POLARITY_REV) && + ((ast->_state == AST_STATE_UP) || (ast->_state == AST_STATE_RING)) ) { + /* Added log_debug information below to provide a better indication of what is going on */ + ast_debug(1, "Polarity Reversal event occured - DEBUG 1: channel %d, state %d, pol= %d, aonp= %d, honp= %d, pdelay= %d, tv= %d\n", p->channel, ast->_state, p->polarity, p->answeronpolarityswitch, p->hanguponpolarityswitch, p->polarityonanswerdelay, ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) ); + + if (ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) > p->polarityonanswerdelay) { + ast_debug(1, "Polarity Reversal detected and now Hanging up on channel %d\n", p->channel); + ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT); + p->polarity = POLARITY_IDLE; + } else { + ast_debug(1, "Polarity Reversal detected but NOT hanging up (too close to answer event) on channel %d, state %d\n", p->channel, ast->_state); + } + } else { + p->polarity = POLARITY_IDLE; + ast_debug(1, "Ignoring Polarity switch to IDLE on channel %d, state %d\n", p->channel, ast->_state); + } + /* Added more log_debug information below to provide a better indication of what is going on */ + ast_debug(1, "Polarity Reversal event occured - DEBUG 2: channel %d, state %d, pol= %d, aonp= %d, honp= %d, pdelay= %d, tv= %d\n", p->channel, ast->_state, p->polarity, p->answeronpolarityswitch, p->hanguponpolarityswitch, p->polarityonanswerdelay, ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) ); + break; + default: + ast_debug(1, "Dunno what to do with event %d on channel %d\n", res, p->channel); + } + return &p->subs[index].f; +} + +struct ast_frame *analog_exception(struct analog_pvt *p, struct ast_channel *ast) +{ + int res; + int usedindex=-1; + int index; + struct ast_frame *f; + + ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel); + + index = analog_get_index(ast, p, 1); + + p->subs[index].f.frametype = AST_FRAME_NULL; + p->subs[index].f.datalen = 0; + p->subs[index].f.samples = 0; + p->subs[index].f.mallocd = 0; + p->subs[index].f.offset = 0; + p->subs[index].f.subclass = 0; + p->subs[index].f.delivery = ast_tv(0,0); + p->subs[index].f.src = "dahdi_exception"; + p->subs[index].f.data.ptr = NULL; + + + if (!p->owner) { + /* If nobody owns us, absorb the event appropriately, otherwise + we loop indefinitely. This occurs when, during call waiting, the + other end hangs up our channel so that it no longer exists, but we + have neither FLASH'd nor ONHOOK'd to signify our desire to + change to the other channel. */ + res = analog_get_event(p); + + /* Switch to real if there is one and this isn't something really silly... */ + if ((res != ANALOG_EVENT_RINGEROFF) && (res != ANALOG_EVENT_RINGERON) && + (res != ANALOG_EVENT_HOOKCOMPLETE)) { + ast_debug(1, "Restoring owner of channel %d on event %d\n", p->channel, res); + p->owner = p->subs[ANALOG_SUB_REAL].owner; + if (p->owner && ast_bridged_channel(p->owner)) + ast_queue_control(p->owner, AST_CONTROL_UNHOLD); + } + switch (res) { + case ANALOG_EVENT_ONHOOK: + analog_set_echocanceller(p, 0); + if (p->owner) { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Channel %s still has call, ringing phone\n", p->owner->name); + analog_ring(p); + analog_stop_callwait(p); + } else + ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n"); + analog_update_conf(p); + break; + case ANALOG_EVENT_RINGOFFHOOK: + analog_set_echocanceller(p, 1); + analog_off_hook(p); + if (p->owner && (p->owner->_state == AST_STATE_RINGING)) { + ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_ANSWER); + p->dialing = 0; + } + break; + case ANALOG_EVENT_HOOKCOMPLETE: + case ANALOG_EVENT_RINGERON: + case ANALOG_EVENT_RINGEROFF: + /* Do nothing */ + break; + case ANALOG_EVENT_WINKFLASH: + gettimeofday(&p->flashtime, NULL); + if (p->owner) { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Channel %d flashed to other channel %s\n", p->channel, p->owner->name); + if (p->owner->_state != AST_STATE_UP) { + /* Answer if necessary */ + usedindex = analog_get_index(p->owner, p, 0); + if (usedindex > -1) { + ast_queue_control(p->subs[usedindex].owner, AST_CONTROL_ANSWER); + } + ast_setstate(p->owner, AST_STATE_UP); + } + analog_stop_callwait(p); + if (ast_bridged_channel(p->owner)) + ast_queue_control(p->owner, AST_CONTROL_UNHOLD); + } else + ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n"); + analog_update_conf(p); + break; + default: + ast_log(LOG_WARNING, "Don't know how to absorb event %s\n", analog_event2str(res)); + } + f = &p->subs[index].f; + return f; + } + ast_debug(1, "Exception on %d, channel %d\n", ast->fds[0],p->channel); + /* If it's not us, return NULL immediately */ + if (ast != p->owner) { + ast_log(LOG_WARNING, "We're %s, not %s\n", ast->name, p->owner->name); + f = &p->subs[index].f; + return f; + } + f = __analog_handle_event(p, ast); + return f; +} + +int analog_handle_init_event(struct analog_pvt *i, int event) +{ + int res; + pthread_t threadid; + pthread_attr_t attr; + struct ast_channel *chan; + + ast_debug(1, "channel (%d) - signaling (%d) - event (%s)\n", + i->channel, i->sig, analog_event2str(event)); + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + /* Handle an event on a given channel for the monitor thread. */ + switch (event) { + case ANALOG_EVENT_WINKFLASH: + case ANALOG_EVENT_RINGOFFHOOK: + if (i->inalarm) break; + /* Got a ring/answer. What kind of channel are we? */ + switch (i->sig) { + case ANALOG_SIG_FXOLS: + case ANALOG_SIG_FXOGS: + case ANALOG_SIG_FXOKS: + res = analog_off_hook(i); + if (res && (errno == EBUSY)) + break; + if (i->immediate) { + analog_set_echocanceller(i, 1); + /* The channel is immediately up. Start right away */ + res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_TONE_RINGTONE); + chan = analog_new_ast_channel(i, AST_STATE_RING, 1, ANALOG_SUB_REAL); + if (!chan) { + ast_log(LOG_WARNING, "Unable to start PBX on channel %d\n", i->channel); + res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_TONE_CONGESTION); + if (res < 0) + ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel); + } + } else { + /* Check for callerid, digits, etc */ + chan = analog_new_ast_channel(i, AST_STATE_RESERVED, 0, ANALOG_SUB_REAL); + i->ss_astchan = chan; + if (chan) { + if (analog_has_voicemail(i)) + res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_TONE_STUTTER); + else + res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_TONE_DIALTONE); + if (res < 0) + ast_log(LOG_WARNING, "Unable to play dialtone on channel %d, do you have defaultzone and loadzone defined?\n", i->channel); + if (ast_pthread_create(&threadid, &attr, __analog_ss_thread, i)) { + ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel); + res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_TONE_CONGESTION); + if (res < 0) + ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel); + ast_hangup(chan); + } + } else + ast_log(LOG_WARNING, "Unable to create channel\n"); + } + break; + case ANALOG_SIG_FXSLS: + case ANALOG_SIG_FXSGS: + case ANALOG_SIG_FXSKS: +#if 0 + i->ringt = i->ringt_base; +#endif + /* Fall through */ + case ANALOG_SIG_EMWINK: + case ANALOG_SIG_FEATD: + case ANALOG_SIG_FEATDMF: + case ANALOG_SIG_FEATDMF_TA: + case ANALOG_SIG_E911: + case ANALOG_SIG_FGC_CAMA: + case ANALOG_SIG_FGC_CAMAMF: + case ANALOG_SIG_FEATB: + case ANALOG_SIG_EM: + case ANALOG_SIG_EM_E1: + case ANALOG_SIG_SFWINK: + case ANALOG_SIG_SF_FEATD: + case ANALOG_SIG_SF_FEATDMF: + case ANALOG_SIG_SF_FEATB: + case ANALOG_SIG_SF: + /* Check for callerid, digits, etc */ + if (i->cid_start == ANALOG_CID_START_POLARITY_IN) { + chan = analog_new_ast_channel(i, AST_STATE_PRERING, 0, ANALOG_SUB_REAL); + } else { + chan = analog_new_ast_channel(i, AST_STATE_RING, 0, ANALOG_SUB_REAL); + } + i->ss_astchan = chan; + if (chan && ast_pthread_create(&threadid, &attr, __analog_ss_thread, i)) { + ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel); + res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_TONE_CONGESTION); + if (res < 0) + ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel); + ast_hangup(chan); + } else if (!chan) { + ast_log(LOG_WARNING, "Cannot allocate new structure on channel %d\n", i->channel); + } + break; + default: + ast_log(LOG_WARNING, "Don't know how to handle ring/answer with signalling %s on channel %d\n", analog_sigtype_to_str(i->sig), i->channel); + res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_TONE_CONGESTION); + if (res < 0) + ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel); + return -1; + } + break; + case ANALOG_EVENT_NOALARM: + i->inalarm = 0; + if (!i->unknown_alarm) { + ast_log(LOG_NOTICE, "Alarm cleared on channel %d\n", i->channel); + manager_event(EVENT_FLAG_SYSTEM, "AlarmClear", + "Channel: %d\r\n", i->channel); + } else { + i->unknown_alarm = 0; + } + break; + case ANALOG_EVENT_ALARM: + i->inalarm = 1; +#if 0 + res = get_alarms(i); + handle_alarms(i, res); +#endif + /* fall thru intentionally */ + case ANALOG_EVENT_ONHOOK: + /* Back on hook. Hang up. */ + switch (i->sig) { + case ANALOG_SIG_FXOLS: + case ANALOG_SIG_FXOGS: + case ANALOG_SIG_FEATD: + case ANALOG_SIG_FEATDMF: + case ANALOG_SIG_FEATDMF_TA: + case ANALOG_SIG_E911: + case ANALOG_SIG_FGC_CAMA: + case ANALOG_SIG_FGC_CAMAMF: + case ANALOG_SIG_FEATB: + case ANALOG_SIG_EM: + case ANALOG_SIG_EM_E1: + case ANALOG_SIG_EMWINK: + case ANALOG_SIG_SF_FEATD: + case ANALOG_SIG_SF_FEATDMF: + case ANALOG_SIG_SF_FEATB: + case ANALOG_SIG_SF: + case ANALOG_SIG_SFWINK: + case ANALOG_SIG_FXSLS: + case ANALOG_SIG_FXSGS: + case ANALOG_SIG_FXSKS: + analog_set_echocanceller(i, 0); + res = analog_play_tone(i, ANALOG_SUB_REAL, -1); + analog_on_hook(i); + break; + case ANALOG_SIG_FXOKS: + analog_set_echocanceller(i, 0); + /* Diddle the battery for the zhone */ +#ifdef ZHONE_HACK + analog_off_hook(i); + usleep(1); +#endif + res = analog_play_tone(i, ANALOG_SUB_REAL, -1); + analog_on_hook(i); + break; + default: + ast_log(LOG_WARNING, "Don't know how to handle on hook with signalling %s on channel %d\n", analog_sigtype_to_str(i->sig), i->channel); + res = analog_play_tone(i, ANALOG_SUB_REAL, -1); + return -1; + } + break; + case ANALOG_EVENT_POLARITY: + switch (i->sig) { + case ANALOG_SIG_FXSLS: + case ANALOG_SIG_FXSKS: + case ANALOG_SIG_FXSGS: + /* We have already got a PR before the channel was + created, but it wasn't handled. We need polarity + to be REV for remote hangup detection to work. + At least in Spain */ + if (i->hanguponpolarityswitch) + i->polarity = POLARITY_REV; + + if (i->cid_start == ANALOG_CID_START_POLARITY || i->cid_start == ANALOG_CID_START_POLARITY_IN) { + i->polarity = POLARITY_REV; + ast_verbose(VERBOSE_PREFIX_2 "Starting post polarity " + "CID detection on channel %d\n", + i->channel); + chan = analog_new_ast_channel(i, AST_STATE_PRERING, 0, ANALOG_SUB_REAL); + i->ss_astchan = chan; + if (chan && ast_pthread_create(&threadid, &attr, __analog_ss_thread, i)) { + ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel); + } + } + break; + default: + ast_log(LOG_WARNING, "handle_init_event detected " + "polarity reversal on non-FXO (ANALOG_SIG_FXS) " + "interface %d\n", i->channel); + } + break; + case ANALOG_EVENT_NEONMWI_ACTIVE: + analog_handle_notify_message(NULL, i, -1, ANALOG_EVENT_NEONMWI_ACTIVE); + break; + case ANALOG_EVENT_NEONMWI_INACTIVE: + analog_handle_notify_message(NULL, i, -1, ANALOG_EVENT_NEONMWI_INACTIVE); + break; + } + pthread_attr_destroy(&attr); + return 0; +} + + +struct analog_pvt * analog_new(enum analog_sigtype signallingtype, struct analog_callback *c, void *private_data) +{ + struct analog_pvt *p; + + p = ast_calloc(1, sizeof(*p)); + + if (!p) + return p; + + p->calls = c; + p->outsigmod = ANALOG_SIG_NONE; + p->sig = signallingtype; + p->chan_pvt = private_data; + + /* Some defaults for values */ + p->sendcalleridafter = 1; + p->cid_start = ANALOG_CID_START_RING; + p->cid_signalling = CID_SIG_BELL; + /* Sub real is assumed to always be alloc'd */ + p->subs[ANALOG_SUB_REAL].allocd = 1; + + return p; +} + +int analog_config_complete(struct analog_pvt *p) +{ + /* No call waiting on non FXS channels */ + if ((p->sig != ANALOG_SIG_FXOKS) && (p->sig != ANALOG_SIG_FXOLS) && (p->sig != ANALOG_SIG_FXOGS)) + p->permcallwaiting = 0; + + p->callwaiting = p->permcallwaiting; + + return 0; +} + +void analog_free(struct analog_pvt *p) +{ + free(p); +} + +/* called while dahdi_pvt is locked in dahdi_fixup */ +int analog_fixup(struct ast_channel *oldchan, struct ast_channel *newchan, void *newp) +{ + struct analog_pvt *new_pvt = newp; + int x; + ast_debug(1, "New owner for channel %d is %s\n", new_pvt->channel, newchan->name); + if (new_pvt->owner == oldchan) { + new_pvt->owner = newchan; + } + for (x = 0; x < 3; x++) + if (new_pvt->subs[x].owner == oldchan) { + new_pvt->subs[x].owner = newchan; + } + + analog_update_conf(new_pvt); + return 0; +} Property changes on: channels/sig_analog.c ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + Author Date Id Revision Index: channels/chan_h323.c =================================================================== --- a/channels/chan_h323.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/chan_h323.c (.../trunk) (revision 202568) @@ -76,7 +76,7 @@ #include "asterisk/utils.h" #include "asterisk/sched.h" #include "asterisk/io.h" -#include "asterisk/rtp.h" +#include "asterisk/rtp_engine.h" #include "asterisk/acl.h" #include "asterisk/callerid.h" #include "asterisk/cli.h" @@ -147,7 +147,7 @@ static call_options_t global_options; /*! \brief Private structure of a OpenH323 channel */ -struct oh323_pvt { +static struct oh323_pvt { ast_mutex_t lock; /*!< Channel private lock */ call_options_t options; /*!update_rtp_info > 0) { if (pvt->rtp) { ast_jb_configure(c, &global_jbconf); - ast_channel_set_fd(c, 0, ast_rtp_fd(pvt->rtp)); - ast_channel_set_fd(c, 1, ast_rtcp_fd(pvt->rtp)); + ast_channel_set_fd(c, 0, ast_rtp_instance_fd(pvt->rtp, 0)); + ast_channel_set_fd(c, 1, ast_rtp_instance_fd(pvt->rtp, 1)); ast_queue_frame(pvt->owner, &ast_null_frame); /* Tell Asterisk to apply changes */ } pvt->update_rtp_info = -1; @@ -446,7 +446,7 @@ AST_SCHED_DEL(sched, pvt->DTMFsched); if (pvt->rtp) { - ast_rtp_destroy(pvt->rtp); + ast_rtp_instance_destroy(pvt->rtp); } /* Free dsp used for in-band DTMF detection */ @@ -512,7 +512,7 @@ if (h323debug) { ast_log(LOG_DTMF, "Begin sending out-of-band digit %c on %s\n", digit, c->name); } - ast_rtp_senddigit_begin(pvt->rtp, digit); + ast_rtp_instance_dtmf_begin(pvt->rtp, digit); ast_mutex_unlock(&pvt->lock); } else if (pvt->txDtmfDigit != digit) { /* in-band DTMF */ @@ -551,7 +551,7 @@ if (h323debug) { ast_log(LOG_DTMF, "End sending out-of-band digit %c on %s, duration %d\n", digit, c->name, duration); } - ast_rtp_senddigit_end(pvt->rtp, digit); + ast_rtp_instance_dtmf_end(pvt->rtp, digit); ast_mutex_unlock(&pvt->lock); } else { /* in-band DTMF */ @@ -608,18 +608,18 @@ /* make sure null terminated */ called_addr[sizeof(called_addr) - 1] = '\0'; - if (c->cid.cid_num) - ast_copy_string(pvt->options.cid_num, c->cid.cid_num, sizeof(pvt->options.cid_num)); + if (c->connected.id.number) + ast_copy_string(pvt->options.cid_num, c->connected.id.number, sizeof(pvt->options.cid_num)); - if (c->cid.cid_name) - ast_copy_string(pvt->options.cid_name, c->cid.cid_name, sizeof(pvt->options.cid_name)); + if (c->connected.id.name) + ast_copy_string(pvt->options.cid_name, c->connected.id.name, sizeof(pvt->options.cid_name)); if (c->cid.cid_rdnis) { ast_copy_string(pvt->options.cid_rdnis, c->cid.cid_rdnis, sizeof(pvt->options.cid_rdnis)); } - pvt->options.presentation = c->cid.cid_pres; - pvt->options.type_of_number = c->cid.cid_ton; + pvt->options.presentation = c->connected.id.number_presentation; + pvt->options.type_of_number = c->connected.id.number_type; if ((addr = pbx_builtin_getvar_helper(c, "PRIREDIRECTREASON"))) { if (!strcasecmp(addr, "UNKNOWN")) @@ -749,11 +749,11 @@ /* Only apply it for the first packet, we just need the correct ip/port */ if (pvt->options.nat) { - ast_rtp_setnat(pvt->rtp, pvt->options.nat); + ast_rtp_instance_set_prop(pvt->rtp, AST_RTP_PROPERTY_NAT, pvt->options.nat); pvt->options.nat = 0; } - f = ast_rtp_read(pvt->rtp); + f = ast_rtp_instance_read(pvt->rtp, 0); /* Don't send RFC2833 if we're not supposed to */ if (f && (f->frametype == AST_FRAME_DTMF) && !(pvt->options.dtmfmode & (H323_DTMF_RFC2833 | H323_DTMF_CISCO))) { return &ast_null_frame; @@ -810,7 +810,7 @@ break; case 1: if (pvt->rtp) - fr = ast_rtcp_read(pvt->rtp); + fr = ast_rtp_instance_read(pvt->rtp, 1); else fr = &ast_null_frame; break; @@ -844,7 +844,7 @@ if (pvt) { ast_mutex_lock(&pvt->lock); if (pvt->rtp && !pvt->recvonly) - res = ast_rtp_write(pvt->rtp, frame); + res = ast_rtp_instance_write(pvt->rtp, frame); __oh323_update_info(c, pvt); ast_mutex_unlock(&pvt->lock); } @@ -912,7 +912,7 @@ res = 0; break; case AST_CONTROL_SRCUPDATE: - ast_rtp_new_source(pvt->rtp); + ast_rtp_instance_new_source(pvt->rtp); res = 0; break; case AST_CONTROL_PROCEEDING: @@ -948,17 +948,17 @@ static int __oh323_rtp_create(struct oh323_pvt *pvt) { - struct in_addr our_addr; + struct sockaddr_in our_addr; if (pvt->rtp) return 0; - if (ast_find_ourip(&our_addr, bindaddr)) { + if (ast_find_ourip(&our_addr.sin_addr, bindaddr)) { ast_mutex_unlock(&pvt->lock); ast_log(LOG_ERROR, "Unable to locate local IP address for RTP stream\n"); return -1; } - pvt->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, our_addr); + pvt->rtp = ast_rtp_instance_new(NULL, sched, &our_addr, NULL); if (!pvt->rtp) { ast_mutex_unlock(&pvt->lock); ast_log(LOG_WARNING, "Unable to create RTP session: %s\n", strerror(errno)); @@ -967,24 +967,24 @@ if (h323debug) ast_debug(1, "Created RTP channel\n"); - ast_rtp_setqos(pvt->rtp, tos, cos, "H323 RTP"); + ast_rtp_instance_set_qos(pvt->rtp, tos, cos, "H323 RTP"); if (h323debug) ast_debug(1, "Setting NAT on RTP to %d\n", pvt->options.nat); - ast_rtp_setnat(pvt->rtp, pvt->options.nat); + ast_rtp_instance_set_prop(pvt->rtp, AST_RTP_PROPERTY_NAT, pvt->options.nat); if (pvt->dtmf_pt[0] > 0) - ast_rtp_set_rtpmap_type(pvt->rtp, pvt->dtmf_pt[0], "audio", "telephone-event", 0); + ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(pvt->rtp), pvt->rtp, pvt->dtmf_pt[0], "audio", "telephone-event", 0); if (pvt->dtmf_pt[1] > 0) - ast_rtp_set_rtpmap_type(pvt->rtp, pvt->dtmf_pt[1], "audio", "cisco-telephone-event", 0); + ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(pvt->rtp), pvt->rtp, pvt->dtmf_pt[1], "audio", "cisco-telephone-event", 0); if (pvt->peercapability) - ast_rtp_codec_setpref(pvt->rtp, &pvt->peer_prefs); + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(pvt->rtp), pvt->rtp, &pvt->peer_prefs); if (pvt->owner && !ast_channel_trylock(pvt->owner)) { ast_jb_configure(pvt->owner, &global_jbconf); - ast_channel_set_fd(pvt->owner, 0, ast_rtp_fd(pvt->rtp)); - ast_channel_set_fd(pvt->owner, 1, ast_rtcp_fd(pvt->rtp)); + ast_channel_set_fd(pvt->owner, 0, ast_rtp_instance_fd(pvt->rtp, 0)); + ast_channel_set_fd(pvt->owner, 1, ast_rtp_instance_fd(pvt->rtp, 1)); ast_queue_frame(pvt->owner, &ast_null_frame); /* Tell Asterisk to apply changes */ ast_channel_unlock(pvt->owner); } else @@ -1030,13 +1030,13 @@ if (!pvt->rtp) __oh323_rtp_create(pvt); #if 0 - ast_channel_set_fd(ch, 0, ast_rtp_fd(pvt->rtp)); - ast_channel_set_fd(ch, 1, ast_rtcp_fd(pvt->rtp)); + ast_channel_set_fd(ch, 0, ast_rtp_instance_fd(pvt->rtp, 0)); + ast_channel_set_fd(ch, 1, ast_rtp_instance_fd(pvt->rtp, 1)); #endif #ifdef VIDEO_SUPPORT if (pvt->vrtp) { - ast_channel_set_fd(ch, 2, ast_rtp_fd(pvt->vrtp)); - ast_channel_set_fd(ch, 3, ast_rtcp_fd(pvt->vrtp)); + ast_channel_set_fd(ch, 2, ast_rtp_instance_fd(pvt->vrtp, 0)); + ast_channel_set_fd(ch, 3, ast_rtp_instance_fd(pvt->vrtp, 1)); } #endif #ifdef T38_SUPPORT @@ -1114,7 +1114,7 @@ } if (!pvt->cd.call_token) { ast_log(LOG_ERROR, "Not enough memory to alocate call token\n"); - ast_rtp_destroy(pvt->rtp); + ast_rtp_instance_destroy(pvt->rtp); ast_free(pvt); return NULL; } @@ -1916,7 +1916,7 @@ return NULL; } /* figure out our local RTP port and tell the H.323 stack about it */ - ast_rtp_get_us(pvt->rtp, &us); + ast_rtp_instance_get_local_address(pvt->rtp, &us); ast_mutex_unlock(&pvt->lock); ast_copy_string(info->addr, ast_inet_ntoa(us.sin_addr), sizeof(info->addr)); @@ -1935,7 +1935,6 @@ { struct oh323_pvt *pvt; struct sockaddr_in them; - struct rtpPayloadType rtptype; int nativeformats_changed; enum { NEED_NONE, NEED_HOLD, NEED_UNHOLD } rtp_change = NEED_NONE; @@ -1957,7 +1956,7 @@ __oh323_rtp_create(pvt); if ((pt == 2) && (pvt->jointcapability & AST_FORMAT_G726_AAL2)) { - ast_rtp_set_rtpmap_type(pvt->rtp, pt, "audio", "G726-32", AST_RTP_OPT_G726_NONSTANDARD); + ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(pvt->rtp), pvt->rtp, pt, "audio", "G726-32", AST_RTP_OPT_G726_NONSTANDARD); } them.sin_family = AF_INET; @@ -1966,13 +1965,13 @@ them.sin_port = htons(remotePort); if (them.sin_addr.s_addr) { - ast_rtp_set_peer(pvt->rtp, &them); + ast_rtp_instance_set_remote_address(pvt->rtp, &them); if (pvt->recvonly) { pvt->recvonly = 0; rtp_change = NEED_UNHOLD; } } else { - ast_rtp_stop(pvt->rtp); + ast_rtp_instance_stop(pvt->rtp); if (!pvt->recvonly) { pvt->recvonly = 1; rtp_change = NEED_HOLD; @@ -1982,7 +1981,7 @@ /* Change native format to reflect information taken from OLC/OLCAck */ nativeformats_changed = 0; if (pt != 128 && pvt->rtp) { /* Payload type is invalid, so try to use previously decided */ - rtptype = ast_rtp_lookup_pt(pvt->rtp, pt); + struct ast_rtp_payload_type rtptype = ast_rtp_codecs_payload_lookup(ast_rtp_instance_get_codecs(pvt->rtp), pt); if (h323debug) ast_debug(1, "Native format is set to %d from %d by RTP payload type %d\n", rtptype.code, pvt->nativeformats, pt); if (pvt->nativeformats != rtptype.code) { @@ -2363,7 +2362,7 @@ } if (pvt->rtp) { /* Immediately stop RTP */ - ast_rtp_destroy(pvt->rtp); + ast_rtp_instance_destroy(pvt->rtp); pvt->rtp = NULL; } /* Free dsp used for in-band DTMF detection */ @@ -2425,7 +2424,7 @@ return; } if (pvt->rtp) { - ast_rtp_set_rtpmap_type(pvt->rtp, payload, "audio", (is_cisco ? "cisco-telephone-event" : "telephone-event"), 0); + ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(pvt->rtp), pvt->rtp, payload, "audio", (is_cisco ? "cisco-telephone-event" : "telephone-event"), 0); } pvt->dtmf_pt[is_cisco ? 1 : 0] = payload; ast_mutex_unlock(&pvt->lock); @@ -2458,10 +2457,10 @@ if (pvt->rtp) { if (pvt->options.autoframing) { ast_debug(2, "Autoframing option set, using peer's packetization settings\n"); - ast_rtp_codec_setpref(pvt->rtp, &pvt->peer_prefs); + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(pvt->rtp), pvt->rtp, &pvt->peer_prefs); } else { ast_debug(2, "Autoframing option not set, ignoring peer's packetization settings\n"); - ast_rtp_codec_setpref(pvt->rtp, &pvt->options.prefs); + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(pvt->rtp), pvt->rtp, &pvt->options.prefs); } } } @@ -3132,19 +3131,19 @@ static struct ast_cli_entry cli_h323_reload = AST_CLI_DEFINE(handle_cli_h323_reload, "Reload H.323 configuration"); -static enum ast_rtp_get_result oh323_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) +static enum ast_rtp_glue_result oh323_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) { struct oh323_pvt *pvt; - enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL; + enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_LOCAL; if (!(pvt = (struct oh323_pvt *)chan->tech_pvt)) - return AST_RTP_GET_FAILED; + return AST_RTP_GLUE_RESULT_FORBID; ast_mutex_lock(&pvt->lock); - *rtp = pvt->rtp; + *instance = pvt->rtp ? ao2_ref(pvt->rtp, +1), pvt->rtp : NULL; #if 0 if (pvt->options.bridge) { - res = AST_RTP_TRY_NATIVE; + res = AST_RTP_GLUE_RESULT_REMOTE; } #endif ast_mutex_unlock(&pvt->lock); @@ -3152,11 +3151,6 @@ return res; } -static enum ast_rtp_get_result oh323_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) -{ - return AST_RTP_GET_FAILED; -} - static char *convertcap(int cap) { switch (cap) { @@ -3184,12 +3178,12 @@ } } -static int oh323_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) +static int oh323_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *trtp, int codecs, int nat_active) { /* XXX Deal with Video */ struct oh323_pvt *pvt; - struct sockaddr_in them; - struct sockaddr_in us; + struct sockaddr_in them = { 0, }; + struct sockaddr_in us = { 0, }; char *mode; if (!rtp) { @@ -3202,19 +3196,18 @@ ast_log(LOG_ERROR, "No Private Structure, this is bad\n"); return -1; } - ast_rtp_get_peer(rtp, &them); - ast_rtp_get_us(rtp, &us); + ast_rtp_instance_get_remote_address(rtp, &them); + ast_rtp_instance_get_local_address(rtp, &us); #if 0 /* Native bridge still isn't ready */ h323_native_bridge(pvt->cd.call_token, ast_inet_ntoa(them.sin_addr), mode); #endif return 0; } -static struct ast_rtp_protocol oh323_rtp = { +static struct ast_rtp_glue oh323_rtp_glue = { .type = "H323", .get_rtp_info = oh323_get_rtp_peer, - .get_vrtp_info = oh323_get_vrtp_peer, - .set_rtp_peer = oh323_set_rtp_peer, + .update_peer = oh323_set_rtp_peer, }; static enum ast_module_load_result load_module(void) @@ -3269,7 +3262,7 @@ } ast_cli_register_multiple(cli_h323, sizeof(cli_h323) / sizeof(struct ast_cli_entry)); - ast_rtp_proto_register(&oh323_rtp); + ast_rtp_glue_register(&oh323_rtp_glue); /* Register our callback functions */ h323_callback_register(setup_incoming_call, @@ -3290,7 +3283,7 @@ /* start the h.323 listener */ if (h323_start_listener(h323_signalling_port, bindaddr)) { ast_log(LOG_ERROR, "Unable to create H323 listener.\n"); - ast_rtp_proto_unregister(&oh323_rtp); + ast_rtp_glue_unregister(&oh323_rtp_glue); ast_cli_unregister_multiple(cli_h323, sizeof(cli_h323) / sizeof(struct ast_cli_entry)); ast_cli_unregister(&cli_h323_reload); h323_end_process(); @@ -3329,7 +3322,7 @@ ast_cli_unregister(&cli_h323_reload); ast_channel_unregister(&oh323_tech); - ast_rtp_proto_unregister(&oh323_rtp); + ast_rtp_glue_unregister(&oh323_rtp_glue); if (!ast_mutex_lock(&iflock)) { /* hangup all interfaces if they have an owner */ Index: channels/sig_analog.h =================================================================== --- a/channels/sig_analog.h (.../tags/1.6.2.0-beta3) (revision 0) +++ b/channels/sig_analog.h (.../trunk) (revision 202568) @@ -0,0 +1,329 @@ +#ifndef _SIG_ANALOG_H +#define _SIG_ANALOG_H +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2009, Digium, Inc. + * + * Mark Spencer + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Interface header for analog signaling module + * + * \author Matthew Fredrickson + */ + +#include "asterisk/channel.h" +#include "asterisk/frame.h" + +/* Signalling types supported */ +enum analog_sigtype { + ANALOG_SIG_NONE = -1, + ANALOG_SIG_FXOLS = 1, + ANALOG_SIG_FXOKS, + ANALOG_SIG_FXOGS, + ANALOG_SIG_FXSLS, + ANALOG_SIG_FXSKS, + ANALOG_SIG_FXSGS, + ANALOG_SIG_EMWINK, + ANALOG_SIG_EM, + ANALOG_SIG_EM_E1, + ANALOG_SIG_FEATD, + ANALOG_SIG_FEATDMF, + ANALOG_SIG_E911, + ANALOG_SIG_FGC_CAMA, + ANALOG_SIG_FGC_CAMAMF, + ANALOG_SIG_FEATB, + ANALOG_SIG_SFWINK, + ANALOG_SIG_SF, + ANALOG_SIG_SF_FEATD, + ANALOG_SIG_SF_FEATDMF, + ANALOG_SIG_FEATDMF_TA, + ANALOG_SIG_SF_FEATB, +}; + +enum analog_tone { + ANALOG_TONE_RINGTONE = 0, + ANALOG_TONE_STUTTER, + ANALOG_TONE_CONGESTION, + ANALOG_TONE_DIALTONE, + ANALOG_TONE_DIALRECALL, + ANALOG_TONE_INFO, +}; + +enum analog_event { + ANALOG_EVENT_NONE = 0, + ANALOG_EVENT_DIALCOMPLETE, + ANALOG_EVENT_WINKFLASH, + ANALOG_EVENT_ONHOOK, + ANALOG_EVENT_RINGOFFHOOK, + ANALOG_EVENT_ALARM, + ANALOG_EVENT_NOALARM, + ANALOG_EVENT_HOOKCOMPLETE, + ANALOG_EVENT_POLARITY, + ANALOG_EVENT_RINGERON, + ANALOG_EVENT_RINGEROFF, + ANALOG_EVENT_RINGBEGIN, + ANALOG_EVENT_PULSE_START, + ANALOG_EVENT_ERROR, + ANALOG_EVENT_NEONMWI_ACTIVE, + ANALOG_EVENT_NEONMWI_INACTIVE, +}; + +enum analog_sub { + ANALOG_SUB_REAL = 0, /*!< Active call */ + ANALOG_SUB_CALLWAIT, /*!< Call-Waiting call on hold */ + ANALOG_SUB_THREEWAY, /*!< Three-way call */ +}; + +enum analog_dsp_digitmode { + ANALOG_DIGITMODE_DTMF = 1, + ANALOG_DIGITMODE_MF, +}; + +enum analog_cid_start { + ANALOG_CID_START_POLARITY = 1, + ANALOG_CID_START_POLARITY_IN, + ANALOG_CID_START_RING, +}; + +#define ANALOG_MAX_CID 300 + +enum dialop { + ANALOG_DIAL_OP_REPLACE = 2, +}; + + +struct analog_dialoperation { + enum dialop op; + char dialstr[256]; +}; + +struct analog_callback { + /* Unlock the private in the signalling private structure. This is used for three way calling madness. */ + void (* const unlock_private)(void *pvt); + /* Lock the private in the signalling private structure. ... */ + void (* const lock_private)(void *pvt); + /* Function which is called back to handle any other DTMF up events that are received. Called by analog_handle_event. Why is this + * important to use, instead of just directly using events received before they are passed into the library? Because sometimes, + * (CWCID) the library absorbs DTMF events received. */ + void (* const handle_dtmfup)(void *pvt, struct ast_channel *ast, enum analog_sub analog_index, struct ast_frame **dest); + + int (* const get_event)(void *pvt); + int (* const wait_event)(void *pvt); + int (* const is_off_hook)(void *pvt); + int (* const is_dialing)(void *pvt, enum analog_sub sub); + /* Start a trunk type signalling protocol (everything except phone ports basically */ + int (* const start)(void *pvt); + int (* const ring)(void *pvt); + int (* const flash)(void *pvt); + /*! \brief Set channel on hook */ + int (* const on_hook)(void *pvt); + /*! \brief Set channel off hook */ + int (* const off_hook)(void *pvt); + /* We're assuming that we're going to only wink on ANALOG_SUB_REAL - even though in the code there's an argument to the index + * function */ + int (* const wink)(void *pvt, enum analog_sub sub); + int (* const dial_digits)(void *pvt, enum analog_sub sub, struct analog_dialoperation *dop); + int (* const send_fsk)(void *pvt, struct ast_channel *ast, char *fsk); + int (* const play_tone)(void *pvt, enum analog_sub sub, enum analog_tone tone); + + int (* const set_echocanceller)(void *pvt, int enable); + int (* const train_echocanceller)(void *pvt); + int (* const dsp_set_digitmode)(void *pvt, enum analog_dsp_digitmode mode); + int (* const dsp_reset_and_flush_digits)(void *pvt); + int (* const send_callerid)(void *pvt, int cwcid, struct ast_callerid *cid); + /* Returns 0 if CID received. Returns 1 if event received, and -1 if error. name and num are size ANALOG_MAX_CID */ + int (* const get_callerid)(void *pvt, char *name, char *num, enum analog_event *ev, size_t timeout); + /* Start CID detection */ + int (* const start_cid_detect)(void *pvt, int cid_signalling); + /* Stop CID detection */ + int (* const stop_cid_detect)(void *pvt); + + /* Play the CAS callwait tone on the REAL sub, then repeat after 10 seconds, and then stop */ + int (* const callwait)(void *pvt); + /* Stop playing any CAS call waiting announcement tones that might be running on the REAL sub */ + int (* const stop_callwait)(void *pvt); + + /* Bearer control related (non signalling) callbacks */ + int (* const allocate_sub)(void *pvt, enum analog_sub sub); + int (* const unallocate_sub)(void *pvt, enum analog_sub sub); + /*! This function is for swapping of the owners with the underlying subs. Typically it means you need to change the fds + * of the new owner to be the fds of the sub specified, for each of the two subs given */ + void (* const swap_subs)(void *pvt, enum analog_sub a, struct ast_channel *new_a_owner, enum analog_sub b, struct ast_channel *new_b_owner); + struct ast_channel * (* const new_ast_channel)(void *pvt, int state, int startpbx, enum analog_sub sub); + + /* Add the given sub to a conference */ + int (* const conf_add)(void *pvt, enum analog_sub sub); + /* Delete the given sub from any conference that might be running on the channels */ + int (* const conf_del)(void *pvt, enum analog_sub sub); + + /* If you would like to do any optimizations after the conference members have been added and removed, + * you can do so here */ + int (* const complete_conference_update)(void *pvt, int needconf); + + /* This is called when there are no more subchannels on the given private that are left up, + * for any cleanup or whatever else you would like to do. Called from analog_hangup() */ + void (* const all_subchannels_hungup)(void *pvt); + + int (* const has_voicemail)(void *pvt); + int (* const check_for_conference)(void *pvt); + void (* const handle_notify_message)(struct ast_channel *chan, void *pvt, int cid_flags, int neon_mwievent); + + /* callbacks for increasing and decreasing ss_thread_count, will handle locking and condition signal */ + void (* const increase_ss_count)(void); + void (* const decrease_ss_count)(void); +}; + + + +#define READ_SIZE 160 + +struct analog_subchannel { + struct ast_channel *owner; + struct ast_frame f; /*!< One frame for each channel. How did this ever work before? */ + unsigned int needcallerid:1; + unsigned int inthreeway:1; + /* Have we allocated a subchannel yet or not */ + unsigned int allocd:1; +}; + +struct analog_pvt { + /* Analog signalling type used in this private */ + enum analog_sigtype sig; + /* To contain the private structure passed into the channel callbacks */ + void *chan_pvt; + /* Callbacks for various functions needed by the analog API */ + struct analog_callback *calls; + /* All members after this are giong to be transient, and most will probably change */ + struct ast_channel *owner; /*!< Our current active owner (if applicable) */ + + struct analog_subchannel subs[3]; /*!< Sub-channels */ + struct analog_dialoperation dop; + + /* XXX: Option Variables - Set by allocator of private structure */ + unsigned int answeronpolarityswitch:1; + unsigned int callreturn:1; + unsigned int cancallforward:1; + unsigned int canpark:1; + unsigned int dahditrcallerid:1; /*!< should we use the callerid from incoming call on dahdi transfer or not */ + unsigned int hanguponpolarityswitch:1; + unsigned int immediate:1; + unsigned int permcallwaiting:1; + unsigned int permhidecallerid:1; /*!< Whether to hide our outgoing caller ID or not */ + unsigned int pulse:1; + unsigned int threewaycalling:1; + unsigned int transfer:1; + unsigned int transfertobusy:1; /*!< allow flash-transfers to busy channels */ + unsigned int use_callerid:1; /*!< Whether or not to use caller id on this channel */ + + /* Not used for anything but log messages. Could be just the TCID */ + int channel; /*!< Channel Number or CRV */ + enum analog_sigtype outsigmod; + int echotraining; + int cid_signalling; /*!< Asterisk callerid type we're using */ + int polarityonanswerdelay; + int stripmsd; + enum analog_cid_start cid_start; + /* Number of rings to wait to send callerid on FXS. Set to 1 for US */ + int sendcalleridafter; + int callwaitingcallerid; + char mohsuggest[MAX_MUSICCLASS]; + char cid_num[AST_MAX_EXTENSION]; + char cid_name[AST_MAX_EXTENSION]; + + + /* XXX: All variables after this are internal */ + unsigned int callwaiting:1; + unsigned int dialednone:1; + unsigned int dialing:1; + unsigned int dnd:1; + unsigned int echobreak:1; + unsigned int hidecallerid:1; + unsigned int outgoing:1; + unsigned int pulsedial:1; /*!< whether a pulse dial phone is detected */ + + char callwait_num[AST_MAX_EXTENSION]; + char callwait_name[AST_MAX_EXTENSION]; + char lastcid_num[AST_MAX_EXTENSION]; + char lastcid_name[AST_MAX_EXTENSION]; + struct ast_callerid cid; + int cidrings; /*!< Which ring to deliver CID on */ + char echorest[20]; + int polarity; + struct timeval polaritydelaytv; + char dialdest[256]; + time_t guardtime; /*!< Must wait this much time before using for new call */ + struct timeval flashtime; /*!< Last flash-hook time */ + int whichwink; /*!< SIG_FEATDMF_TA Which wink are we on? */ + char finaldial[64]; + char *origcid_num; /*!< malloced original callerid */ + char *origcid_name; /*!< malloced original callerid */ + char call_forward[AST_MAX_EXTENSION]; + + /* Ast channel to pass to __ss_analog_thread */ + void *ss_astchan; + + /* All variables after this are definitely going to be audited */ + unsigned int inalarm:1; // + unsigned int unknown_alarm:1;// + + int callwaitcas; + +#if 0 + int ringt; // + int ringt_base; +#endif +}; + +struct analog_pvt * analog_new(enum analog_sigtype signallingtype, struct analog_callback *c, void *private_data); + +void analog_free(struct analog_pvt *p); + +int analog_call(struct analog_pvt *p, struct ast_channel *ast, char *rdest, int timeout); + +int analog_hangup(struct analog_pvt *p, struct ast_channel *ast); + +int analog_answer(struct analog_pvt *p, struct ast_channel *ast); + +struct ast_frame *analog_exception(struct analog_pvt *p, struct ast_channel *ast); + +struct ast_channel * analog_request(struct analog_pvt *p, int *callwait); + +int analog_available(struct analog_pvt *p, int channelmatch, ast_group_t groupmatch, int *busy, int *channelmatched, int *groupmatched); + +int analog_handle_init_event(struct analog_pvt *i, int event); + +int analog_config_complete(struct analog_pvt *p); + +void analog_handle_dtmfup(struct analog_pvt *p, struct ast_channel *ast, enum analog_sub index, struct ast_frame **dest); + +enum analog_cid_start analog_str_to_cidstart(const char *value); + +const char *analog_cidstart_to_str(enum analog_cid_start cid_start); + +enum analog_sigtype analog_str_to_sigtype(const char *name); + +const char *analog_sigtype_to_str(enum analog_sigtype sigtype); + +unsigned int analog_str_to_cidtype(const char *name); + +const char *analog_cidtype_to_str(unsigned int cid_type); + +int analog_ss_thread_start(struct analog_pvt *p, struct ast_channel *ast); + +int analog_fixup(struct ast_channel *oldchan, struct ast_channel *newchan, void *newp); + +#endif /* _SIG_ANSLOG_H */ Property changes on: channels/sig_analog.h ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + Author Date Id Revision Index: channels/chan_sip.c =================================================================== --- a/channels/chan_sip.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/chan_sip.c (.../trunk) (revision 202568) @@ -229,7 +229,7 @@ #include "asterisk/pbx.h" #include "asterisk/sched.h" #include "asterisk/io.h" -#include "asterisk/rtp.h" +#include "asterisk/rtp_engine.h" #include "asterisk/udptl.h" #include "asterisk/acl.h" #include "asterisk/manager.h" @@ -271,6 +271,7 @@ #include "asterisk/ast_version.h" #include "asterisk/event.h" #include "asterisk/tcptls.h" +#include "asterisk/stun.h" /*** DOCUMENTATION @@ -472,6 +473,79 @@ Check the domain= configuration in sip.conf. + + + List SIP peers (text format). + + + + + + Lists SIP peers in text format with details on current status. + Peerlist will follow as separate events, followed by a final event called + PeerlistComplete. + + + + + show SIP peer (text format). + + + + + The peer name you want to check. + + + + Show one SIP peer with details on current status. + + + + + Qualify SIP peers. + + + + + The peer name you want to qualify. + + + + Qualify a SIP peer. + + + + + Show SIP registrations (text format). + + + + + + Lists all registration requests and status. Registrations will follow as separate + events. followed by a final event called RegistrationsComplete. + + + + + Send a SIP notify. + + + + + Peer to receive the notify. + + + At least one variable pair must be specified. + name=value + + + + Sends a SIP Notify event. + All parameters for this event must be specified in the body of this request + via multiple Variable: name=value sequences. + + ***/ #ifndef FALSE @@ -691,6 +765,7 @@ AUTH_PEER_NOT_DYNAMIC = -6, AUTH_ACL_FAILED = -7, AUTH_BAD_TRANSPORT = -8, + AUTH_RTP_FAILED = 9, }; /*! \brief States for outbound registrations (with register= lines in sip.conf */ @@ -940,12 +1015,60 @@ { SIP_OPT_TARGET_DIALOG,NOT_SUPPORTED, "tdialog" }, }; +/*! \brief Diversion header reasons + * + * The core defines a bunch of constants used to define + * redirecting reasons. This provides a translation table + * between those and the strings which may be present in + * a SIP Diversion header + */ +static const struct sip_reasons { + enum AST_REDIRECTING_REASON code; + char * const text; +} sip_reason_table[] = { + { AST_REDIRECTING_REASON_UNKNOWN, "unknown" }, + { AST_REDIRECTING_REASON_USER_BUSY, "user-busy" }, + { AST_REDIRECTING_REASON_NO_ANSWER, "no-answer" }, + { AST_REDIRECTING_REASON_UNAVAILABLE, "unavailable" }, + { AST_REDIRECTING_REASON_UNCONDITIONAL, "unconditional" }, + { AST_REDIRECTING_REASON_TIME_OF_DAY, "time-of-day" }, + { AST_REDIRECTING_REASON_DO_NOT_DISTURB, "do-not-disturb" }, + { AST_REDIRECTING_REASON_DEFLECTION, "deflection" }, + { AST_REDIRECTING_REASON_FOLLOW_ME, "follow-me" }, + { AST_REDIRECTING_REASON_OUT_OF_ORDER, "out-of-service" }, + { AST_REDIRECTING_REASON_AWAY, "away" }, + { AST_REDIRECTING_REASON_CALL_FWD_DTE, "unknown"} +}; +static enum AST_REDIRECTING_REASON sip_reason_str_to_code(const char *text) +{ + enum AST_REDIRECTING_REASON ast = AST_REDIRECTING_REASON_UNKNOWN; + int i; + + for (i = 0; i < ARRAY_LEN(sip_reason_table); ++i) { + if (!strcasecmp(text, sip_reason_table[i].text)) { + ast = sip_reason_table[i].code; + break; + } + } + + return ast; +} + +static const char *sip_reason_code_to_str(enum AST_REDIRECTING_REASON code) +{ + if (code >= 0 && code < ARRAY_LEN(sip_reason_table)) { + return sip_reason_table[code].text; + } + + return "unknown"; +} + /*! \brief SIP Methods we support \todo This string should be set dynamically. We only support REFER and SUBSCRIBE if we have allowsubscribe and allowrefer on in sip.conf. */ -#define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY" +#define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO" /*! \brief SIP Extensions we support \note This should be generated based on the previous array @@ -980,6 +1103,7 @@ #define DEFAULT_MOHSUGGEST "" #define DEFAULT_VMEXTEN "asterisk" /*!< Default voicemail extension */ #define DEFAULT_CALLERID "asterisk" /*!< Default caller ID */ +#define DEFAULT_MWI_FROM "" #define DEFAULT_NOTIFYMIME "application/simple-message-summary" #define DEFAULT_ALLOWGUEST TRUE #define DEFAULT_RTPKEEPALIVE 0 /*!< Default RTPkeepalive setting */ @@ -1011,6 +1135,7 @@ #define DEFAULT_USERAGENT "Asterisk PBX" /*!< Default Useragent: header unless re-defined in sip.conf */ #define DEFAULT_SDPSESSION "Asterisk PBX" /*!< Default SDP session name, (s=) header unless re-defined in sip.conf */ #define DEFAULT_SDPOWNER "root" /*!< Default SDP username field in (o=) header unless re-defined in sip.conf */ +#define DEFAULT_ENGINE "asterisk" /*!< Default RTP engine to use for sessions */ #endif /*@}*/ @@ -1021,6 +1146,7 @@ /*@{*/ static char default_language[MAX_LANGUAGE]; static char default_callerid[AST_MAX_EXTENSION]; +static char default_mwi_from[80]; static char default_fromdomain[AST_MAX_EXTENSION]; static char default_notifymime[AST_MAX_EXTENSION]; static int default_qualify; /*!< Default Qualify= setting */ @@ -1029,6 +1155,7 @@ static char default_mohsuggest[MAX_MUSICCLASS]; /*!< Global setting for moh class to suggest when putting * a bridged channel on hold */ static char default_parkinglot[AST_MAX_CONTEXT]; /*!< Parkinglot */ +static char default_engine[256]; /*!< Default RTP engine */ static int default_maxcallbitrate; /*!< Maximum bitrate for call */ static struct ast_codec_pref default_prefs; /*!< Default codec prefs */ static unsigned int default_transports; /*!< Default Transports (enum sip_transport) that are acceptable */ @@ -1095,8 +1222,8 @@ static unsigned int global_cos_audio; /*!< 802.1p class of service for audio RTP packets */ static unsigned int global_cos_video; /*!< 802.1p class of service for video RTP packets */ static unsigned int global_cos_text; /*!< 802.1p class of service for text RTP packets */ -static int recordhistory; /*!< Record SIP history. Off by default */ -static int dumphistory; /*!< Dump history to verbose before destroying SIP dialog */ +static unsigned int recordhistory; /*!< Record SIP history. Off by default */ +static unsigned int dumphistory; /*!< Dump history to verbose before destroying SIP dialog */ static char global_regcontext[AST_MAX_CONTEXT]; /*!< Context for auto-extensions */ static char global_useragent[AST_MAX_EXTENSION]; /*!< Useragent for the SIP channel */ static char global_sdpsession[AST_MAX_EXTENSION]; /*!< SDP session name for the SIP channel */ @@ -1105,7 +1232,7 @@ static int global_t1; /*!< T1 time */ static int global_t1min; /*!< T1 roundtrip time minimum */ static int global_timer_b; /*!< Timer B - RFC 3261 Section 17.1.1.2 */ -static int global_autoframing; /*!< Turn autoframing on or off. */ +static unsigned int global_autoframing; /*!< Turn autoframing on or off. */ static int global_qualifyfreq; /*!< Qualify frequency */ static int global_qualify_gap; /*!< Time between our group of peer pokes */ static int global_qualify_peers; /*!< Number of peers to poke at a given time */ @@ -1350,7 +1477,10 @@ #define SIP_PROG_INBAND_NO (1 << 25) #define SIP_PROG_INBAND_YES (2 << 25) -#define SIP_SENDRPID (1 << 29) /*!< DP: Remote Party-ID Support */ +#define SIP_SENDRPID (3 << 29) /*!< DP: Remote Party-ID Support */ +#define SIP_SENDRPID_NO (0 << 29) +#define SIP_SENDRPID_PAI (1 << 29) /*!< Use "P-Asserted-Identity" for rpid */ +#define SIP_SENDRPID_RPID (2 << 29) /*!< Use "Remote-Party-ID" for rpid */ #define SIP_G726_NONSTANDARD (1 << 31) /*!< DP: Use non-standard packing for G726-32 data */ /*! \brief Flags to copy from peer/user to dialog */ @@ -1366,10 +1496,14 @@ /* realtime flags */ #define SIP_PAGE2_RTCACHEFRIENDS (1 << 0) /*!< GP: Should we keep RT objects in memory for extended time? */ #define SIP_PAGE2_RTAUTOCLEAR (1 << 2) /*!< GP: Should we clean memory from peers after expiry? */ +#define SIP_PAGE2_RPID_UPDATE (1 << 3) /* Space for addition of other realtime flags in the future */ #define SIP_PAGE2_STATECHANGEQUEUE (1 << 9) /*!< D: Unsent state pending change exists */ -#define SIP_PAGE2_RPORT_PRESENT (1 << 10) /*!< Was rport received in the Via header? */ +#define SIP_PAGE2_CONNECTLINEUPDATE_PEND (1 << 10) +#define SIP_PAGE2_RPID_IMMEDIATE (1 << 11) +#define SIP_PAGE2_RPORT_PRESENT (1 << 12) /*!< Was rport received in the Via header? */ +#define SIP_PAGE2_PREFERRED_CODEC (1 << 13) /*!< GDP: Only respond with single most preferred joint codec */ #define SIP_PAGE2_VIDEOSUPPORT (1 << 14) /*!< DP: Video supported if offered? */ #define SIP_PAGE2_TEXTSUPPORT (1 << 15) /*!< GDP: Global text enable */ #define SIP_PAGE2_ALLOWSUBSCRIBE (1 << 16) /*!< GP: Allow subscriptions from this peer? */ @@ -1399,7 +1533,8 @@ (SIP_PAGE2_ALLOWSUBSCRIBE | SIP_PAGE2_ALLOWOVERLAP | SIP_PAGE2_IGNORESDPVERSION | \ SIP_PAGE2_VIDEOSUPPORT | SIP_PAGE2_T38SUPPORT | SIP_PAGE2_RFC2833_COMPENSATE | \ SIP_PAGE2_BUGGY_MWI | SIP_PAGE2_TEXTSUPPORT | SIP_PAGE2_FAX_DETECT | \ - SIP_PAGE2_UDPTL_DESTINATION | SIP_PAGE2_VIDEOSUPPORT_ALWAYS) + SIP_PAGE2_UDPTL_DESTINATION | SIP_PAGE2_VIDEOSUPPORT_ALWAYS | SIP_PAGE2_PREFERRED_CODEC | \ + SIP_PAGE2_RPID_IMMEDIATE | SIP_PAGE2_RPID_UPDATE) /*@}*/ @@ -1604,13 +1739,13 @@ AST_STRING_FIELD(peermd5secret); AST_STRING_FIELD(cid_num); /*!< Caller*ID number */ AST_STRING_FIELD(cid_name); /*!< Caller*ID name */ + AST_STRING_FIELD(mwi_from); /*!< Name to place in the From header in outgoing NOTIFY requests */ AST_STRING_FIELD(fullcontact); /*!< The Contact: that the UA registers with us */ /* we only store the part in in this field. */ AST_STRING_FIELD(our_contact); /*!< Our contact header */ - AST_STRING_FIELD(rpid); /*!< Our RPID header */ - AST_STRING_FIELD(rpid_from); /*!< Our RPID From header */ AST_STRING_FIELD(url); /*!< URL to be sent with next message to peer */ AST_STRING_FIELD(parkinglot); /*!< Parkinglot */ + AST_STRING_FIELD(engine); /*!< RTP engine to use */ ); char via[128]; /*!< Via: header */ struct sip_socket socket; /*!< The socket used for this dialog */ @@ -1622,15 +1757,21 @@ int lastnoninvite; /*!< Last Cseq of non-invite */ struct ast_flags flags[2]; /*!< SIP_ flags */ - /* boolean or small integers that don't belong in flags */ - char do_history; /*!< Set if we want to record history */ - char alreadygone; /*!< already destroyed by our peer */ - char needdestroy; /*!< need to be destroyed by the monitor thread */ - char outgoing_call; /*!< this is an outgoing call */ - char answered_elsewhere; /*!< This call is cancelled due to answer on another channel */ - char novideo; /*!< Didn't get video in invite, don't offer */ - char notext; /*!< Text not supported (?) */ - + /* boolean flags that don't belong in flags */ + unsigned short do_history:1; /*!< Set if we want to record history */ + unsigned short alreadygone:1; /*!< already destroyed by our peer */ + unsigned short needdestroy:1; /*!< need to be destroyed by the monitor thread */ + unsigned short outgoing_call:1; /*!< this is an outgoing call */ + unsigned short answered_elsewhere:1; /*!< This call is cancelled due to answer on another channel */ + unsigned short novideo:1; /*!< Didn't get video in invite, don't offer */ + unsigned short notext:1; /*!< Text not supported (?) */ + unsigned short session_modify:1; /*!< Session modification request true/false */ + unsigned short route_persistent:1; /*!< Is this the "real" route? */ + unsigned short autoframing:1; /*!< Whether to use our local configuration for frame sizes (off) + * or respect the other endpoint's request for frame sizes (on) + * for incoming calls + */ + char tag[11]; /*!< Our tag for this session */ int timer_t1; /*!< SIP timer T1, ms rtt */ int timer_b; /*!< SIP timer B, ms */ unsigned int sipoptions; /*!< Supported SIP options on the other end */ @@ -1644,20 +1785,19 @@ int jointnoncodeccapability; /*!< Joint Non codec capability */ int redircodecs; /*!< Redirect codecs */ int maxcallbitrate; /*!< Maximum Call Bitrate for Video Calls */ + int request_queue_sched_id; /*!< Scheduler ID of any scheduled action to process queued requests */ + int authtries; /*!< Times we've tried to authenticate */ struct sip_proxy *outboundproxy; /*!< Outbound proxy for this dialog. Use ref_proxy to set this instead of setting it directly*/ struct t38properties t38; /*!< T38 settings */ struct sockaddr_in udptlredirip; /*!< Where our T.38 UDPTL should be going if not to us */ struct ast_udptl *udptl; /*!< T.38 UDPTL session */ int callingpres; /*!< Calling presentation */ - int authtries; /*!< Times we've tried to authenticate */ int expiry; /*!< How long we take to expire */ + int sessionversion; /*!< SDP Session Version */ + int sessionid; /*!< SDP Session ID */ long branch; /*!< The branch identifier of this session */ long invite_branch; /*!< The branch used when we sent the initial INVITE */ - char tag[11]; /*!< Our tag for this session */ - int sessionid; /*!< SDP Session ID */ - int sessionversion; /*!< SDP Session Version */ - uint64_t sessionversion_remote; /*!< Remote UA's SDP Session Version */ - int session_modify; /*!< Session modification request true/false */ + int64_t sessionversion_remote; /*!< Remote UA's SDP Session Version */ struct sockaddr_in sa; /*!< Our peer */ struct sockaddr_in redirip; /*!< Where our RTP should be going if not to us */ struct sockaddr_in vredirip; /*!< Where our Video RTP should be going if not to us */ @@ -1667,9 +1807,9 @@ int rtptimeout; /*!< RTP timeout time */ struct sockaddr_in recv; /*!< Received as */ struct sockaddr_in ourip; /*!< Our IP (as seen from the outside) */ + enum transfermodes allowtransfer; /*!< REFER: restriction scheme */ struct ast_channel *owner; /*!< Who owns us (if we have an owner) */ struct sip_route *route; /*!< Head of linked list of routing steps (fm Record-Route) */ - int route_persistant; /*!< Is this the "real" route? */ struct ast_variable *notify_headers; /*!< Custom notify type */ struct sip_auth *peerauth; /*!< Realm authentication */ int noncecount; /*!< Nonce-count */ @@ -1687,38 +1827,37 @@ int waitid; /*!< Wait ID for scheduler after 491 or other delays */ int autokillid; /*!< Auto-kill ID (scheduler) */ int t38id; /*!< T.38 Response ID */ - enum transfermodes allowtransfer; /*!< REFER: restriction scheme */ struct sip_refer *refer; /*!< REFER: SIP transfer data structure */ enum subscriptiontype subscribed; /*!< SUBSCRIBE: Is this dialog a subscription? */ int stateid; /*!< SUBSCRIBE: ID for devicestate subscriptions */ int laststate; /*!< SUBSCRIBE: Last known extension state */ int dialogver; /*!< SUBSCRIBE: Version for subscription dialog-info */ - struct ast_dsp *vad; /*!< Inband DTMF Detection dsp */ + struct ast_dsp *dsp; /*!< Inband DTMF Detection dsp */ struct sip_peer *relatedpeer; /*!< If this dialog is related to a peer, which one Used in peerpoke, mwi subscriptions */ struct sip_registry *registry; /*!< If this is a REGISTER dialog, to which registry */ - struct ast_rtp *rtp; /*!< RTP Session */ - struct ast_rtp *vrtp; /*!< Video RTP session */ - struct ast_rtp *trtp; /*!< Text RTP session */ + struct ast_rtp_instance *rtp; /*!< RTP Session */ + struct ast_rtp_instance *vrtp; /*!< Video RTP session */ + struct ast_rtp_instance *trtp; /*!< Text RTP session */ struct sip_pkt *packets; /*!< Packets scheduled for re-transmission */ struct sip_history_head *history; /*!< History of this SIP dialog */ size_t history_entries; /*!< Number of entires in the history */ struct ast_variable *chanvars; /*!< Channel variables to set for inbound call */ AST_LIST_HEAD_NOLOCK(request_queue, sip_request) request_queue; /*!< Requests that arrived but could not be processed immediately */ - int request_queue_sched_id; /*!< Scheduler ID of any scheduled action to process queued requests */ struct sip_invite_param *options; /*!< Options for INVITE */ - int autoframing; /*!< The number of Asters we group in a Pyroflax - before strolling to the Grokyzpå - (A bit unsure of this, please correct if - you know more) */ struct sip_st_dlg *stimer; /*!< SIP Session-Timers */ int red; /*!< T.140 RTP Redundancy */ int hangupcause; /*!< Storage of hangupcause copied from our owner before we disconnect from the AST channel (only used at hangup) */ struct sip_subscription_mwi *mwi; /*!< If this is a subscription MWI dialog, to which subscription */ + /*! The SIP methods allowed on this dialog. We get this information from the Allow header present in + * the peer's REGISTER. If peer does not register with us, then we will use the first transaction we + * have with this peer to determine its allowed methods. + */ + unsigned int allowed_methods; }; @@ -1730,7 +1869,7 @@ * the container and individual items, and functions to add/remove * references to the individual items. */ -struct ao2_container *dialogs; +static struct ao2_container *dialogs; #define sip_pvt_lock(x) ao2_lock(x) #define sip_pvt_trylock(x) ao2_trylock(x) @@ -1791,6 +1930,7 @@ int seqno; /*!< Sequence number */ char is_resp; /*!< 1 if this is a response packet (e.g. 200 OK), 0 if it is a request */ char is_fatal; /*!< non-zero if there is a fatal error */ + int response_code; /*!< If this is a response, the response code */ struct sip_pvt *owner; /*!< Owner AST call */ int retransid; /*!< Retransmission ID */ int timer_a; /*!< SIP timer A, retransmission timer */ @@ -1844,11 +1984,23 @@ AST_STRING_FIELD(mohsuggest); /*!< Music on Hold class */ AST_STRING_FIELD(parkinglot); /*!< Parkinglot */ AST_STRING_FIELD(useragent); /*!< User agent in SIP request (saved from registration) */ + AST_STRING_FIELD(mwi_from); /*!< Name to place in From header for outgoing NOTIFY requests */ + AST_STRING_FIELD(engine); /*!< RTP Engine to use */ ); struct sip_socket socket; /*!< Socket used for this peer */ enum sip_transport default_outbound_transport; /*!< Peer Registration may change the default outbound transport. - If register expires, default should be reset. to this value */ - unsigned int transports:3; /*!< Transports (enum sip_transport) that are acceptable for this peer */ + If register expires, default should be reset. to this value */ + /* things that don't belong in flags */ + unsigned short transports:3; /*!< Transports (enum sip_transport) that are acceptable for this peer */ + unsigned short is_realtime:1; /*!< this is a 'realtime' peer */ + unsigned short rt_fromcontact:1;/*!< copy fromcontact from realtime */ + unsigned short host_dynamic:1; /*!< Dynamic Peers register with Asterisk */ + unsigned short selfdestruct:1; /*!< Automatic peers need to destruct themselves */ + unsigned short the_mark:1; /*!< moved out of ASTOBJ into struct proper; That which bears the_mark should be deleted! */ + unsigned short autoframing:1; /*!< Whether to use our local configuration for frame sizes (off) + * or respect the other endpoint's request for frame sizes (on) + * for incoming calls + */ struct sip_auth *auth; /*!< Realm authentication list */ int amaflags; /*!< AMA Flags (for billing) */ int callingpres; /*!< Calling id presentation */ @@ -1866,13 +2018,7 @@ /*! Mailboxes that this peer cares about */ AST_LIST_HEAD_NOLOCK(, sip_mailbox) mailboxes; - /* things that don't belong in flags */ - char is_realtime; /*!< this is a 'realtime' peer */ - char rt_fromcontact; /*!< copy fromcontact from realtime */ - char host_dynamic; /*!< Dynamic Peers register with Asterisk */ - char selfdestruct; /*!< Automatic peers need to destruct themselves */ - char the_mark; /*!< moved out of ASTOBJ into struct proper; That which bears the_mark should be deleted! */ - + int maxcallbitrate; /*!< Maximum Bitrate for a video call */ int expire; /*!< When to expire this peer registration */ int capability; /*!< Codec capability */ int rtptimeout; /*!< RTP timeout */ @@ -1883,8 +2029,6 @@ struct sip_proxy *outboundproxy; /*!< Outbound proxy for this peer */ struct ast_dnsmgr_entry *dnsmgr;/*!< DNS refresh manager for peer */ struct sockaddr_in addr; /*!< IP address of peer */ - int maxcallbitrate; /*!< Maximum Bitrate for a video call */ - /* Qualification */ struct sip_pvt *call; /*!< Call pointer */ int pokeexpire; /*!< When to expire poke (qualify= checking) */ @@ -1897,7 +2041,6 @@ struct ast_ha *contactha; /*!< Restrict what IPs are allowed in the Contact header (for registration) */ struct ast_variable *chanvars; /*!< Variables to set for channel created by user */ struct sip_pvt *mwipvt; /*!< Subscription for MWI */ - int autoframing; struct sip_st_cfg stimer; /*!< SIP Session-Timers */ int timer_t1; /*!< The maximum T1 value for the peer */ int timer_b; /*!< The maximum timer B (transaction timeouts) */ @@ -1905,6 +2048,7 @@ /*XXX Seems like we suddenly have two flags with the same content. Why? To be continued... */ enum sip_peer_type type; /*!< Distinguish between "user" and "peer" types. This is used solely for CLI and manager commands */ + unsigned int allowed_methods; }; @@ -1919,12 +2063,6 @@ * or once the previously completed registration one expires). * The registration can be in one of many states, though at the moment * the handling is a bit mixed. - * - * XXX \todo Reference count handling for this object has some problems with - * respect to scheduler entries. The ref count is handled in some places, - * but not all of them. There are some places where references get leaked - * when this scheduler entry gets cancelled. At worst, this would cause - * memory leaks on reloads if registrations get removed from configuration. */ struct sip_registry { ASTOBJ_COMPONENTS_FULL(struct sip_registry,1,1); @@ -2005,8 +2143,8 @@ static AST_LIST_HEAD_STATIC(threadl, sip_threadinfo); /*! \brief The peer list: Users, Peers and Friends */ -struct ao2_container *peers; -struct ao2_container *peers_by_ip; +static struct ao2_container *peers; +static struct ao2_container *peers_by_ip; /*! \brief The register list: Other SIP proxies we register with and place calls to */ static struct ast_register_list { @@ -2215,6 +2353,7 @@ static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); static int sip_senddigit_begin(struct ast_channel *ast, char digit); static int sip_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration); +static int sip_setoption(struct ast_channel *chan, int option, void *data, int datalen); static int sip_queryoption(struct ast_channel *chan, int option, void *data, int *datalen); static const char *sip_get_callid(struct ast_channel *chan); @@ -2233,7 +2372,7 @@ static int transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req); static int transmit_response_reliable(struct sip_pvt *p, const char *msg, const struct sip_request *req); static int transmit_response_with_date(struct sip_pvt *p, const char *msg, const struct sip_request *req); -static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable, int oldsdp); +static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable, int oldsdp, int rpid); static int transmit_response_with_unsupported(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *unsupported); static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *rand, enum xmittype reliable, const char *header, int stale); static int transmit_response_with_allow(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable); @@ -2254,12 +2393,12 @@ static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno); static void copy_request(struct sip_request *dst, const struct sip_request *src); static void receive_message(struct sip_pvt *p, struct sip_request *req); -static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req); +static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req, char **name, char **number, int set_call_forward); static int sip_send_mwi_to_peer(struct sip_peer *peer, const struct ast_event *event, int cache_only); /*--- Dialog management */ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *sin, - int useglobal_nat, const int intended_method); + int useglobal_nat, const int intended_method, struct sip_request *req); static int __sip_autodestruct(const void *data); static void sip_scheddestroy(struct sip_pvt *p, int ms); static int sip_cancel_destroy(struct sip_pvt *p); @@ -2279,7 +2418,7 @@ static void list_route(struct sip_route *route); static void build_route(struct sip_pvt *p, struct sip_request *req, int backwards); static enum check_auth_result register_verify(struct sip_pvt *p, struct sockaddr_in *sin, - struct sip_request *req, char *uri); + struct sip_request *req, const char *uri); static struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *totag, const char *fromtag); static void check_pendings(struct sip_pvt *p); static void *sip_park_thread(void *stuff); @@ -2307,11 +2446,11 @@ static int build_reply_digest(struct sip_pvt *p, int method, char *digest, int digest_len); static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request *req, const char *username, const char *secret, const char *md5secret, int sipmethod, - char *uri, enum xmittype reliable, int ignore); + const char *uri, enum xmittype reliable, int ignore); static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_request *req, - int sipmethod, char *uri, enum xmittype reliable, + int sipmethod, const char *uri, enum xmittype reliable, struct sockaddr_in *sin, struct sip_peer **authpeer); -static int check_user(struct sip_pvt *p, struct sip_request *req, int sipmethod, char *uri, enum xmittype reliable, struct sockaddr_in *sin); +static int check_user(struct sip_pvt *p, struct sip_request *req, int sipmethod, const char *uri, enum xmittype reliable, struct sockaddr_in *sin); /*--- Domain handling */ static int check_sip_domain(const char *domain, char *context, size_t len); /* Check if domain is one of our local domains */ @@ -2416,13 +2555,13 @@ static char *sip_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); static char *sip_show_channelstats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); static char *sip_show_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); -static char *sip_do_debug_ip(int fd, char *arg); -static char *sip_do_debug_peer(int fd, char *arg); +static char *sip_do_debug_ip(int fd, const char *arg); +static char *sip_do_debug_peer(int fd, const char *arg); static char *sip_do_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); static char *sip_cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); static char *sip_set_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); -static int sip_dtmfmode(struct ast_channel *chan, void *data); -static int sip_addheader(struct ast_channel *chan, void *data); +static int sip_dtmfmode(struct ast_channel *chan, const char *data); +static int sip_addheader(struct ast_channel *chan, const char *data); static int sip_do_reload(enum channelreloadreason reason); static char *sip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *preparse, char *buf, size_t buflen); @@ -2443,7 +2582,6 @@ static void sip_dump_history(struct sip_pvt *dialog); /*--- Device object handling */ -static struct sip_peer *temp_peer(const char *name); static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime); static int update_call_counter(struct sip_pvt *fup, int event); static void sip_destroy_peer(struct sip_peer *peer); @@ -2458,6 +2596,7 @@ static void destroy_association(struct sip_peer *peer); static void set_insecure_flags(struct ast_flags *flags, const char *value, int lineno); static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask, struct ast_variable *v); +static void set_socket_transport(struct sip_socket *socket, int transport); /* Realtime device support */ static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, const char *username, const char *fullcontact, const char *useragent, int expirey, int deprecated_username, int lastms); @@ -2468,7 +2607,7 @@ static char *sip_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); /*--- Internal UA client handling (outbound registrations) */ -static void ast_sip_ouraddrfor(struct in_addr *them, struct sockaddr_in *us); +static void ast_sip_ouraddrfor(struct in_addr *them, struct sockaddr_in *us, struct sip_pvt *p); static void sip_registry_destroy(struct sip_registry *reg); static int sip_register(const char *value, int lineno); static const char *regstate2str(enum sipregistrystate regstate) attribute_const; @@ -2485,6 +2624,8 @@ static const char *gettag(const struct sip_request *req, const char *header, char *tagbuf, int tagbufsize); static int find_sip_method(const char *msg); static unsigned int parse_sip_options(struct sip_pvt *pvt, const char *supported); +static unsigned int parse_allowed_methods(struct sip_request *req); +static unsigned int set_pvt_allowed_methods(struct sip_pvt *pvt, struct sip_request *req); static int parse_request(struct sip_request *req); static const char *get_header(const struct sip_request *req, const char *name); static const char *referstatus2str(enum referstatus rstatus) attribute_pure; @@ -2502,11 +2643,14 @@ static int set_address_from_contact(struct sip_pvt *pvt); static void check_via(struct sip_pvt *p, struct sip_request *req); static char *get_calleridname(const char *input, char *output, size_t outputsize); -static int get_rpid_num(const char *input, char *output, int maxlen); -static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq); +static int get_rpid(struct sip_pvt *p, struct sip_request *oreq); +static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, char **number, int *reason); static int get_destination(struct sip_pvt *p, struct sip_request *oreq); static int get_msg_text(char *buf, int len, struct sip_request *req, int addnewline); static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout); +static void update_connectedline(struct sip_pvt *p, const void *data, size_t datalen); +static void update_redirecting(struct sip_pvt *p, const void *data, size_t datalen); +static void change_redirecting_information(struct sip_pvt *p, struct sip_request *req, struct ast_party_redirecting *redirecting, int set_call_forward); /*-- TCP connection handling ---*/ static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_session_instance *tcptls_session); @@ -2533,6 +2677,7 @@ static int add_line(struct sip_request *req, const char *line); static int add_text(struct sip_request *req, const char *text); static int add_digit(struct sip_request *req, char digit, unsigned int duration, int mode); +static int add_rpid(struct sip_request *req, struct sip_pvt *p); static int add_vidupdate(struct sip_request *req); static void add_route(struct sip_request *req, struct sip_route *route); static int copy_header(struct sip_request *req, const struct sip_request *orig, const char *field); @@ -2541,39 +2686,31 @@ static void set_destination(struct sip_pvt *p, char *uri); static void append_date(struct sip_request *req); static void build_contact(struct sip_pvt *p); -static void build_rpid(struct sip_pvt *p); /*------Request handling functions */ static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int *recount, int *nounlock); -static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin, int *recount, char *e, int *nounlock); +static int handle_request_update(struct sip_pvt *p, struct sip_request *req); +static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin, int *recount, const char *e, int *nounlock); static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, int *nounlock); static int handle_request_bye(struct sip_pvt *p, struct sip_request *req); -static int handle_request_register(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, char *e); +static int handle_request_register(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, const char *e); static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req); static int handle_request_message(struct sip_pvt *p, struct sip_request *req); -static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, char *e); +static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, const char *e); static void handle_request_info(struct sip_pvt *p, struct sip_request *req); static int handle_request_options(struct sip_pvt *p, struct sip_request *req); static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin); -static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, char *e); +static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, const char *e); static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, int seqno); /*------Response handling functions */ -static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno); -static void handle_response_notify(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno); -static void handle_response_refer(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno); -static void handle_response_subscribe(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno); -static int handle_response_register(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno); -static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno); +static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno); +static void handle_response_notify(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno); +static void handle_response_refer(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno); +static void handle_response_subscribe(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno); +static int handle_response_register(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno); +static void handle_response(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno); -/*----- RTP interface functions */ -static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active); -static enum ast_rtp_get_result sip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); -static enum ast_rtp_get_result sip_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); -static enum ast_rtp_get_result sip_get_trtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); -static int sip_get_codec(struct ast_channel *chan); -static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p, int *faxdetect); - /*------ T38 Support --------- */ static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans); static struct ast_udptl *sip_get_udptl_peer(struct ast_channel *chan); @@ -2594,6 +2731,9 @@ static enum st_mode st_get_mode(struct sip_pvt *); static struct sip_st_dlg* sip_st_alloc(struct sip_pvt *const p); +/*------- RTP Glue functions -------- */ +static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *instance, struct ast_rtp_instance *vinstance, struct ast_rtp_instance *tinstance, int codecs, int nat_active); + /*!--- SIP MWI Subscription support */ static int sip_subscribe_mwi(const char *value, int lineno); static void sip_subscribe_mwi_destroy(struct sip_subscription_mwi *mwi); @@ -2622,10 +2762,11 @@ .fixup = sip_fixup, /* called with chan locked */ .send_digit_begin = sip_senddigit_begin, /* called with chan unlocked */ .send_digit_end = sip_senddigit_end, - .bridge = ast_rtp_bridge, /* XXX chan unlocked ? */ - .early_bridge = ast_rtp_early_bridge, + .bridge = ast_rtp_instance_bridge, /* XXX chan unlocked ? */ + .early_bridge = ast_rtp_instance_early_bridge, .send_text = sip_sendtext, /* called with chan locked */ .func_channel_read = acf_channel_read, + .setoption = sip_setoption, .queryoption = sip_queryoption, .get_pvt_uniqueid = sip_get_callid, }; @@ -2696,17 +2837,6 @@ return errorvalue; } - -/*! \brief Interface structure with callbacks used to connect to RTP module */ -static struct ast_rtp_protocol sip_rtp = { - .type = "SIP", - .get_rtp_info = sip_get_rtp_peer, - .get_vrtp_info = sip_get_vrtp_peer, - .get_trtp_info = sip_get_trtp_peer, - .set_rtp_peer = sip_set_rtp_peer, - .get_codec = sip_get_codec, -}; - /*! * duplicate a list of channel variables, \return the copy. */ @@ -2777,14 +2907,14 @@ reqcpy.data = str_save; ast_str_reset(reqcpy.data); - req.socket.fd = tcptls_session->fd; if (tcptls_session->ssl) { - req.socket.type = SIP_TRANSPORT_TLS; + set_socket_transport(&req.socket, SIP_TRANSPORT_TLS); req.socket.port = htons(ourport_tls); } else { - req.socket.type = SIP_TRANSPORT_TCP; + set_socket_transport(&req.socket, SIP_TRANSPORT_TCP); req.socket.port = htons(ourport_tcp); } + req.socket.fd = tcptls_session->fd; res = ast_wait_for_input(tcptls_session->fd, -1); if (res < 0) { ast_debug(2, "SIP %s server :: ast_wait_for_input returned %d\n", tcptls_session->ssl ? "SSL": "TCP", res); @@ -3270,8 +3400,9 @@ */ static inline const char *get_transport_pvt(struct sip_pvt *p) { - if (p->outboundproxy && p->outboundproxy->transport) - p->socket.type = p->outboundproxy->transport; + if (p->outboundproxy && p->outboundproxy->transport) { + set_socket_transport(&p->socket, p->outboundproxy->transport); + } return get_transport(p->socket.type); } @@ -3346,7 +3477,7 @@ * externip or can get away with our internal bindaddr * 'us' is always overwritten. */ -static void ast_sip_ouraddrfor(struct in_addr *them, struct sockaddr_in *us) +static void ast_sip_ouraddrfor(struct in_addr *them, struct sockaddr_in *us, struct sip_pvt *p) { struct sockaddr_in theirs; /* Set want_remap to non-zero if we want to remap 'us' to an externally @@ -3390,10 +3521,34 @@ ast_log(LOG_WARNING, "stun failed\n"); ast_debug(1, "Target address %s is not local, substituting externip\n", ast_inet_ntoa(*(struct in_addr *)&them->s_addr)); + } else if (p) { + /* no remapping, but we bind to a specific address, so use it. */ + switch (p->socket.type) { + case SIP_TRANSPORT_TCP: + if (sip_tcp_desc.local_address.sin_addr.s_addr) { + *us = sip_tcp_desc.local_address; + } else { + us->sin_port = sip_tcp_desc.local_address.sin_port; + } + break; + case SIP_TRANSPORT_TLS: + if (sip_tls_desc.local_address.sin_addr.s_addr) { + *us = sip_tls_desc.local_address; + } else { + us->sin_port = sip_tls_desc.local_address.sin_port; + } + break; + case SIP_TRANSPORT_UDP: + /* fall through on purpose */ + default: + if (bindaddr.sin_addr.s_addr) { + *us = bindaddr; + } + } } else if (bindaddr.sin_addr.s_addr) { - /* no remapping, but we bind to a specific address, so use it. */ *us = bindaddr; } + ast_debug(3, "Setting SIP_TRANSPORT_%s with address %s:%d\n", get_transport(p->socket.type), ast_inet_ntoa(us->sin_addr), ntohs(us->sin_port)); } /*! \brief Append to SIP dialog history with arg list */ @@ -3573,6 +3728,7 @@ struct sip_pkt *pkt = NULL; int siptimer_a = DEFAULT_RETRANS; int xmitres = 0; + int respid; if (sipmethod == SIP_INVITE) { /* Note this is a pending invite */ @@ -3609,6 +3765,12 @@ pkt->owner = dialog_ref(p, "__sip_reliable_xmit: setting pkt->owner"); pkt->next = p->packets; p->packets = pkt; /* Add it to the queue */ + if (resp) { + /* Parse out the response code */ + if (sscanf(ast_str_buffer(pkt->data), "SIP/2.0 %d", &respid) == 1) { + pkt->response_code = respid; + } + } pkt->timer_t1 = p->timer_t1; /* Set SIP timer T1 */ pkt->retransid = -1; if (pkt->timer_t1) @@ -3916,12 +4078,74 @@ return res; } +static void enable_digit_detect(struct sip_pvt *p) +{ + if (p->dsp) { + return; + } + + if (!(p->dsp = ast_dsp_new())) { + return; + } + + ast_dsp_set_features(p->dsp, DSP_FEATURE_DIGIT_DETECT); + if (global_relaxdtmf) { + ast_dsp_set_digitmode(p->dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF); + } +} + +static void disable_digit_detect(struct sip_pvt *p) +{ + if (p->dsp) { + ast_dsp_free(p->dsp); + p->dsp = NULL; + } +} + +/*! \brief Set an option on a SIP dialog */ +static int sip_setoption(struct ast_channel *chan, int option, void *data, int datalen) +{ + int res = -1; + struct sip_pvt *p = chan->tech_pvt; + + switch (option) { + case AST_OPTION_FORMAT_READ: + res = ast_rtp_instance_set_read_format(p->rtp, *(int *) data); + break; + case AST_OPTION_FORMAT_WRITE: + res = ast_rtp_instance_set_write_format(p->rtp, *(int *) data); + break; + case AST_OPTION_MAKE_COMPATIBLE: + res = ast_rtp_instance_make_compatible(chan, p->rtp, (struct ast_channel *) data); + break; + case AST_OPTION_DIGIT_DETECT: + if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) || + (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) { + char *cp = (char *) data; + + ast_debug(1, "%sabling digit detection on %s\n", *cp ? "En" : "Dis", chan->name); + if (*cp) { + enable_digit_detect(p); + } else { + disable_digit_detect(p); + } + res = 0; + } + break; + default: + break; + } + + return res; +} + /*! \brief Query an option on a SIP dialog */ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int *datalen) { int res = -1; enum ast_t38_state state = T38_STATE_UNAVAILABLE; struct sip_pvt *p = (struct sip_pvt *) chan->tech_pvt; + char *cp; switch (option) { case AST_OPTION_T38_STATE: @@ -3955,6 +4179,11 @@ res = 0; break; + case AST_OPTION_DIGIT_DETECT: + cp = (char *) data; + *cp = p->dsp ? 1 : 0; + ast_debug(1, "Reporting digit detection %sabled on %s\n", *cp ? "en" : "dis", chan->name); + break; default: break; } @@ -4642,11 +4871,11 @@ if (p->rtp) { ast_debug(1, "Setting NAT on RTP to %s\n", mode); - ast_rtp_setnat(p->rtp, natflags); + ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_NAT, natflags); } if (p->vrtp) { ast_debug(1, "Setting NAT on VRTP to %s\n", mode); - ast_rtp_setnat(p->vrtp, natflags); + ast_rtp_instance_set_prop(p->vrtp, AST_RTP_PROPERTY_NAT, natflags); } if (p->udptl) { ast_debug(1, "Setting NAT on UDPTL to %s\n", mode); @@ -4654,7 +4883,7 @@ } if (p->trtp) { ast_debug(1, "Setting NAT on TRTP to %s\n", mode); - ast_rtp_setnat(p->trtp, natflags); + ast_rtp_instance_set_prop(p->trtp, AST_RTP_PROPERTY_NAT, natflags); } } @@ -4746,6 +4975,53 @@ *to_sock = *from_sock; } +/*! \brief Initialize RTP portion of a dialog + * \returns -1 on failure, 0 on success + */ +static int dialog_initialize_rtp(struct sip_pvt *dialog) +{ + if (!sip_methods[dialog->method].need_rtp) { + return 0; + } + + if (!(dialog->rtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr, NULL))) { + return -1; + } + + if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT) && (dialog->capability & AST_FORMAT_VIDEO_MASK)) { + if (!(dialog->vrtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr, NULL))) { + return -1; + } + ast_rtp_instance_set_timeout(dialog->vrtp, global_rtptimeout); + ast_rtp_instance_set_hold_timeout(dialog->vrtp, global_rtpholdtimeout); + + ast_rtp_instance_set_prop(dialog->vrtp, AST_RTP_PROPERTY_RTCP, 1); + } + + if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_TEXTSUPPORT)) { + if (!(dialog->trtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr, NULL))) { + return -1; + } + ast_rtp_instance_set_timeout(dialog->trtp, global_rtptimeout); + ast_rtp_instance_set_hold_timeout(dialog->trtp, global_rtpholdtimeout); + + ast_rtp_instance_set_prop(dialog->trtp, AST_RTP_PROPERTY_RTCP, 1); + } + + ast_rtp_instance_set_timeout(dialog->rtp, global_rtptimeout); + ast_rtp_instance_set_hold_timeout(dialog->rtp, global_rtpholdtimeout); + + ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_RTCP, 1); + ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); + ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); + + ast_rtp_instance_set_qos(dialog->rtp, global_tos_audio, 0, "SIP RTP"); + + do_setnat(dialog, ast_test_flag(&dialog->flags[0], SIP_NAT) & SIP_NAT_ROUTE); + + return 0; +} + /*! \brief Create address structure from peer reference. * This function copies data from peer to the dialog, so we don't have to look up the peer * again from memory or database during the life time of the dialog. @@ -4773,17 +5049,6 @@ ast_copy_flags(&dialog->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY); ast_copy_flags(&dialog->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY); dialog->capability = peer->capability; - if (!ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT_ALWAYS) && - (!ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT) || - !(dialog->capability & AST_FORMAT_VIDEO_MASK)) && - dialog->vrtp) { - ast_rtp_destroy(dialog->vrtp); - dialog->vrtp = NULL; - } - if (!ast_test_flag(&dialog->flags[1], SIP_PAGE2_TEXTSUPPORT) && dialog->trtp) { - ast_rtp_destroy(dialog->trtp); - dialog->trtp = NULL; - } dialog->prefs = peer->prefs; if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_T38SUPPORT)) { if (!dialog->udptl) { @@ -4797,31 +5062,29 @@ ast_udptl_destroy(dialog->udptl); dialog->udptl = NULL; } - do_setnat(dialog, ast_test_flag(&dialog->flags[0], SIP_NAT) & SIP_NAT_ROUTE); + ast_string_field_set(dialog, engine, peer->engine); + + if (dialog_initialize_rtp(dialog)) { + return -1; + } + if (dialog->rtp) { /* Audio */ - ast_rtp_setdtmf(dialog->rtp, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); - ast_rtp_setdtmfcompensate(dialog->rtp, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); - ast_rtp_set_rtptimeout(dialog->rtp, peer->rtptimeout); - ast_rtp_set_rtpholdtimeout(dialog->rtp, peer->rtpholdtimeout); - ast_rtp_set_rtpkeepalive(dialog->rtp, peer->rtpkeepalive); + ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); + ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); + ast_rtp_instance_set_timeout(dialog->rtp, peer->rtptimeout); + ast_rtp_instance_set_hold_timeout(dialog->rtp, peer->rtpholdtimeout); /* Set Frame packetization */ - ast_rtp_codec_setpref(dialog->rtp, &dialog->prefs); + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(dialog->rtp), dialog->rtp, &dialog->prefs); dialog->autoframing = peer->autoframing; } if (dialog->vrtp) { /* Video */ - ast_rtp_setdtmf(dialog->vrtp, 0); - ast_rtp_setdtmfcompensate(dialog->vrtp, 0); - ast_rtp_set_rtptimeout(dialog->vrtp, peer->rtptimeout); - ast_rtp_set_rtpholdtimeout(dialog->vrtp, peer->rtpholdtimeout); - ast_rtp_set_rtpkeepalive(dialog->vrtp, peer->rtpkeepalive); + ast_rtp_instance_set_timeout(dialog->vrtp, peer->rtptimeout); + ast_rtp_instance_set_hold_timeout(dialog->vrtp, peer->rtpholdtimeout); } if (dialog->trtp) { /* Realtime text */ - ast_rtp_setdtmf(dialog->trtp, 0); - ast_rtp_setdtmfcompensate(dialog->trtp, 0); - ast_rtp_set_rtptimeout(dialog->trtp, peer->rtptimeout); - ast_rtp_set_rtpholdtimeout(dialog->trtp, peer->rtpholdtimeout); - ast_rtp_set_rtpkeepalive(dialog->trtp, peer->rtpkeepalive); + ast_rtp_instance_set_timeout(dialog->trtp, peer->rtptimeout); + ast_rtp_instance_set_hold_timeout(dialog->trtp, peer->rtpholdtimeout); } ast_string_field_set(dialog, peername, peer->name); @@ -4834,7 +5097,11 @@ ast_string_field_set(dialog, tohost, peer->tohost); ast_string_field_set(dialog, fullcontact, peer->fullcontact); ast_string_field_set(dialog, context, peer->context); + ast_string_field_set(dialog, cid_num, peer->cid_num); + ast_string_field_set(dialog, cid_name, peer->cid_name); + ast_string_field_set(dialog, mwi_from, peer->mwi_from); ast_string_field_set(dialog, parkinglot, peer->parkinglot); + ast_string_field_set(dialog, engine, peer->engine); ref_proxy(dialog, obproxy_get(dialog, peer)); dialog->callgroup = peer->callgroup; dialog->pickupgroup = peer->pickupgroup; @@ -4843,6 +5110,7 @@ dialog->rtptimeout = peer->rtptimeout; dialog->peerauth = peer->auth; dialog->maxcallbitrate = peer->maxcallbitrate; + dialog->allowed_methods = peer->allowed_methods; if (ast_strlen_zero(dialog->tohost)) ast_string_field_set(dialog, tohost, ast_inet_ntoa(dialog->sa.sin_addr)); if (!ast_strlen_zero(peer->fromdomain)) { @@ -4918,8 +5186,9 @@ if (peer) { int res; - if (newdialog) - dialog->socket.type = 0; + if (newdialog) { + set_socket_transport(&dialog->socket, 0); + } res = create_addr_from_peer(dialog, peer); if (!ast_strlen_zero(port)) { if ((portno = atoi(port))) { @@ -4930,7 +5199,9 @@ return res; } - do_setnat(dialog, ast_test_flag(&dialog->flags[0], SIP_NAT) & SIP_NAT_ROUTE); + if (dialog_initialize_rtp(dialog)) { + return -1; + } ast_string_field_set(dialog, tohost, peername); @@ -4981,7 +5252,7 @@ } if (!dialog->socket.type) - dialog->socket.type = SIP_TRANSPORT_UDP; + set_socket_transport(&dialog->socket, SIP_TRANSPORT_UDP); if (!dialog->socket.port) dialog->socket.port = bindaddr.sin_port; dialog->sa.sin_port = htons(portno); @@ -5075,7 +5346,7 @@ return res; } p->callingpres = ast->cid.cid_pres; - p->jointcapability = ast_translate_available_formats(p->capability, p->prefcodec); + p->jointcapability = ast_rtp_instance_available_formats(p->rtp, p->capability, p->prefcodec); p->jointnoncodeccapability = p->noncodeccapability; /* If there are no audio formats left to offer, punt */ @@ -5088,11 +5359,13 @@ p->t38.jointcapability = p->t38.capability; ast_debug(2, "Our T38 capability (%d), joint T38 capability (%d)\n", p->t38.capability, p->t38.jointcapability); + sip_pvt_lock(p); xmitres = transmit_invite(p, SIP_INVITE, 1, 2); + sip_pvt_unlock(p); if (xmitres == XMIT_ERROR) return -1; p->invitestate = INV_CALLING; - + /* Initialize auto-congest time */ AST_SCHED_REPLACE_UNREF(p->initid, sched, p->timer_b, auto_congest, p, dialog_unref(_data, "dialog ptr dec when SCHED_REPLACE del op succeeded"), @@ -5199,15 +5472,13 @@ p->notify_headers = NULL; } if (p->rtp) { - ast_rtp_destroy(p->rtp); + ast_rtp_instance_destroy(p->rtp); } if (p->vrtp) { - ast_rtp_destroy(p->vrtp); + ast_rtp_instance_destroy(p->vrtp); } if (p->trtp) { - while (ast_rtp_get_bridged(p->trtp)) - usleep(1); - ast_rtp_destroy(p->trtp); + ast_rtp_instance_destroy(p->trtp); } if (p->udptl) ast_udptl_destroy(p->udptl); @@ -5506,6 +5777,16 @@ case 606: /* Not acceptable */ return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL; default: + if (cause < 500 && cause >= 400) { + /* 4xx class error that is unknown - someting wrong with our request */ + return AST_CAUSE_INTERWORKING; + } else if (cause < 600 && cause >= 500) { + /* 5xx class error - problem in the remote end */ + return AST_CAUSE_CONGESTION; + } else if (cause < 700 && cause >= 600) { + /* 6xx - global errors in the 4xx class */ + return AST_CAUSE_INTERWORKING; + } return AST_CAUSE_NORMAL; } /* Never reached */ @@ -5591,7 +5872,6 @@ return 0; } - /*! \brief sip_hangup: Hangup SIP call * Part of PBX interface, called from ast_hangup */ static int sip_hangup(struct ast_channel *ast) @@ -5670,8 +5950,7 @@ append_history(p, needcancel ? "Cancel" : "Hangup", "Cause %s", p->owner ? ast_cause2str(p->hangupcause) : "Unknown"); /* Disconnect */ - if (p->vad) - ast_dsp_free(p->vad); + disable_digit_detect(p); p->owner = NULL; ast->tech_pvt = dialog_unref(ast->tech_pvt, "unref ast->tech_pvt"); @@ -5726,58 +6005,62 @@ if (!p->pendinginvite) { struct ast_channel *bridge = ast_bridged_channel(oldowner); - char *audioqos = ""; - char *videoqos = ""; - char *textqos = ""; + char quality_buf[AST_MAX_USER_FIELD], *quality; - /* We need to get the lock on bridge because ast_rtp_set_vars will attempt + /* We need to get the lock on bridge because ast_rtp_instance_set_stats_vars will attempt * to lock the bridge. This may get hairy... */ while (bridge && ast_channel_trylock(bridge)) { - struct ast_channel *chan = p->owner; sip_pvt_unlock(p); do { - /* Use chan since p->owner could go NULL on us - * while p is unlocked - */ - CHANNEL_DEADLOCK_AVOIDANCE(chan); + CHANNEL_DEADLOCK_AVOIDANCE(oldowner); } while (sip_pvt_trylock(p)); - bridge = p->owner ? ast_bridged_channel(p->owner) : NULL; + bridge = ast_bridged_channel(oldowner); } - if (p->rtp) - ast_rtp_set_vars(oldowner, p->rtp); + if (p->rtp) { + ast_rtp_instance_set_stats_vars(oldowner, p->rtp); + } if (bridge) { struct sip_pvt *q = bridge->tech_pvt; - if (IS_SIP_TECH(bridge->tech) && q && q->rtp) - ast_rtp_set_vars(bridge, q->rtp); + if (IS_SIP_TECH(bridge->tech) && q && q->rtp) { + ast_rtp_instance_set_stats_vars(bridge, q->rtp); + } ast_channel_unlock(bridge); } - if (p->vrtp) - videoqos = ast_rtp_get_quality(p->vrtp, NULL, RTPQOS_SUMMARY); - if (p->trtp) - textqos = ast_rtp_get_quality(p->trtp, NULL, RTPQOS_SUMMARY); + if (p->do_history || oldowner) { + if (p->rtp && (quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { + if (p->do_history) { + append_history(p, "RTCPaudio", "Quality:%s", quality); + } + if (oldowner) { + pbx_builtin_setvar_helper(oldowner, "RTPAUDIOQOS", quality); + } + } + if (p->vrtp && (quality = ast_rtp_instance_get_quality(p->vrtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { + if (p->do_history) { + append_history(p, "RTCPvideo", "Quality:%s", quality); + } + if (oldowner) { + pbx_builtin_setvar_helper(oldowner, "RTPVIDEOQOS", quality); + } + } + if (p->trtp && (quality = ast_rtp_instance_get_quality(p->trtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { + if (p->do_history) { + append_history(p, "RTCPtext", "Quality:%s", quality); + } + if (oldowner) { + pbx_builtin_setvar_helper(oldowner, "RTPTEXTQOS", quality); + } + } + } + /* Send a hangup */ transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1); - /* Get RTCP quality before end of call */ - if (p->do_history) { - if (p->rtp) - append_history(p, "RTCPaudio", "Quality:%s", audioqos); - if (p->vrtp) - append_history(p, "RTCPvideo", "Quality:%s", videoqos); - if (p->trtp) - append_history(p, "RTCPtext", "Quality:%s", textqos); - } - if (p->rtp && oldowner) - pbx_builtin_setvar_helper(oldowner, "RTPAUDIOQOS", audioqos); - if (p->vrtp && oldowner) - pbx_builtin_setvar_helper(oldowner, "RTPVIDEOQOS", videoqos); - if (p->trtp && oldowner) - pbx_builtin_setvar_helper(oldowner, "RTPTEXTQOS", textqos); } else { /* Note we will need a BYE when this all settles out but we can't send one while we have "INVITE" outstanding. */ @@ -5801,9 +6084,27 @@ { int fmt; const char *codec; + struct ast_channel* chan; - codec = pbx_builtin_getvar_helper(p->owner, "SIP_CODEC"); - if (!codec) + chan = ast_channel_ref(p->owner); + while (ast_channel_trylock(chan)) { + sip_pvt_unlock(p); + sched_yield(); + sip_pvt_lock(p); + } + + if (p->outgoing_call) { + codec = pbx_builtin_getvar_helper(p->owner, "SIP_CODEC_OUTBOUND"); + } else if (!(codec = pbx_builtin_getvar_helper(p->owner, "SIP_CODEC_INBOUND"))) { + codec = pbx_builtin_getvar_helper(p->owner, "SIP_CODEC"); + } + + codec = ast_strdupa(S_OR(codec, "")); + + ast_channel_unlock(chan); + chan = ast_channel_unref(chan); + + if (ast_strlen_zero(codec)) return; fmt = ast_getformatbyname(codec); @@ -5832,8 +6133,11 @@ ast_setstate(ast, AST_STATE_UP); ast_debug(1, "SIP answering channel: %s\n", ast->name); - ast_rtp_new_source(p->rtp); - res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL, FALSE); + if (p->t38.state == T38_PEER_DIRECT) { + change_t38_state(p, T38_ENABLED); + } + ast_rtp_instance_new_source(p->rtp); + res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL, FALSE, TRUE); ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); } sip_pvt_unlock(p); @@ -5867,16 +6171,16 @@ if ((ast->_state != AST_STATE_UP) && !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) && !ast_test_flag(&p->flags[0], SIP_OUTGOING)) { - ast_rtp_new_source(p->rtp); + ast_rtp_instance_new_source(p->rtp); p->invitestate = INV_EARLY_MEDIA; - transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE); - ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); + transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE, FALSE); + ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); } else if (p->t38.state == T38_ENABLED && !p->t38.direct) { change_t38_state(p, T38_DISABLED); transmit_reinvite_with_sdp(p, FALSE, FALSE); } else { p->lastrtptx = time(NULL); - res = ast_rtp_write(p->rtp, frame); + res = ast_rtp_instance_write(p->rtp, frame); } } sip_pvt_unlock(p); @@ -5891,11 +6195,11 @@ !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) && !ast_test_flag(&p->flags[0], SIP_OUTGOING)) { p->invitestate = INV_EARLY_MEDIA; - transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE); - ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); + transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE, FALSE); + ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); } p->lastrtptx = time(NULL); - res = ast_rtp_write(p->vrtp, frame); + res = ast_rtp_instance_write(p->vrtp, frame); } sip_pvt_unlock(p); } @@ -5904,7 +6208,7 @@ if (p) { sip_pvt_lock(p); if (p->red) { - red_buffer_t140(p->trtp, frame); + ast_rtp_red_buffer(p->trtp, frame); } else { if (p->trtp) { /* Activate text early media */ @@ -5912,11 +6216,11 @@ !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) && !ast_test_flag(&p->flags[0], SIP_OUTGOING)) { p->invitestate = INV_EARLY_MEDIA; - transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE); - ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); + transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE, FALSE); + ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); } p->lastrtptx = time(NULL); - res = ast_rtp_write(p->trtp, frame); + res = ast_rtp_instance_write(p->trtp, frame); } } sip_pvt_unlock(p); @@ -6004,11 +6308,15 @@ sip_pvt_lock(p); switch (ast_test_flag(&p->flags[0], SIP_DTMF)) { case SIP_DTMF_INBAND: - res = -1; /* Tell Asterisk to generate inband indications */ + if (p->rtp && ast_rtp_instance_dtmf_mode_get(p->rtp) == AST_RTP_DTMF_MODE_INBAND) { + ast_rtp_instance_dtmf_begin(p->rtp, digit); + } else { + res = -1; /* Tell Asterisk to generate inband indications */ + } break; case SIP_DTMF_RFC2833: if (p->rtp) - ast_rtp_senddigit_begin(p->rtp, digit); + ast_rtp_instance_dtmf_begin(p->rtp, digit); break; default: break; @@ -6033,10 +6341,14 @@ break; case SIP_DTMF_RFC2833: if (p->rtp) - ast_rtp_senddigit_end(p->rtp, digit); + ast_rtp_instance_dtmf_end(p->rtp, digit); break; case SIP_DTMF_INBAND: - res = -1; /* Tell Asterisk to stop inband indications */ + if (p->rtp && ast_rtp_instance_dtmf_mode_get(p->rtp) == AST_RTP_DTMF_MODE_INBAND) { + ast_rtp_instance_dtmf_end(p->rtp, digit); + } else { + res = -1; /* Tell Asterisk to stop inband indications */ + } break; } sip_pvt_unlock(p); @@ -6124,18 +6436,18 @@ !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) && !ast_test_flag(&p->flags[0], SIP_OUTGOING)) { p->invitestate = INV_EARLY_MEDIA; - transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE); - ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); + transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE, FALSE); + ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); break; } res = -1; break; case AST_CONTROL_HOLD: - ast_rtp_new_source(p->rtp); + ast_rtp_instance_new_source(p->rtp); ast_moh_start(ast, data, p->mohinterpret); break; case AST_CONTROL_UNHOLD: - ast_rtp_new_source(p->rtp); + ast_rtp_instance_new_source(p->rtp); ast_moh_stop(ast); break; case AST_CONTROL_VIDUPDATE: /* Request a video frame update */ @@ -6181,8 +6493,14 @@ } break; case AST_CONTROL_SRCUPDATE: - ast_rtp_new_source(p->rtp); + ast_rtp_instance_new_source(p->rtp); break; + case AST_CONTROL_CONNECTED_LINE: + update_connectedline(p, data, datalen); + break; + case AST_CONTROL_REDIRECTING: + update_redirecting(p, data, datalen); + break; case -1: res = -1; break; @@ -6195,7 +6513,6 @@ return res; } - /*! \brief Initiate a call in the SIP channel called from sip_request_call (calls from the pbx ) for outbound channels and from handle_request_invite for inbound channels @@ -6294,24 +6611,28 @@ else ast_debug(3, "This channel will not be able to handle video.\n"); - if ((ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) || (ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) { - i->vad = ast_dsp_new(); - ast_dsp_set_features(i->vad, DSP_FEATURE_DIGIT_DETECT); - if (global_relaxdtmf) - ast_dsp_set_digitmode(i->vad, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF); + if ((ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) || + (ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) { + if (!i->rtp || ast_rtp_instance_dtmf_mode_set(i->rtp, AST_RTP_DTMF_MODE_INBAND)) { + enable_digit_detect(i); + } + } else if (ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) { + if (i->rtp) { + ast_rtp_instance_dtmf_mode_set(i->rtp, AST_RTP_DTMF_MODE_RFC2833); + } } /* Set file descriptors for audio, video, realtime text and UDPTL as needed */ if (i->rtp) { - ast_channel_set_fd(tmp, 0, ast_rtp_fd(i->rtp)); - ast_channel_set_fd(tmp, 1, ast_rtcp_fd(i->rtp)); + ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(i->rtp, 0)); + ast_channel_set_fd(tmp, 1, ast_rtp_instance_fd(i->rtp, 1)); } if (needvideo && i->vrtp) { - ast_channel_set_fd(tmp, 2, ast_rtp_fd(i->vrtp)); - ast_channel_set_fd(tmp, 3, ast_rtcp_fd(i->vrtp)); + ast_channel_set_fd(tmp, 2, ast_rtp_instance_fd(i->vrtp, 0)); + ast_channel_set_fd(tmp, 3, ast_rtp_instance_fd(i->vrtp, 1)); } if (needtext && i->trtp) - ast_channel_set_fd(tmp, 4, ast_rtp_fd(i->trtp)); + ast_channel_set_fd(tmp, 4, ast_rtp_instance_fd(i->trtp, 0)); if (i->udptl) ast_channel_set_fd(tmp, 5, ast_udptl_fd(i->udptl)); @@ -6493,9 +6814,9 @@ for (pass = 0; name && pass < 2;pass++) { int x, len = strlen(name); for (x = *start; x < req->headers; x++) { - char *header = REQ_OFFSET_TO_STR(req, header[x]); + const char *header = REQ_OFFSET_TO_STR(req, header[x]); if (!strncasecmp(header, name, len)) { - char *r = header + len; /* skip name */ + const char *r = header + len; /* skip name */ if (sip_cfg.pedanticsipchecking) r = ast_skip_blanks(r); @@ -6535,19 +6856,19 @@ switch(ast->fdno) { case 0: - f = ast_rtp_read(p->rtp); /* RTP Audio */ + f = ast_rtp_instance_read(p->rtp, 0); /* RTP Audio */ break; case 1: - f = ast_rtcp_read(p->rtp); /* RTCP Control Channel */ + f = ast_rtp_instance_read(p->rtp, 1); /* RTCP Control Channel */ break; case 2: - f = ast_rtp_read(p->vrtp); /* RTP Video */ + f = ast_rtp_instance_read(p->vrtp, 0); /* RTP Video */ break; case 3: - f = ast_rtcp_read(p->vrtp); /* RTCP Control Channel for video */ + f = ast_rtp_instance_read(p->vrtp, 1); /* RTCP Control Channel for video */ break; case 4: - f = ast_rtp_read(p->trtp); /* RTP Text */ + f = ast_rtp_instance_read(p->trtp, 0); /* RTP Text */ if (sipdebug_text) { int i; unsigned char* arr = f->data.ptr; @@ -6589,8 +6910,8 @@ ast_set_write_format(p->owner, p->owner->writeformat); } - if (f && (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) && p->vad) { - f = ast_dsp_process(p->owner, p->vad, f); + if (f && p->dsp) { + f = ast_dsp_process(p->owner, p->dsp, f); if (f && f->frametype == AST_FRAME_DTMF) { if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_UDPTL) && f->subclass == 'f') { ast_debug(1, "Fax CNG detected on %s\n", ast->name); @@ -6706,7 +7027,7 @@ * remember to release the reference. */ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *sin, - int useglobal_nat, const int intended_method) + int useglobal_nat, const int intended_method, struct sip_request *req) { struct sip_pvt *p; @@ -6718,8 +7039,13 @@ return NULL; } + if (req) { + set_socket_transport(&p->socket, req->socket.type); /* Later in ast_sip_ouraddrfor we need this to choose the right ip and port for the specific transport */ + } else { + set_socket_transport(&p->socket, SIP_TRANSPORT_UDP); + } + p->socket.fd = -1; - p->socket.type = SIP_TRANSPORT_UDP; p->method = intended_method; p->initid = -1; p->waitid = -1; @@ -6742,7 +7068,7 @@ p->ourip = internip; else { p->sa = *sin; - ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip); + ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip, p); } /* Copy global flags to this PVT at setup. */ @@ -6756,50 +7082,11 @@ p->ocseq = INITIAL_CSEQ; if (sip_methods[intended_method].need_rtp) { - p->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); - /* If the global videosupport flag is on, we always create a RTP interface for video */ - if (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT)) - p->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); - if (ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT)) - p->trtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); - if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) - p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr); - if (!p->rtp|| (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) && !p->vrtp) - || (ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT) && !p->trtp)) { - ast_log(LOG_WARNING, "Unable to create RTP audio %s%ssession: %s\n", - ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "and video " : "", - ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT) ? "and text " : "", strerror(errno)); - if (p->chanvars) { - ast_variables_destroy(p->chanvars); - p->chanvars = NULL; - } - ao2_t_ref(p, -1, "failed to create RTP audio session, drop p"); - return NULL; + if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && (p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr))) { + ast_udptl_setqos(p->udptl, global_tos_audio, global_cos_audio); } - ast_rtp_setqos(p->rtp, global_tos_audio, global_cos_audio, "SIP RTP"); - ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); - ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); - ast_rtp_set_rtptimeout(p->rtp, global_rtptimeout); - ast_rtp_set_rtpholdtimeout(p->rtp, global_rtpholdtimeout); - ast_rtp_set_rtpkeepalive(p->rtp, global_rtpkeepalive); - if (p->vrtp) { - ast_rtp_setqos(p->vrtp, global_tos_video, global_cos_video, "SIP VRTP"); - ast_rtp_setdtmf(p->vrtp, 0); - ast_rtp_setdtmfcompensate(p->vrtp, 0); - ast_rtp_set_rtptimeout(p->vrtp, global_rtptimeout); - ast_rtp_set_rtpholdtimeout(p->vrtp, global_rtpholdtimeout); - ast_rtp_set_rtpkeepalive(p->vrtp, global_rtpkeepalive); - } - if (p->trtp) { - ast_rtp_setqos(p->trtp, global_tos_text, global_cos_text, "SIP TRTP"); - ast_rtp_setdtmf(p->trtp, 0); - ast_rtp_setdtmfcompensate(p->trtp, 0); - } - if (p->udptl) - ast_udptl_setqos(p->udptl, global_tos_audio, global_cos_audio); p->maxcallbitrate = default_maxcallbitrate; p->autoframing = global_autoframing; - ast_rtp_codec_setpref(p->rtp, &p->prefs); } if (useglobal_nat && sin) { @@ -6831,6 +7118,7 @@ } ast_string_field_set(p, context, sip_cfg.default_context); ast_string_field_set(p, parkinglot, default_parkinglot); + ast_string_field_set(p, engine, default_engine); AST_LIST_HEAD_INIT_NOLOCK(&p->request_queue); @@ -6985,7 +7273,7 @@ transmit_response_using_temp(callid, sin, 1, intended_method, req, "489 Bad event"); } else { /* Ok, time to create a new SIP dialog object, a pvt */ - if ((p = sip_alloc(callid, sin, 1, intended_method))) { + if ((p = sip_alloc(callid, sin, 1, intended_method, req))) { /* Ok, we've created a dialog, let's go and process it */ sip_pvt_lock(p); } else { @@ -7027,19 +7315,20 @@ enum sip_transport transport = SIP_TRANSPORT_UDP; char buf[256] = ""; char *username = NULL; - char *hostname=NULL, *secret=NULL, *authuser=NULL, *expire=NULL; + char *hostname=NULL, *secret=NULL, *authuser=NULL, *expire=NULL, *buf2=NULL; char *callback=NULL; if (!value) return -1; ast_copy_string(buf, value, sizeof(buf)); + buf2 = strrchr(buf, '@'); - /* split [/contact][~expiry] */ - expire = strchr(buf, '~'); + /* split [/extension][~expiry] */ + expire = strchr(buf2, '~'); if (expire) *expire++ = '\0'; - callback = strrchr(buf, '/'); + callback = strrchr(buf2, '/'); if (callback) *callback++ = '\0'; if (ast_strlen_zero(callback)) @@ -7182,6 +7471,97 @@ return 0; } +static void mark_method_allowed(unsigned int *allowed_methods, enum sipmethod method) +{ + (*allowed_methods) |= (1 << method); +} + +static void mark_method_unallowed(unsigned int *allowed_methods, enum sipmethod method) +{ + (*allowed_methods) &= ~(1 << method); +} + +static int is_method_allowed(unsigned int *allowed_methods, enum sipmethod method) +{ + return ((*allowed_methods) >> method) & 1; +} + +/*! + * \brief parse the Allow header to see what methods the endpoint we + * are communicating with allows. + * + * We parse the allow header on incoming Registrations and save the + * result to the SIP peer that is registering. When the registration + * expires, we clear what we know about the peer's allowed methods. + * When the peer re-registers, we once again parse to see if the + * list of allowed methods has changed. + * + * For peers that do not register, we parse the first message we receive + * during a call to see what is allowed, and save the information + * for the duration of the call. + * \param req The SIP request we are parsing + * \retval The methods allowed + */ +static unsigned int parse_allowed_methods(struct sip_request *req) +{ + char *allow = ast_strdupa(get_header(req, "Allow")); + char *method; + unsigned int allowed_methods = SIP_UNKNOWN; + + if (ast_strlen_zero(allow)) { + /* I have witnessed that REGISTER requests from Polycom phones do not + * place the phone's allowed methods in an Allow header. Instead, they place the + * allowed methods in a methods= parameter in the Contact header. + */ + char *contact = ast_strdupa(get_header(req, "Contact")); + char *methods = strstr(contact, ";methods="); + + if (ast_strlen_zero(methods)) { + /* RFC 3261 states: + * + * "The absence of an Allow header field MUST NOT be + * interpreted to mean that the UA sending the message supports no + * methods. Rather, it implies that the UA is not providing any + * information on what methods it supports." + * + * For simplicity, we'll assume that the peer allows all known + * SIP methods if they have no Allow header. We can then clear out the necessary + * bits if the peer lets us know that we have sent an unsupported method. + */ + return UINT_MAX; + } + allow = ast_strip_quoted(methods + 9, "\"", "\""); + } + for (method = strsep(&allow, ","); !ast_strlen_zero(method); method = strsep(&allow, ",")) { + int id = find_sip_method(ast_skip_blanks(method)); + if (id == SIP_UNKNOWN) { + continue; + } + mark_method_allowed(&allowed_methods, id); + } + return allowed_methods; +} + +/*! A wrapper for parse_allowed_methods geared toward sip_pvts + * + * This function, in addition to setting the allowed methods for a sip_pvt + * also will take into account the setting of the SIP_PAGE2_RPID_UPDATE flag. + * + * \param pvt The sip_pvt we are setting the allowed_methods for + * \param req The request which we are parsing + * \retval The methods alloweded by the sip_pvt + */ +static unsigned int set_pvt_allowed_methods(struct sip_pvt *pvt, struct sip_request *req) +{ + pvt->allowed_methods = parse_allowed_methods(req); + + if (ast_test_flag(&pvt->flags[1], SIP_PAGE2_RPID_UPDATE)) { + mark_method_allowed(&pvt->allowed_methods, SIP_UPDATE); + } + + return pvt->allowed_methods; +} + /*! \brief Parse multiline SIP headers into one header This is enabled if pedanticsipchecking is enabled */ static int lws2sws(char *msgbuf, int len) @@ -7397,7 +7777,7 @@ sdp part and the end boundry if it exists */ for (x = 0; x < (req->lines); x++) { - char *line = REQ_OFFSET_TO_STR(req, line[x]); + const char *line = REQ_OFFSET_TO_STR(req, line[x]); if (!strncasecmp(line, boundary, strlen(boundary))){ if (found_application_sdp && found_end_of_headers) { req->sdp_end = x-1; @@ -7446,7 +7826,7 @@ /* Continue since there may be a valid host in a c= line specific to the audio stream */ } /* We only want the m and c lines for audio */ - while ((m = get_sdp_iterate(&miterator, req, "m"))) { + for (m = get_sdp_iterate(&miterator, req, "m"); !ast_strlen_zero(m); m = get_sdp_iterate(&miterator, req, "m")) { if ((media == SDP_AUDIO && ((sscanf(m, "audio %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) || (sscanf(m, "audio %d RTP/AVP %n", &x, &len) == 1 && len > 0))) || (media == SDP_VIDEO && ((sscanf(m, "video %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) || @@ -7522,7 +7902,7 @@ int iterator; int sendonly = -1; int numberofports; - struct ast_rtp *newaudiortp, *newvideortp, *newtextrtp; /* Buffers for codec handling */ + struct ast_rtp_codecs newaudiortp, newvideortp, newtextrtp; int newjointcapability; /* Negotiated capability */ int newpeercapability; int newnoncodeccapability; @@ -7533,7 +7913,7 @@ int last_rtpmap_codec=0; char buf[SIPBUFSIZE]; - uint64_t rua_version; + int64_t rua_version; int red_data_pt[10]; int red_num_gen = 0; @@ -7547,34 +7927,11 @@ return -1; } - /* Initialize the temporary RTP structures we use to evaluate the offer from the peer */ -#ifdef LOW_MEMORY - newaudiortp = ast_threadstorage_get(&ts_audio_rtp, ast_rtp_alloc_size()); -#else - newaudiortp = alloca(ast_rtp_alloc_size()); -#endif - memset(newaudiortp, 0, ast_rtp_alloc_size()); - ast_rtp_new_init(newaudiortp); - ast_rtp_pt_clear(newaudiortp); + /* Make sure that the codec structures are all cleared out */ + ast_rtp_codecs_payloads_clear(&newaudiortp, NULL); + ast_rtp_codecs_payloads_clear(&newvideortp, NULL); + ast_rtp_codecs_payloads_clear(&newtextrtp, NULL); -#ifdef LOW_MEMORY - newvideortp = ast_threadstorage_get(&ts_video_rtp, ast_rtp_alloc_size()); -#else - newvideortp = alloca(ast_rtp_alloc_size()); -#endif - memset(newvideortp, 0, ast_rtp_alloc_size()); - ast_rtp_new_init(newvideortp); - ast_rtp_pt_clear(newvideortp); - -#ifdef LOW_MEMORY - newtextrtp = ast_threadstorage_get(&ts_text_rtp, ast_rtp_alloc_size()); -#else - newtextrtp = alloca(ast_rtp_alloc_size()); -#endif - memset(newtextrtp, 0, ast_rtp_alloc_size()); - ast_rtp_new_init(newtextrtp); - ast_rtp_pt_clear(newtextrtp); - /* Update our last rtprx when we receive an SDP, too */ p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */ @@ -7609,21 +7966,44 @@ ast_log(LOG_WARNING, "SDP syntax error in o= line\n"); return -1; } - if (!sscanf(token, "%" SCNu64, &rua_version)) { + if (!sscanf(token, "%" SCNd64, &rua_version)) { ast_log(LOG_WARNING, "SDP syntax error in o= line version\n"); return -1; } - if (ast_test_flag(&p->flags[1], SIP_PAGE2_IGNORESDPVERSION) - || p->sessionversion_remote < 0 - || p->sessionversion_remote != rua_version) { - + /* we need to check the SDP version number the other end sent us; + * our rules for deciding what to accept are a bit complex. + * + * 1) if 'ignoresdpversion' has been set for this dialog, then + * we will just accept whatever they sent and assume it is + * a modification of the session, even if it is not + * 2) otherwise, if this is the first SDP we've seen from them + * we accept it + * 3) otherwise, if the new SDP version number is higher than the + * old one, we accept it + * 4) otherwise, if this SDP is in response to us requesting a switch + * to T.38, we accept the SDP, but also generate a warning message + * that this peer should have the 'ignoresdpversion' option set, + * because it is not following the SDP offer/answer RFC; if we did + * not request a switch to T.38, then we stop parsing the SDP, as it + * has not changed from the previous version + */ + + if (ast_test_flag(&p->flags[1], SIP_PAGE2_IGNORESDPVERSION) || + (p->sessionversion_remote < 0) || + (p->sessionversion_remote < rua_version)) { p->sessionversion_remote = rua_version; p->session_modify = TRUE; - } else if (p->sessionversion_remote == rua_version) { - p->session_modify = FALSE; - ast_debug(2, "SDP version number same as previous SDP. Not parsing this SDP.\n"); - return 0; + } else { + if (p->t38.state == T38_LOCAL_REINVITE) { + p->sessionversion_remote = rua_version; + p->session_modify = TRUE; + ast_log(LOG_WARNING, "Call %s responded to our T.38 reinvite without changing SDP version; 'ignoresdpversion' should be set for this peer.\n", p->callid); + } else { + p->session_modify = FALSE; + ast_debug(2, "Call %s responded to our reinvite without changing SDP version; ignoring SDP.\n", p->callid); + return 0; + } } /* Try to find first media stream */ @@ -7655,12 +8035,14 @@ p->novideo = TRUE; p->notext = TRUE; - if (p->vrtp) - ast_rtp_pt_clear(newvideortp); /* Must be cleared in case no m=video line exists */ - - if (p->trtp) - ast_rtp_pt_clear(newtextrtp); /* Must be cleared in case no m=text line exists */ + if (p->vrtp) { + ast_rtp_codecs_payloads_clear(&newvideortp, NULL); + } + if (p->trtp) { + ast_rtp_codecs_payloads_clear(&newtextrtp, NULL); + } + /* Find media streams in this SDP offer */ while ((m = get_sdp_iterate(&iterator, req, "m"))[0] != '\0') { int x; @@ -7684,7 +8066,8 @@ } if (debug) ast_verbose("Found RTP audio format %d\n", codec); - ast_rtp_set_m_type(newaudiortp, codec); + + ast_rtp_codecs_payloads_set_m_type(&newaudiortp, NULL, codec); } } else if ((sscanf(m, "video %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) || (sscanf(m, "video %d RTP/AVP %n", &x, &len) == 1 && len >= 0)) { @@ -7700,7 +8083,7 @@ } if (debug) ast_verbose("Found RTP video format %d\n", codec); - ast_rtp_set_m_type(newvideortp, codec); + ast_rtp_codecs_payloads_set_m_type(&newvideortp, NULL, codec); } } else if ((sscanf(m, "text %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) || (sscanf(m, "text %d RTP/AVP %n", &x, &len) == 1 && len > 0)) { @@ -7716,7 +8099,7 @@ } if (debug) ast_verbose("Found RTP text format %d\n", codec); - ast_rtp_set_m_type(newtextrtp, codec); + ast_rtp_codecs_payloads_set_m_type(&newtextrtp, NULL, codec); } } else if (p->udptl && ( (sscanf(m, "image %d udptl t38%n", &x, &len) == 1 && len > 0) || (sscanf(m, "image %d UDPTL t38%n", &x, &len) == 1 && len > 0) )) { @@ -7781,10 +8164,10 @@ if (udptlportno > 0) { sin.sin_port = htons(udptlportno); if (ast_test_flag(&p->flags[0], SIP_NAT) && ast_test_flag(&p->flags[1], SIP_PAGE2_UDPTL_DESTINATION)) { - struct sockaddr_in peer; - ast_rtp_get_peer(p->rtp, &peer); - if (peer.sin_addr.s_addr) { - memcpy(&sin.sin_addr, &peer.sin_addr, sizeof(sin.sin_addr)); + struct sockaddr_in remote_address = { 0, }; + ast_rtp_instance_get_remote_address(p->rtp, &remote_address); + if (remote_address.sin_addr.s_addr) { + memcpy(&sin, &remote_address, sizeof(sin)); if (debug) { ast_log(LOG_DEBUG, "Peer T.38 UDPTL is set behind NAT and with destination, destination address now %s\n", ast_inet_ntoa(sin.sin_addr)); } @@ -7804,7 +8187,7 @@ if (p->rtp) { if (portno > 0) { sin.sin_port = htons(portno); - ast_rtp_set_peer(p->rtp, &sin); + ast_rtp_instance_set_remote_address(p->rtp, &sin); if (debug) ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); } else { @@ -7812,7 +8195,7 @@ if (debug) ast_verbose("Got T.38 Re-invite without audio. Keeping RTP active during T.38 session. Callid %s\n", p->callid); } else { - ast_rtp_stop(p->rtp); + ast_rtp_instance_stop(p->rtp); if (debug) ast_verbose("Peer doesn't provide audio. Callid %s\n", p->callid); } @@ -7895,18 +8278,17 @@ } } if (framing && p->autoframing) { - struct ast_codec_pref *pref = ast_rtp_codec_getpref(p->rtp); + struct ast_codec_pref *pref = &ast_rtp_instance_get_codecs(p->rtp)->pref; int codec_n; - int format = 0; - for (codec_n = 0; codec_n < MAX_RTP_PT; codec_n++) { - format = ast_rtp_codec_getformat(codec_n); - if (!format) /* non-codec or not found */ + for (codec_n = 0; codec_n < AST_RTP_MAX_PT; codec_n++) { + struct ast_rtp_payload_type format = ast_rtp_codecs_payload_lookup(ast_rtp_instance_get_codecs(p->rtp), codec_n); + if (!format.asterisk_format || !format.code) /* non-codec or not found */ continue; if (option_debug) - ast_log(LOG_DEBUG, "Setting framing for %d to %ld\n", format, framing); - ast_codec_pref_setsize(pref, format, framing); + ast_log(LOG_DEBUG, "Setting framing for %d to %ld\n", format.code, framing); + ast_codec_pref_setsize(pref, format.code, framing); } - ast_rtp_codec_setpref(p->rtp, pref); + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, pref); } continue; } @@ -7918,7 +8300,7 @@ sscanf(red_cp, "%u", &red_data_pt[red_num_gen]); red_cp = strtok(red_cp, "/"); - while (red_cp && red_num_gen++ < RED_MAX_GENERATION) { + while (red_cp && red_num_gen++ < AST_RED_MAX_GENERATION) { sscanf(red_cp, "%u", &red_data_pt[red_num_gen]); red_cp = strtok(NULL, "/"); } @@ -7927,15 +8309,15 @@ } if (sscanf(a, "fmtp: %u %63s", &codec, fmtp_string) == 2) { - struct rtpPayloadType payload; + struct ast_rtp_payload_type payload; unsigned int handled = 0; - payload = ast_rtp_lookup_pt(newaudiortp, codec); + payload = ast_rtp_codecs_payload_lookup(&newaudiortp, codec); if (!payload.code) { /* it wasn't found, try the video rtp */ - payload = ast_rtp_lookup_pt(newvideortp, codec); + payload = ast_rtp_codecs_payload_lookup(&newvideortp, codec); } - if (payload.code && payload.isAstFormat) { + if (payload.code && payload.asterisk_format) { unsigned int bit_rate; switch (payload.code) { @@ -7943,7 +8325,7 @@ if (sscanf(fmtp_string, "bitrate=%u", &bit_rate) == 1) { if (bit_rate != 32000) { ast_log(LOG_WARNING, "Got Siren7 offer at %d bps, but only 32000 bps supported; ignoring.\n", bit_rate); - ast_rtp_unset_m_type(newaudiortp, codec); + ast_rtp_codecs_payloads_unset(&newaudiortp, NULL, codec); } else { handled = 1; } @@ -7953,7 +8335,7 @@ if (sscanf(fmtp_string, "bitrate=%u", &bit_rate) == 1) { if (bit_rate != 48000) { ast_log(LOG_WARNING, "Got Siren14 offer at %d bps, but only 48000 bps supported; ignoring.\n", bit_rate); - ast_rtp_unset_m_type(newaudiortp, codec); + ast_rtp_codecs_payloads_unset(&newaudiortp, NULL, codec); } else { handled = 1; } @@ -7975,24 +8357,24 @@ /* Note: should really look at the '#chans' params too */ /* Note: This should all be done in the context of the m= above */ if (!strncasecmp(mimeSubtype, "H26", 3) || !strncasecmp(mimeSubtype, "MP4", 3)) { /* Video */ - if (ast_rtp_set_rtpmap_type_rate(newvideortp, codec, "video", mimeSubtype, 0, sample_rate) != -1) { + if (ast_rtp_codecs_payloads_set_rtpmap_type_rate(&newvideortp, NULL, codec, "video", mimeSubtype, 0, sample_rate) != -1) { if (debug) ast_verbose("Found video description format %s for ID %d\n", mimeSubtype, codec); found_rtpmap_codecs[last_rtpmap_codec] = codec; last_rtpmap_codec++; } else { - ast_rtp_unset_m_type(newvideortp, codec); + ast_rtp_codecs_payloads_unset(&newvideortp, NULL, codec); if (debug) ast_verbose("Found unknown media description format %s for ID %d\n", mimeSubtype, codec); } } else if (!strncasecmp(mimeSubtype, "T140", 4)) { /* Text */ if (p->trtp) { /* ast_verbose("Adding t140 mimeSubtype to textrtp struct\n"); */ - ast_rtp_set_rtpmap_type(newtextrtp, codec, "text", mimeSubtype, 0); + ast_rtp_codecs_payloads_set_rtpmap_type_rate(&newtextrtp, NULL, codec, "text", mimeSubtype, 0, sample_rate); } } else if (!strncasecmp(mimeSubtype, "RED", 3)) { /* Text with Redudancy */ if (p->trtp) { - ast_rtp_set_rtpmap_type(newtextrtp, codec, "text", mimeSubtype, 0); + ast_rtp_codecs_payloads_set_rtpmap_type_rate(&newtextrtp, NULL, codec, "text", mimeSubtype, 0, sample_rate); red_pt = codec; sprintf(red_fmtp, "fmtp:%d ", red_pt); @@ -8000,15 +8382,14 @@ ast_verbose("RED submimetype has payload type: %d\n", red_pt); } } else { /* Must be audio?? */ - if (ast_rtp_set_rtpmap_type_rate(newaudiortp, codec, "audio", mimeSubtype, - ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0, - sample_rate) != -1) { + if (ast_rtp_codecs_payloads_set_rtpmap_type_rate(&newaudiortp, NULL, codec, "audio", mimeSubtype, + ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0, sample_rate) != -1) { if (debug) ast_verbose("Found audio description format %s for ID %d\n", mimeSubtype, codec); found_rtpmap_codecs[last_rtpmap_codec] = codec; last_rtpmap_codec++; } else { - ast_rtp_unset_m_type(newaudiortp, codec); + ast_rtp_codecs_payloads_unset(&newaudiortp, NULL, codec); if (debug) ast_verbose("Found unknown media description format %s for ID %d\n", mimeSubtype, codec); } @@ -8147,15 +8528,14 @@ } /* Now gather all of the codecs that we are asked for: */ - ast_rtp_get_current_formats(newaudiortp, &peercapability, &peernoncodeccapability); - ast_rtp_get_current_formats(newvideortp, &vpeercapability, &vpeernoncodeccapability); - ast_rtp_get_current_formats(newtextrtp, &tpeercapability, &tpeernoncodeccapability); + ast_rtp_codecs_payload_formats(&newaudiortp, &peercapability, &peernoncodeccapability); + ast_rtp_codecs_payload_formats(&newvideortp, &vpeercapability, &vpeernoncodeccapability); + ast_rtp_codecs_payload_formats(&newtextrtp, &tpeercapability, &tpeernoncodeccapability); newjointcapability = p->capability & (peercapability | vpeercapability | tpeercapability); newpeercapability = (peercapability | vpeercapability | tpeercapability); newnoncodeccapability = p->noncodeccapability & peernoncodeccapability; - - + if (debug) { /* shame on whoever coded this.... */ char s1[SIPBUFSIZE], s2[SIPBUFSIZE], s3[SIPBUFSIZE], s4[SIPBUFSIZE], s5[SIPBUFSIZE]; @@ -8166,11 +8546,17 @@ ast_getformatname_multiple(s3, SIPBUFSIZE, vpeercapability), ast_getformatname_multiple(s4, SIPBUFSIZE, tpeercapability), ast_getformatname_multiple(s5, SIPBUFSIZE, newjointcapability)); + } + if (debug) { + struct ast_str *s1 = ast_str_alloca(SIPBUFSIZE); + struct ast_str *s2 = ast_str_alloca(SIPBUFSIZE); + struct ast_str *s3 = ast_str_alloca(SIPBUFSIZE); + ast_verbose("Non-codec capabilities (dtmf): us - %s, peer - %s, combined - %s\n", - ast_rtp_lookup_mime_multiple(s1, SIPBUFSIZE, p->noncodeccapability, 0, 0), - ast_rtp_lookup_mime_multiple(s2, SIPBUFSIZE, peernoncodeccapability, 0, 0), - ast_rtp_lookup_mime_multiple(s3, SIPBUFSIZE, newnoncodeccapability, 0, 0)); + ast_rtp_lookup_mime_multiple2(s1, p->noncodeccapability, 0, 0), + ast_rtp_lookup_mime_multiple2(s2, peernoncodeccapability, 0, 0), + ast_rtp_lookup_mime_multiple2(s3, newnoncodeccapability, 0, 0)); } if (!newjointcapability) { /* If T.38 was not negotiated either, totally bail out... */ @@ -8190,18 +8576,24 @@ p->peercapability = newpeercapability; /* The other sides capability in latest offer */ p->jointnoncodeccapability = newnoncodeccapability; /* DTMF capabilities */ + if (ast_test_flag(&p->flags[1], SIP_PAGE2_PREFERRED_CODEC)) { /* respond with single most preferred joint codec, limiting the other side's choice */ + p->jointcapability = ast_codec_choose(&p->prefs, p->jointcapability, 1); + } + if (p->jointcapability & AST_FORMAT_T140RED) { - p->red = 1; - rtp_red_init(p->trtp, 300, red_data_pt, 2); + p->red = 1; + ast_rtp_red_init(p->trtp, 300, red_data_pt, 2); } else { - p->red = 0; + p->red = 0; } - ast_rtp_pt_copy(p->rtp, newaudiortp); - if (p->vrtp) - ast_rtp_pt_copy(p->vrtp, newvideortp); - if (p->trtp) - ast_rtp_pt_copy(p->trtp, newtextrtp); + ast_rtp_codecs_payloads_copy(&newaudiortp, ast_rtp_instance_get_codecs(p->rtp), p->rtp); + if (p->vrtp) { + ast_rtp_codecs_payloads_copy(&newvideortp, ast_rtp_instance_get_codecs(p->vrtp), p->vrtp); + } + if (p->trtp) { + ast_rtp_codecs_payloads_copy(&newtextrtp, ast_rtp_instance_get_codecs(p->trtp), p->trtp); + } if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO) { ast_clear_flag(&p->flags[0], SIP_DTMF); @@ -8209,8 +8601,8 @@ /* XXX Would it be reasonable to drop the DSP at this point? XXX */ ast_set_flag(&p->flags[0], SIP_DTMF_RFC2833); /* Since RFC2833 is now negotiated we need to change some properties of the RTP stream */ - ast_rtp_setdtmf(p->rtp, 1); - ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); + ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, 1); + ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); } else { ast_set_flag(&p->flags[0], SIP_DTMF_INBAND); } @@ -8218,21 +8610,21 @@ /* Setup audio port number */ if (p->rtp && sin.sin_port) { - ast_rtp_set_peer(p->rtp, &sin); + ast_rtp_instance_set_remote_address(p->rtp, &sin); if (debug) ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); } /* Setup video port number */ if (p->vrtp && vsin.sin_port) { - ast_rtp_set_peer(p->vrtp, &vsin); + ast_rtp_instance_set_remote_address(p->vrtp, &vsin); if (debug) ast_verbose("Peer video RTP is at port %s:%d\n", ast_inet_ntoa(vsin.sin_addr), ntohs(vsin.sin_port)); } /* Setup text port number */ if (p->trtp && tsin.sin_port) { - ast_rtp_set_peer(p->trtp, &tsin); + ast_rtp_instance_set_remote_address(p->trtp, &tsin); if (debug) ast_verbose("Peer text RTP is at port %s:%d\n", ast_inet_ntoa(tsin.sin_addr), ntohs(tsin.sin_port)); } @@ -8279,7 +8671,7 @@ S_OR(p->mohsuggest, NULL), !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); if (sendonly) - ast_rtp_stop(p->rtp); + ast_rtp_instance_stop(p->rtp); /* RTCP needs to go ahead, even if we're on hold!!! */ /* Activate a re-invite */ ast_queue_frame(p->owner, &ast_null_frame); @@ -8749,7 +9141,7 @@ * Similarly, if we need to re-send an INVITE with auth credentials, then we * need to use the same branch as we did the first time we sent the INVITE. */ - if (sipmethod == SIP_CANCEL || (sipmethod == SIP_INVITE && !ast_strlen_zero(p->options->auth))) { + if (sipmethod == SIP_CANCEL || (sipmethod == SIP_INVITE && p->options && !ast_strlen_zero(p->options->auth))) { p->branch = p->invite_branch; build_via(p); } else if (newbranch) { @@ -8830,9 +9222,6 @@ if (!ast_strlen_zero(global_useragent)) add_header(req, "User-Agent", global_useragent); - if (!ast_strlen_zero(p->rpid)) - add_header(req, "Remote-Party-ID", p->rpid); - if (!ast_strlen_zero(p->url)) { add_header(req, "Access-URL", p->url); ast_string_field_set(p, url, NULL); @@ -8870,6 +9259,14 @@ return -1; } respprep(&resp, p, msg, req); + + if (ast_test_flag(&p->flags[0], SIP_SENDRPID) + && ast_test_flag(&p->flags[1], SIP_PAGE2_CONNECTLINEUPDATE_PEND) + && (!strncmp(msg, "180", 3) || !strncmp(msg, "183", 3))) { + ast_clear_flag(&p->flags[1], SIP_PAGE2_CONNECTLINEUPDATE_PEND); + add_rpid(&resp, p); + } + add_header_contentLength(&resp, 0); /* If we are cancelling an incoming invite for some reason, add information about the reason why we are doing this in clear text */ @@ -8924,7 +9321,7 @@ p->ourip = internip; else { p->sa = *sin; - ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip); + ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip, p); } p->branch = ast_random(); @@ -9089,6 +9486,89 @@ return 0; } +/*! + * \pre if p->owner exists, it must be locked + * \brief Add Remote-Party-ID header to SIP message + */ +static int add_rpid(struct sip_request *req, struct sip_pvt *p) +{ + struct ast_str *tmp = ast_str_alloca(256); + char *lid_num = NULL; + char *lid_name = NULL; + int lid_pres; + const char *fromdomain; + const char *privacy = NULL; + const char *screen = NULL; + const char *anonymous_string = "\"Anonymous\" "; + + if (!ast_test_flag(&p->flags[0], SIP_SENDRPID)) { + return 0; + } + + if (p->owner && p->owner->connected.id.number) + lid_num = p->owner->connected.id.number; + if (p->owner && p->owner->connected.id.name) + lid_name = p->owner->connected.id.name; + lid_pres = (p->owner) ? p->owner->connected.id.number_presentation : AST_PRES_NUMBER_NOT_AVAILABLE; + + if (ast_strlen_zero(lid_num)) + return 0; + if (ast_strlen_zero(lid_name)) + lid_name = lid_num; + fromdomain = S_OR(p->fromdomain, ast_inet_ntoa(p->ourip.sin_addr)); + + if (ast_test_flag(&p->flags[0], SIP_SENDRPID_PAI)) { + if ((lid_pres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED) { + ast_str_set(&tmp, -1, "%s", anonymous_string); + } else { + ast_str_set(&tmp, -1, "\"%s\" ", lid_name, lid_num, fromdomain); + } + add_header(req, "P-Asserted-Identity", ast_str_buffer(tmp)); + } else { + ast_str_set(&tmp, -1, "\"%s\" ;party=%s", lid_name, lid_num, fromdomain, ast_test_flag(&p->flags[0], SIP_OUTGOING) ? "calling" : "called"); + + switch (lid_pres) { + case AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED: + case AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN: + privacy = "off"; + screen = "no"; + break; + case AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN: + case AST_PRES_ALLOWED_NETWORK_NUMBER: + privacy = "off"; + screen = "yes"; + break; + case AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED: + case AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN: + privacy = "full"; + screen = "no"; + break; + case AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN: + case AST_PRES_PROHIB_NETWORK_NUMBER: + privacy = "full"; + screen = "yes"; + break; + case AST_PRES_NUMBER_NOT_AVAILABLE: + break; + default: + if ((lid_pres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED) { + privacy = "full"; + } + else + privacy = "off"; + screen = "no"; + break; + } + + if (!ast_strlen_zero(privacy) && !ast_strlen_zero(screen)) { + ast_str_append(&tmp, -1, ";privacy=%s;screen=%s", privacy, screen); + } + + add_header(req, "Remote-Party-ID", ast_str_buffer(tmp)); + } + return 0; +} + /*! \brief add XML encoded media control with update \note XML: The only way to turn 0 bits of information into a few hundred. (markster) */ static int add_vidupdate(struct sip_request *req) @@ -9120,19 +9600,19 @@ if (debug) ast_verbose("Adding codec 0x%x (%s) to SDP\n", codec, ast_getformatname(codec)); - if ((rtp_code = ast_rtp_lookup_code(p->rtp, 1, codec)) == -1) + if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(p->rtp), 1, codec)) == -1) return; if (p->rtp) { - struct ast_codec_pref *pref = ast_rtp_codec_getpref(p->rtp); + struct ast_codec_pref *pref = &ast_rtp_instance_get_codecs(p->rtp)->pref; fmt = ast_codec_pref_getsize(pref, codec); } else /* I dont see how you couldn't have p->rtp, but good to check for and error out if not there like earlier code */ return; ast_str_append(m_buf, 0, " %d", rtp_code); ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code, - ast_rtp_lookup_mime_subtype(1, codec, + ast_rtp_lookup_mime_subtype2(1, codec, ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0), - ast_rtp_lookup_sample_rate(1, codec)); + ast_rtp_lookup_sample_rate2(1, codec)); switch (codec) { case AST_FORMAT_G729A: @@ -9179,13 +9659,13 @@ if (debug) ast_verbose("Adding video codec 0x%x (%s) to SDP\n", codec, ast_getformatname(codec)); - if ((rtp_code = ast_rtp_lookup_code(p->vrtp, 1, codec)) == -1) + if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(p->vrtp), 1, codec)) == -1) return; ast_str_append(m_buf, 0, " %d", rtp_code); ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code, - ast_rtp_lookup_mime_subtype(1, codec, 0), - ast_rtp_lookup_sample_rate(1, codec)); + ast_rtp_lookup_mime_subtype2(1, codec, 0), + ast_rtp_lookup_sample_rate2(1, codec)); /* Add fmtp code here */ } @@ -9202,20 +9682,21 @@ if (debug) ast_verbose("Adding text codec 0x%x (%s) to SDP\n", codec, ast_getformatname(codec)); - if ((rtp_code = ast_rtp_lookup_code(p->trtp, 1, codec)) == -1) + if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(p->trtp), 1, codec)) == -1) return; ast_str_append(m_buf, 0, " %d", rtp_code); ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code, - ast_rtp_lookup_mime_subtype(1, codec, 0), - ast_rtp_lookup_sample_rate(1, codec)); + ast_rtp_lookup_mime_subtype2(1, codec, 0), + ast_rtp_lookup_sample_rate2(1, codec)); /* Add fmtp code here */ if (codec == AST_FORMAT_T140RED) { - ast_str_append(a_buf, 0, "a=fmtp:%d %d/%d/%d\r\n", rtp_code, - ast_rtp_lookup_code(p->trtp, 1, AST_FORMAT_T140), - ast_rtp_lookup_code(p->trtp, 1, AST_FORMAT_T140), - ast_rtp_lookup_code(p->trtp, 1, AST_FORMAT_T140)); + int t140code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(p->trtp), 1, AST_FORMAT_T140); + ast_str_append(a_buf, 0, "a=fmtp:%d %d/%d/%d\r\n", rtp_code, + t140code, + t140code, + t140code); } } @@ -9258,14 +9739,14 @@ int rtp_code; if (debug) - ast_verbose("Adding non-codec 0x%x (%s) to SDP\n", format, ast_rtp_lookup_mime_subtype(0, format, 0)); - if ((rtp_code = ast_rtp_lookup_code(p->rtp, 0, format)) == -1) + ast_verbose("Adding non-codec 0x%x (%s) to SDP\n", format, ast_rtp_lookup_mime_subtype2(0, format, 0)); + if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(p->rtp), 0, format)) == -1) return; ast_str_append(m_buf, 0, " %d", rtp_code); ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code, - ast_rtp_lookup_mime_subtype(0, format, 0), - ast_rtp_lookup_sample_rate(0, format)); + ast_rtp_lookup_mime_subtype2(0, format, 0), + ast_rtp_lookup_sample_rate2(0, format)); if (format == AST_RTP_DTMF) /* Indicate we support DTMF and FLASH... */ ast_str_append(a_buf, 0, "a=fmtp:%d 0-16\r\n", rtp_code); } @@ -9278,11 +9759,11 @@ struct sockaddr_in *dest, struct sockaddr_in *vdest) { /* First, get our address */ - ast_rtp_get_us(p->rtp, sin); + ast_rtp_instance_get_local_address(p->rtp, sin); if (p->vrtp) - ast_rtp_get_us(p->vrtp, vsin); + ast_rtp_instance_get_local_address(p->vrtp, vsin); if (p->trtp) - ast_rtp_get_us(p->trtp, tsin); + ast_rtp_instance_get_local_address(p->trtp, tsin); /* Now, try to figure out where we want them to send data */ /* Is this a re-invite to move the media out, then use the original offer from caller */ @@ -9317,11 +9798,11 @@ int len = 0; int alreadysent = 0; - struct sockaddr_in sin; - struct sockaddr_in vsin; - struct sockaddr_in tsin; - struct sockaddr_in dest; - struct sockaddr_in udptlsin; + struct sockaddr_in sin = { 0, }; + struct sockaddr_in vsin = { 0, }; + struct sockaddr_in tsin = { 0, }; + struct sockaddr_in dest = { 0, }; + struct sockaddr_in udptlsin = { 0, }; struct sockaddr_in vdest = { 0, }; struct sockaddr_in tdest = { 0, }; struct sockaddr_in udptldest = { 0, }; @@ -9701,7 +10182,7 @@ /*! \brief Used for 200 OK and 183 early media \return Will return XMIT_ERROR for network errors. */ -static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable, int oldsdp) +static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable, int oldsdp, int rpid) { struct sip_request resp; int seqno; @@ -9710,10 +10191,13 @@ return -1; } respprep(&resp, p, msg, req); + if (rpid == TRUE) { + add_rpid(&resp, p); + } if (p->rtp) { if (!p->autoframing && !ast_test_flag(&p->flags[0], SIP_OUTGOING)) { ast_debug(1, "Setting framing from config on incoming call\n"); - ast_rtp_codec_setpref(p->rtp, &p->prefs); + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, &p->prefs); } try_suggested_sip_codec(p); if (p->t38.state == T38_PEER_DIRECT || p->t38.state == T38_ENABLED) { @@ -9799,8 +10283,13 @@ add_header(&req, "X-asterisk-Info", "SIP re-invite (External RTP bridge)"); } + if (ast_test_flag(&p->flags[1], SIP_SENDRPID)) + add_rpid(&req, p); + if (p->do_history) append_history(p, "ReInv", "Re-invite sent"); + + try_suggested_sip_codec(p); if (t38version) add_sdp(&req, p, oldsdp, FALSE, TRUE); else @@ -9847,95 +10336,18 @@ { int ourport = ntohs(p->ourip.sin_port); - - if (p->socket.type & SIP_TRANSPORT_UDP) { - if (!sip_standard_port(p->socket.type, ourport)) + /* only add port if it's non-standard for the transport type */ + if (!sip_standard_port(p->socket.type, ourport)) { + if (p->socket.type == SIP_TRANSPORT_UDP) ast_string_field_build(p, our_contact, "", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip.sin_addr), ourport); else + ast_string_field_build(p, our_contact, "", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip.sin_addr), ourport, get_transport(p->socket.type)); + } else { + if (p->socket.type == SIP_TRANSPORT_UDP) ast_string_field_build(p, our_contact, "", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip.sin_addr)); - } else { - /*! \todo We should not always add port here. Port is only added if it's non-standard (see code above) */ - ast_string_field_build(p, our_contact, "", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip.sin_addr), ourport, get_transport(p->socket.type)); - } -} - -/*! \brief Build the Remote Party-ID & From using callingpres options */ -static void build_rpid(struct sip_pvt *p) -{ - int send_pres_tags = TRUE; - const char *privacy=NULL; - const char *screen=NULL; - char buf[256]; - const char *clid = default_callerid; - const char *clin = NULL; - const char *fromdomain; - - if (!ast_strlen_zero(p->rpid) || !ast_strlen_zero(p->rpid_from)) - return; - - if (p->owner && p->owner->cid.cid_num) - clid = p->owner->cid.cid_num; - if (p->owner && p->owner->cid.cid_name) - clin = p->owner->cid.cid_name; - if (ast_strlen_zero(clin)) - clin = clid; - - switch (p->callingpres) { - case AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED: - privacy = "off"; - screen = "no"; - break; - case AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN: - privacy = "off"; - screen = "yes"; - break; - case AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN: - privacy = "off"; - screen = "no"; - break; - case AST_PRES_ALLOWED_NETWORK_NUMBER: - privacy = "off"; - screen = "yes"; - break; - case AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED: - privacy = "full"; - screen = "no"; - break; - case AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN: - privacy = "full"; - screen = "yes"; - break; - case AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN: - privacy = "full"; - screen = "no"; - break; - case AST_PRES_PROHIB_NETWORK_NUMBER: - privacy = "full"; - screen = "yes"; - break; - case AST_PRES_NUMBER_NOT_AVAILABLE: - send_pres_tags = FALSE; - break; - default: - ast_log(LOG_WARNING, "Unsupported callingpres (%d)\n", p->callingpres); - if ((p->callingpres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED) - privacy = "full"; else - privacy = "off"; - screen = "no"; - break; + ast_string_field_build(p, our_contact, "", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip.sin_addr), get_transport(p->socket.type)); } - - fromdomain = S_OR(p->fromdomain, ast_inet_ntoa(p->ourip.sin_addr)); - - snprintf(buf, sizeof(buf), "\"%s\" ", clin, clid, fromdomain); - if (send_pres_tags) - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ";privacy=%s;screen=%s", privacy, screen); - ast_string_field_set(p, rpid, buf); - - ast_string_field_build(p, rpid_from, "\"%s\" ;tag=%s", clin, - S_OR(p->fromuser, clid), - fromdomain, p->tag); } /*! \brief Initiate new SIP request to peer/user */ @@ -9973,16 +10385,21 @@ snprintf(p->lastmsg, sizeof(p->lastmsg), "Init: %s", sip_methods[sipmethod].text); - if (p->owner) { - l = p->owner->cid.cid_num; - n = p->owner->cid.cid_name; + if (p->owner && (p->owner->connected.id.number_presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) { + l = p->owner->connected.id.number; + n = p->owner->connected.id.name; } - /* if we are not sending RPID and user wants his callerid restricted */ - if (!ast_test_flag(&p->flags[0], SIP_SENDRPID) && - ((p->callingpres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED)) { - l = CALLERID_UNKNOWN; - n = l; + + /* Hey, it's a NOTIFY! See if they've configured a mwi_from. + * XXX Right now, this logic works because the only place that mwi_from + * is set on the sip_pvt is in sip_send_mwi_to_peer. If things changed, then + * we might end up putting the mwi_from setting into other types of NOTIFY + * messages as well. + */ + if (sipmethod == SIP_NOTIFY && !ast_strlen_zero(p->mwi_from)) { + l = p->mwi_from; } + if (ast_strlen_zero(l)) l = default_callerid; if (ast_strlen_zero(n)) @@ -10070,12 +10487,7 @@ /* SLD: FIXME?: do Route: here too? I think not cos this is the first request. * OTOH, then we won't have anything in p->route anyway */ - /* Build Remote Party-ID and From */ - if (ast_test_flag(&p->flags[0], SIP_SENDRPID) && (sipmethod == SIP_INVITE)) { - build_rpid(p); - add_header(req, "From", p->rpid_from); - } else - add_header(req, "From", from); + add_header(req, "From", from); add_header(req, "To", to); ast_string_field_set(p, exten, l); build_contact(p); @@ -10084,10 +10496,46 @@ add_header(req, "CSeq", tmp_n); if (!ast_strlen_zero(global_useragent)) add_header(req, "User-Agent", global_useragent); - if (!ast_strlen_zero(p->rpid)) - add_header(req, "Remote-Party-ID", p->rpid); } +/*! \brief Add "Diversion" header to outgoing message + * + * We need to add a Diversion header if the owner channel of + * this dialog has redirecting information associated with it. + * + * \param req The request/response to which we will add the header + * \param pvt The sip_pvt which represents the call-leg + * \param apr Redirecting data used to make the diversion header + */ +static void add_diversion_header(struct sip_request *req, struct sip_pvt *pvt) +{ + const char *diverting_number; + const char *diverting_name; + const char *reason; + char header_text[256]; + + if (!pvt->owner) { + return; + } + + diverting_number = pvt->owner->cid.cid_rdnis; + diverting_name = pvt->owner->redirecting.from.name; + reason = sip_reason_code_to_str(pvt->owner->redirecting.reason); + + if (ast_strlen_zero(diverting_number)) { + return; + } + + /* We at least have a number to place in the Diversion header, which is enough */ + if (ast_strlen_zero(diverting_name)) { + snprintf(header_text, sizeof(header_text), ";reason=%s", diverting_number, ast_inet_ntoa(pvt->ourip.sin_addr), reason); + } else { + snprintf(header_text, sizeof(header_text), "\"%s\" ;reason=%s", diverting_name, diverting_number, ast_inet_ntoa(pvt->ourip.sin_addr), reason); + } + + add_header(req, "Diversion", header_text); +} + /*! \brief Build REFER/INVITE/OPTIONS/SUBSCRIBE message and transmit it \param init 0 = Prepare request within dialog, 1= prepare request, new branch, 2= prepare new request and new dialog. do_proxy_auth calls this with init!=2 \param p sip_pvt structure @@ -10206,13 +10654,20 @@ ast_channel_unlock(chan); } + if ((sipmethod == SIP_INVITE || sipmethod == SIP_UPDATE) && ast_test_flag(&p->flags[0], SIP_SENDRPID)) + add_rpid(&req, p); + if (sipmethod == SIP_INVITE) { + add_diversion_header(&req, p); + } if (sdp) { if (p->udptl && p->t38.state == T38_LOCAL_REINVITE) { ast_udptl_offered_from_local(p->udptl, 1); ast_debug(1, "T38 is in state %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : ""); add_sdp(&req, p, FALSE, FALSE, TRUE); - } else if (p->rtp) + } else if (p->rtp) { + try_suggested_sip_codec(p); add_sdp(&req, p, FALSE, TRUE, FALSE); + } } else { if (!p->notify_headers) { add_header_contentLength(&req, 0); @@ -10221,7 +10676,9 @@ if (!p->initreq.headers || init > 2) initialize_initreq(p, &req); - p->lastinvite = p->ocseq; + if (sipmethod == SIP_INVITE) { + p->lastinvite = p->ocseq; + } return send_request(p, &req, init ? XMIT_CRITICAL : XMIT_RELIABLE, p->ocseq); } @@ -10258,7 +10715,7 @@ } /* Create a dialog that we will use for the subscription */ - if (!(mwi->call = sip_alloc(NULL, NULL, 0, SIP_SUBSCRIBE))) { + if (!(mwi->call = sip_alloc(NULL, NULL, 0, SIP_SUBSCRIBE, NULL))) { return -1; } @@ -10296,9 +10753,9 @@ if (!ast_strlen_zero(mwi->secret)) { ast_string_field_set(mwi->call, peersecret, mwi->secret); } - mwi->call->socket.type = mwi->transport; + set_socket_transport(&mwi->call->socket, mwi->transport); mwi->call->socket.port = htons(mwi->portno); - ast_sip_ouraddrfor(&mwi->call->sa.sin_addr, &mwi->call->ourip); + ast_sip_ouraddrfor(&mwi->call->sa.sin_addr, &mwi->call->ourip, mwi->call); build_contact(mwi->call); build_via(mwi->call); build_callid_pvt(mwi->call); @@ -10313,30 +10770,31 @@ return 0; } -static int find_calling_channel(struct ast_channel *c, void *data) { +static int find_calling_channel(void *obj, void *arg, void *data, int flags) +{ + struct ast_channel *c = obj; struct sip_pvt *p = data; + int res; - return (c->pbx && + ast_channel_lock(c); + + res = (c->pbx && (!strcasecmp(c->macroexten, p->exten) || !strcasecmp(c->exten, p->exten)) && (sip_cfg.notifycid == IGNORE_CONTEXT || !strcasecmp(c->context, p->context))); + + ast_channel_unlock(c); + + return res ? CMP_MATCH | CMP_STOP : 0; } -/*! \brief Used in the SUBSCRIBE notification subsystem (RFC3265) */ -static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout) +/*! \brief Builds XML portion of state NOTIFY messages */ +static void state_notify_build_xml(int state, int full, const char *exten, const char *context, struct ast_str **tmp, struct sip_pvt *p, int subscribed, const char *mfrom, const char *mto) { - struct ast_str *tmp = ast_str_alloca(4000); - char from[256], to[256]; - char *c, *mfrom, *mto; - struct sip_request req; + enum state { NOTIFY_OPEN, NOTIFY_INUSE, NOTIFY_CLOSED } local_state = NOTIFY_OPEN; + const char *statestring = "terminated"; + const char *pidfstate = "--"; + const char *pidfnote= "Ready"; char hint[AST_MAX_EXTENSION]; - char *statestring = "terminated"; - const struct cfsubscription_types *subscriptiontype; - enum state { NOTIFY_OPEN, NOTIFY_INUSE, NOTIFY_CLOSED } local_state = NOTIFY_OPEN; - char *pidfstate = "--"; - char *pidfnote= "Ready"; - - memset(from, 0, sizeof(from)); - memset(to, 0, sizeof(to)); switch (state) { case (AST_EXTENSION_RINGING | AST_EXTENSION_INUSE): @@ -10381,10 +10839,8 @@ break; } - subscriptiontype = find_subscription_type(p->subscribed); - /* Check which device/devices we are watching and if they are registered */ - if (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, p->context, p->exten)) { + if (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten)) { char *hint2 = hint, *individual_hint = NULL; int hint_count = 0, unavailable_count = 0; @@ -10405,103 +10861,64 @@ } } - ast_copy_string(from, get_header(&p->initreq, "From"), sizeof(from)); - c = get_in_brackets(from); - if (strncasecmp(c, "sip:", 4) && strncasecmp(c, "sips:", 5)) { - ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c); - return -1; - } - - mfrom = remove_uri_parameters(c); - - ast_copy_string(to, get_header(&p->initreq, "To"), sizeof(to)); - c = get_in_brackets(to); - if (strncasecmp(c, "sip:", 4) && strncasecmp(c, "sips:", 5)) { - ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c); - return -1; - } - mto = remove_uri_parameters(c); - - reqprep(&req, p, SIP_NOTIFY, 0, 1); - - - add_header(&req, "Event", subscriptiontype->event); - add_header(&req, "Content-Type", subscriptiontype->mediatype); - switch(state) { - case AST_EXTENSION_DEACTIVATED: - if (timeout) - add_header(&req, "Subscription-State", "terminated;reason=timeout"); - else { - add_header(&req, "Subscription-State", "terminated;reason=probation"); - add_header(&req, "Retry-After", "60"); - } - break; - case AST_EXTENSION_REMOVED: - add_header(&req, "Subscription-State", "terminated;reason=noresource"); - break; - default: - if (p->expiry) - add_header(&req, "Subscription-State", "active"); - else /* Expired */ - add_header(&req, "Subscription-State", "terminated;reason=timeout"); - } - switch (p->subscribed) { + switch (subscribed) { case XPIDF_XML: case CPIM_PIDF_XML: - ast_str_append(&tmp, 0, + ast_str_append(tmp, 0, "\n" "\n" "\n"); - ast_str_append(&tmp, 0, "\n", mfrom); - ast_str_append(&tmp, 0, "\n", p->exten); - ast_str_append(&tmp, 0, "
\n", mto); - ast_str_append(&tmp, 0, "\n", (local_state == NOTIFY_OPEN) ? "open" : (local_state == NOTIFY_INUSE) ? "inuse" : "closed"); - ast_str_append(&tmp, 0, "\n", (local_state == NOTIFY_OPEN) ? "online" : (local_state == NOTIFY_INUSE) ? "onthephone" : "offline"); - ast_str_append(&tmp, 0, "
\n
\n
\n"); + ast_str_append(tmp, 0, "\n", mfrom); + ast_str_append(tmp, 0, "\n", exten); + ast_str_append(tmp, 0, "
\n", mto); + ast_str_append(tmp, 0, "\n", (local_state == NOTIFY_OPEN) ? "open" : (local_state == NOTIFY_INUSE) ? "inuse" : "closed"); + ast_str_append(tmp, 0, "\n", (local_state == NOTIFY_OPEN) ? "online" : (local_state == NOTIFY_INUSE) ? "onthephone" : "offline"); + ast_str_append(tmp, 0, "
\n
\n\n"); break; case PIDF_XML: /* Eyebeam supports this format */ - ast_str_append(&tmp, 0, + ast_str_append(tmp, 0, "\n" "\n", mfrom); - ast_str_append(&tmp, 0, "\n"); + ast_str_append(tmp, 0, "\n"); if (pidfstate[0] != '-') - ast_str_append(&tmp, 0, "\n", pidfstate); - ast_str_append(&tmp, 0, "\n"); - ast_str_append(&tmp, 0, "%s\n", pidfnote); /* Note */ - ast_str_append(&tmp, 0, "\n", p->exten); /* Tuple start */ - ast_str_append(&tmp, 0, "%s\n", mto); + ast_str_append(tmp, 0, "\n", pidfstate); + ast_str_append(tmp, 0, "\n"); + ast_str_append(tmp, 0, "%s\n", pidfnote); /* Note */ + ast_str_append(tmp, 0, "\n", exten); /* Tuple start */ + ast_str_append(tmp, 0, "%s\n", mto); if (pidfstate[0] == 'b') /* Busy? Still open ... */ - ast_str_append(&tmp, 0, "open\n"); + ast_str_append(tmp, 0, "open\n"); else - ast_str_append(&tmp, 0, "%s\n", (local_state != NOTIFY_CLOSED) ? "open" : "closed"); - ast_str_append(&tmp, 0, "\n\n"); + ast_str_append(tmp, 0, "%s\n", (local_state != NOTIFY_CLOSED) ? "open" : "closed"); + ast_str_append(tmp, 0, "\n\n"); break; case DIALOG_INFO_XML: /* SNOM subscribes in this format */ - ast_str_append(&tmp, 0, "\n"); - ast_str_append(&tmp, 0, "\n", p->dialogver++, full ? "full" : "partial", mto); + ast_str_append(tmp, 0, ""); + ast_str_append(tmp, 0, "", p->dialogver, full ? "full" : "partial", mto); if ((state & AST_EXTENSION_RINGING) && sip_cfg.notifyringing) { - const char *local_display = p->exten; - char *local_target = mto; + const char *local_display = exten; + char *local_target = ast_strdupa(mto); /* There are some limitations to how this works. The primary one is that the callee must be dialing the same extension that is being monitored. Simply dialing the hint'd device is not sufficient. */ if (sip_cfg.notifycid) { - struct ast_channel *caller = ast_channel_search_locked(find_calling_channel, p); + struct ast_channel *caller; - if (caller) { + if ((caller = ast_channel_callback(find_calling_channel, NULL, p, 0))) { int need = strlen(caller->cid.cid_num) + strlen(p->fromdomain) + sizeof("sip:@"); local_target = alloca(need); + ast_channel_lock(caller); snprintf(local_target, need, "sip:%s@%s", caller->cid.cid_num, p->fromdomain); local_display = ast_strdupa(caller->cid.cid_name); ast_channel_unlock(caller); - caller = NULL; + caller = ast_channel_unref(caller); } } /* We create a fake call-id which the phone will send back in an INVITE Replaces header which we can grab and do some magic with. */ - ast_str_append(&tmp, 0, + ast_str_append(tmp, 0, "\n" "\n" /* See the limitations of this above. Luckily the phone seems to still be @@ -10513,23 +10930,102 @@ "%s\n" "\n" "\n", - p->exten, p->callid, local_display, local_target, local_target, mto, mto); + exten, p->callid, local_display, local_target, local_target, mto, mto); } else { - ast_str_append(&tmp, 0, "\n", p->exten); + ast_str_append(tmp, 0, "", exten); } - ast_str_append(&tmp, 0, "%s\n", statestring); + ast_str_append(tmp, 0, "%s\n", statestring); if (state == AST_EXTENSION_ONHOLD) { - ast_str_append(&tmp, 0, "\n\n" - "\n" - "\n\n", mto); + ast_str_append(tmp, 0, "\n\n" + "\n" + "\n\n", mto); } - ast_str_append(&tmp, 0, "\n\n"); + ast_str_append(tmp, 0, "\n\n"); break; case NONE: default: break; } +} + +/*! \brief Used in the SUBSCRIBE notification subsystem (RFC3265) */ +static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout) +{ + struct ast_str *tmp = ast_str_alloca(4000); + char from[256], to[256]; + char *c, *mfrom, *mto; + struct sip_request req; + const struct cfsubscription_types *subscriptiontype; + + memset(from, 0, sizeof(from)); + memset(to, 0, sizeof(to)); + + subscriptiontype = find_subscription_type(p->subscribed); + + ast_copy_string(from, get_header(&p->initreq, "From"), sizeof(from)); + c = get_in_brackets(from); + if (strncasecmp(c, "sip:", 4) && strncasecmp(c, "sips:", 5)) { + ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c); + return -1; + } + + mfrom = remove_uri_parameters(c); + + ast_copy_string(to, get_header(&p->initreq, "To"), sizeof(to)); + c = get_in_brackets(to); + if (strncasecmp(c, "sip:", 4) && strncasecmp(c, "sips:", 5)) { + ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c); + return -1; + } + mto = remove_uri_parameters(c); + + reqprep(&req, p, SIP_NOTIFY, 0, 1); + + switch(state) { + case AST_EXTENSION_DEACTIVATED: + if (timeout) + add_header(&req, "Subscription-State", "terminated;reason=timeout"); + else { + add_header(&req, "Subscription-State", "terminated;reason=probation"); + add_header(&req, "Retry-After", "60"); + } + break; + case AST_EXTENSION_REMOVED: + add_header(&req, "Subscription-State", "terminated;reason=noresource"); + break; + default: + if (p->expiry) + add_header(&req, "Subscription-State", "active"); + else /* Expired */ + add_header(&req, "Subscription-State", "terminated;reason=timeout"); + } + + switch (p->subscribed) { + case XPIDF_XML: + case CPIM_PIDF_XML: + add_header(&req, "Event", subscriptiontype->event); + state_notify_build_xml(state, full, p->exten, p->context, &tmp, p, p->subscribed, mfrom, mto); + add_header(&req, "Content-Type", subscriptiontype->mediatype); + p->dialogver++; + break; + case PIDF_XML: /* Eyebeam supports this format */ + add_header(&req, "Event", subscriptiontype->event); + state_notify_build_xml(state, full, p->exten, p->context, &tmp, p, p->subscribed, mfrom, mto); + add_header(&req, "Content-Type", subscriptiontype->mediatype); + p->dialogver++; + break; + case DIALOG_INFO_XML: /* SNOM subscribes in this format */ + add_header(&req, "Event", subscriptiontype->event); + state_notify_build_xml(state, full, p->exten, p->context, &tmp, p, p->subscribed, mfrom, mto); + add_header(&req, "Content-Type", subscriptiontype->mediatype); + p->dialogver++; + break; + case NONE: + default: + break; + } + add_header_contentLength(&req, tmp->used); add_line(&req, tmp->str); @@ -10644,7 +11140,7 @@ channame += 4; } - if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY))) { + if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY, NULL))) { astman_send_error(s, m, "Unable to build sip pvt data for notify (memory/socket error)"); return 0; } @@ -10662,7 +11158,7 @@ ast_set_flag(&p->flags[0], SIP_OUTGOING); /* Recalculate our side, and recalculate Call ID */ - ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip); + ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip, p); build_via(p); ao2_t_unlink(dialogs, p, "About to change the callid -- remove the old name"); build_callid_pvt(p); @@ -10679,15 +11175,85 @@ return 0; } -static char mandescr_sipnotify[] = -"Description: Sends a SIP Notify event\n" -"All parameters for this event must be specified in the body of this request\n" -"via multiple Variable: name=value sequences.\n" -"Variables: \n" -" *Channel: Peer to receive the notify. Required.\n" -" *Variable: = At least one variable pair must be specified.\n" -" ActionID: Action ID for this transaction. Will be returned.\n"; +/*! \brief Send a provisional response indicating that a call was redirected + */ +static void update_redirecting(struct sip_pvt *p, const void *data, size_t datalen) +{ + struct sip_request resp; + if (p->owner->_state == AST_STATE_UP || ast_test_flag(&p->flags[0], SIP_OUTGOING)) { + return; + } + + if (!ast_strlen_zero(p->owner->redirecting.to.number)) { + ast_string_field_set(p, exten, p->owner->redirecting.to.number); + build_contact(p); + } + respprep(&resp, p, "181 Call is being forwarded", &p->initreq); + add_diversion_header(&resp, p); + send_response(p, &resp, XMIT_UNRELIABLE, 0); +} + +/*! \brief Notify peer that the connected line has changed */ +static void update_connectedline(struct sip_pvt *p, const void *data, size_t datalen) +{ + + if (!ast_test_flag(&p->flags[0], SIP_SENDRPID)) + return; + if (ast_strlen_zero(p->owner->connected.id.number)) + return; + + append_history(p, "ConnectedLine", "%s party is now %s <%s>", ast_test_flag(&p->flags[0], SIP_OUTGOING) ? "Calling" : "Called", p->owner->connected.id.name, p->owner->connected.id.number); + + if (p->owner->_state == AST_STATE_UP || ast_test_flag(&p->flags[0], SIP_OUTGOING)) { + struct sip_request req; + + if (p->invitestate == INV_CONFIRMED || p->invitestate == INV_TERMINATED) { + reqprep(&req, p, ast_test_flag(&p->flags[0], SIP_REINVITE_UPDATE) ? SIP_UPDATE : SIP_INVITE, 0, 1); + + add_header(&req, "Allow", ALLOWED_METHODS); + add_header(&req, "Supported", SUPPORTED_EXTENSIONS); + add_rpid(&req, p); + add_sdp(&req, p, FALSE, TRUE, FALSE); + + initialize_initreq(p, &req); + p->lastinvite = p->ocseq; + ast_set_flag(&p->flags[0], SIP_OUTGOING); + p->invitestate = INV_CALLING; + send_request(p, &req, XMIT_CRITICAL, p->ocseq); + } else if (is_method_allowed(&p->allowed_methods, SIP_UPDATE)) { + reqprep(&req, p, SIP_UPDATE, 0, 1); + add_rpid(&req, p); + add_header(&req, "X-Asterisk-rpid-update", "Yes"); + add_header_contentLength(&req, 0); + send_request(p, &req, XMIT_CRITICAL, p->ocseq); + } else { + /* We cannot send the update yet, so we have to wait until we can */ + ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); + } + } else { + if (ast_test_flag(&p->flags[1], SIP_PAGE2_RPID_IMMEDIATE)) { + struct sip_request resp; + + if ((p->owner->_state == AST_STATE_RING) && !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT)) { + respprep(&resp, p, "180 Ringing", &p->initreq); + add_rpid(&resp, p); + send_response(p, &resp, XMIT_UNRELIABLE, 0); + ast_set_flag(&p->flags[0], SIP_RINGING); + } else if (p->owner->_state == AST_STATE_RINGING) { + respprep(&resp, p, "183 Session Progress", &p->initreq); + add_rpid(&resp, p); + send_response(p, &resp, XMIT_UNRELIABLE, 0); + ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); + } else { + ast_log(LOG_DEBUG, "Unable able to send update to '%s' in state '%s'\n", p->owner->name, ast_state2str(p->owner->_state)); + } + } else { + ast_set_flag(&p->flags[1], SIP_PAGE2_CONNECTLINEUPDATE_PEND); + } + } +} + static const struct _map_x_s regstatestrings[] = { { REG_STATE_FAILED, "Failed" }, { REG_STATE_UNREGISTERED, "Unregistered"}, @@ -10715,7 +11281,7 @@ static int sip_reregister(const void *data) { /* if we are here, we know that we need to reregister. */ - struct sip_registry *r= (struct sip_registry *) data; + struct sip_registry *r = (struct sip_registry *) data; /* if we couldn't get a reference to the registry object, punt */ if (!r) @@ -10730,7 +11296,7 @@ r->expire = -1; __sip_do_register(r); - registry_unref(r, "unreg the re-registered"); + registry_unref(r, "unref the re-register scheduled event"); return 0; } @@ -10849,7 +11415,7 @@ r->callid_valid = TRUE; } /* Allocate SIP dialog for registration */ - if (!(p = sip_alloc( r->callid, NULL, 0, SIP_REGISTER))) { + if (!(p = sip_alloc( r->callid, NULL, 0, SIP_REGISTER, NULL))) { ast_log(LOG_WARNING, "Unable to allocate registration transaction (memory or socket error)\n"); return 0; } @@ -10916,7 +11482,7 @@ ast_string_field_set(p, exten, r->callback); /* Set transport and port so the correct contact is built */ - p->socket.type = r->transport; + set_socket_transport(&p->socket, r->transport); if (r->transport == SIP_TRANSPORT_TLS || r->transport == SIP_TRANSPORT_TCP) { p->socket.port = sip_tcp_desc.local_address.sin_port; } @@ -10926,7 +11492,7 @@ based on whether the remote host is on the external or internal network so we can register through nat */ - ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip); + ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip, p); build_contact(p); } @@ -11248,19 +11814,20 @@ ast_update_realtime(tablename, "name", peer->name, "fullcontact", "", "ipaddr", "", "port", "", "regseconds", "0", peer->deprecated_username ? "username" : "defaultuser", "", "regserver", "", "useragent", "", "lastms", "", SENTINEL); } else { ast_db_del("SIP/Registry", peer->name); + ast_db_del("SIP/PeerMethods", peer->name); } } } -static void set_peer_transport(struct sip_peer *peer, int transport) +static void set_socket_transport(struct sip_socket *socket, int transport) { /* if the transport type changes, clear all socket data */ - if (peer->socket.type != transport) { - peer->socket.type = transport; - peer->socket.fd = -1; - if (peer->socket.tcptls_session) { - ao2_ref(peer->socket.tcptls_session, -1); - peer->socket.tcptls_session = NULL; + if (socket->type != transport) { + socket->fd = -1; + socket->type = transport; + if (socket->tcptls_session) { + ao2_ref(socket->tcptls_session, -1); + socket->tcptls_session = NULL; } } } @@ -11277,11 +11844,12 @@ memset(&peer->addr, 0, sizeof(peer->addr)); destroy_association(peer); /* remove registration data from storage */ - set_peer_transport(peer, peer->default_outbound_transport); + set_socket_transport(&peer->socket, peer->default_outbound_transport); manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name); register_peer_exten(peer, FALSE); /* Remove regexten */ ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name); + peer->allowed_methods = SIP_UNKNOWN; /* Do we need to release this peer from memory? Only for realtime peers and autocreated peers @@ -11324,11 +11892,13 @@ int expire; int port; char *scan, *addr, *port_str, *expiry_str, *username, *contact; + char allowed_methods_str[256] = ""; if (peer->rt_fromcontact) return; if (ast_db_get("SIP/Registry", peer->name, data, sizeof(data))) return; + ast_db_get("SIP/PeerMethods", peer->name, allowed_methods_str, sizeof(allowed_methods_str)); scan = data; addr = strsep(&scan, ":"); @@ -11355,6 +11925,10 @@ if (contact) ast_string_field_set(peer, fullcontact, contact); + if (!ast_strlen_zero(allowed_methods_str)) { + peer->allowed_methods = atoi(allowed_methods_str); + } + ast_debug(2, "SIP Seeding peer from astdb: '%s' at %s@%s:%d for %d\n", peer->name, peer->username, ast_inet_ntoa(in), port, expire); @@ -11524,7 +12098,7 @@ } else if (!strcasecmp(curi, "*") || !expire) { /* Unregister this peer */ /* This means remove all registrations and return OK */ memset(&peer->addr, 0, sizeof(peer->addr)); - set_peer_transport(peer, peer->default_outbound_transport); + set_socket_transport(&peer->socket, peer->default_outbound_transport); AST_SCHED_DEL_UNREF(sched, peer->expire, unref_peer(peer, "remove register expire ref")); @@ -11579,7 +12153,7 @@ * transport type, change it. If it got this far, it is a * supported type, but check just in case */ if ((peer->socket.type != transport_type) && (peer->transports & transport_type)) { - set_peer_transport(peer, transport_type); + set_socket_transport(&peer->socket, transport_type); } oldsin = peer->addr; @@ -11712,8 +12286,8 @@ int len; const char *rr, *contact, *c; - /* Once a persistant route is set, don't fool with it */ - if (p->route && p->route_persistant) { + /* Once a persistent route is set, don't fool with it */ + if (p->route && p->route_persistent) { ast_debug(1, "build_route: Retaining previous route: <%s>\n", p->route->hop); return; } @@ -11724,7 +12298,7 @@ } /* We only want to create the route set the first time this is called */ - p->route_persistant = 1; + p->route_persistent = 1; /* Build a tailq, then assign it to p->route when done. * If backwards, we add entries from the head so they end up @@ -11817,7 +12391,7 @@ */ static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request *req, const char *username, const char *secret, const char *md5secret, int sipmethod, - char *uri, enum xmittype reliable, int ignore) + const char *uri, enum xmittype reliable, int ignore) { const char *response; char *reqheader, *respheader; @@ -12182,15 +12756,16 @@ - Registration requests are only matched with peers that are marked as "dynamic" */ static enum check_auth_result register_verify(struct sip_pvt *p, struct sockaddr_in *sin, - struct sip_request *req, char *uri) + struct sip_request *req, const char *uri) { enum check_auth_result res = AUTH_NOT_FOUND; struct sip_peer *peer; char tmp[256]; char *name, *c; char *domain; + char *uri2 = ast_strdupa(uri); - terminate_uri(uri); /* warning, overwrite the string */ + terminate_uri(uri2); ast_copy_string(tmp, get_header(req, "To"), sizeof(tmp)); if (sip_cfg.pedanticsipchecking) @@ -12242,12 +12817,7 @@ } if (peer) { - /*! \todo OEJ Remove this - there's never RTP in a REGISTER dialog... */ - /* Set Frame packetization */ - if (p->rtp) { - ast_rtp_codec_setpref(p->rtp, &peer->prefs); - p->autoframing = peer->autoframing; - } + ao2_lock(peer); if (!peer->host_dynamic) { ast_log(LOG_ERROR, "Peer '%s' is trying to register, but not configured as host=dynamic\n", peer->name); res = AUTH_PEER_NOT_DYNAMIC; @@ -12255,7 +12825,7 @@ ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_NAT); if (ast_test_flag(&p->flags[1], SIP_PAGE2_REGISTERTRYING)) transmit_response(p, "100 Trying", req); - if (!(res = check_auth(p, req, peer->name, peer->secret, peer->md5secret, SIP_REGISTER, uri, XMIT_UNRELIABLE, req->ignore))) { + if (!(res = check_auth(p, req, peer->name, peer->secret, peer->md5secret, SIP_REGISTER, uri2, XMIT_UNRELIABLE, req->ignore))) { if (sip_cancel_destroy(p)) ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); @@ -12292,6 +12862,7 @@ } } + ao2_unlock(peer); } if (!peer && sip_cfg.autocreatepeer) { /* Create peer if we have autocreate mode enabled */ @@ -12301,7 +12872,7 @@ if (peer->addr.sin_addr.s_addr) { ao2_t_link(peers_by_ip, peer, "link peer into peers-by-ip table"); } - + ao2_lock(peer); if (sip_cancel_destroy(p)) ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); switch (parse_register_contact(p, peer, req)) { @@ -12324,6 +12895,7 @@ res = 0; break; } + ao2_unlock(peer); } } if (!peer && sip_cfg.alwaysauthreject) { @@ -12386,8 +12958,19 @@ break; } } - if (peer) + if (peer) { + ao2_lock(peer); + if (peer->allowed_methods == SIP_UNKNOWN) { + peer->allowed_methods = set_pvt_allowed_methods(p, req); + } + if (!peer->rt_fromcontact) { + char allowed_methods_str[256]; + snprintf(allowed_methods_str, sizeof(allowed_methods_str), "%u", peer->allowed_methods); + ast_db_put("SIP/PeerMethods", peer->name, allowed_methods_str); + } + ao2_unlock(peer); unref_peer(peer, "register_verify: unref_peer: tossing stack peer pointer at end of func"); + } return res; } @@ -12422,23 +13005,199 @@ } } +/*! \brief Parse the parts of the P-Asserted-Identity header + * on an incoming packet. Returns 1 if a valid header is found + * and it is different from the current caller id. + */ +static int get_pai(struct sip_pvt *p, struct sip_request *req) +{ + char pai[256]; + char privacy[64]; + char *cid_num = ""; + char *cid_name = ""; + int callingpres = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED; + char *start = NULL, *end = NULL; + + ast_copy_string(pai, get_header(req, "P-Asserted-Identity"), sizeof(pai)); + + if (ast_strlen_zero(pai)) { + return 0; + } + + start = pai; + if (*start == '"') { + *start++ = '\0'; + end = strchr(start, '"'); + if (!end) + return 0; + *end++ = '\0'; + cid_name = start; + start = ast_skip_blanks(end); + } + + if (*start != '<') + return 0; + *start++ = '\0'; + end = strchr(start, '@'); + if (!end) + return 0; + *end++ = '\0'; + if (!strncasecmp(start, "anonymous@anonymous.invalid", 27)) { + callingpres = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED; + /*XXX Assume no change in cid_num. Perhaps it should be + * blanked? + */ + cid_num = (char *)p->cid_num; + } else if (!strncasecmp(start, "sip:", 4)) { + cid_num = start + 4; + if (ast_is_shrinkable_phonenumber(cid_num)) + ast_shrink_phone_number(cid_num); + start = end; + + end = strchr(start, '>'); + if (!end) + return 0; + *end = '\0'; + } else { + return 0; + } + + ast_copy_string(privacy, get_header(req, "Privacy"), sizeof(privacy)); + if (!ast_strlen_zero(privacy) && strncmp(privacy, "id", 2)) { + callingpres = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED; + } + + /* Only return true if the supplied caller id is different */ + if (!strcasecmp(p->cid_num, cid_num) && !strcasecmp(p->cid_name, cid_name) && p->callingpres == callingpres) + return 0; + + ast_string_field_set(p, cid_num, cid_num); + ast_string_field_set(p, cid_name, cid_name); + p->callingpres = callingpres; + + if (p->owner) { + ast_set_callerid(p->owner, cid_num, cid_name, NULL); + p->owner->cid.cid_pres = callingpres; + } + + return 1; +} + +/*! \brief Get name, number and presentation from remote party id header, + * returns true if a valid header was found and it was different from the + * current caller id. + */ +static int get_rpid(struct sip_pvt *p, struct sip_request *oreq) +{ + char tmp[256]; + struct sip_request *req; + char *cid_num = ""; + char *cid_name = ""; + int callingpres = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED; + char *privacy = ""; + char *screen = ""; + char *start, *end; + + if (!ast_test_flag(&p->flags[0], SIP_TRUSTRPID)) + return 0; + req = oreq; + if (!req) + req = &p->initreq; + ast_copy_string(tmp, get_header(req, "Remote-Party-ID"), sizeof(tmp)); + if (ast_strlen_zero(tmp)) { + return get_pai(p, req); + } + + start = tmp; + if (*start == '"') { + *start++ = '\0'; + end = strchr(start, '"'); + if (!end) + return 0; + *end++ = '\0'; + cid_name = start; + start = ast_skip_blanks(end); + } + + if (*start != '<') + return 0; + *start++ = '\0'; + end = strchr(start, '@'); + if (!end) + return 0; + *end++ = '\0'; + if (strncasecmp(start, "sip:", 4)) + return 0; + cid_num = start + 4; + if (ast_is_shrinkable_phonenumber(cid_num)) + ast_shrink_phone_number(cid_num); + start = end; + + end = strchr(start, '>'); + if (!end) + return 0; + *end++ = '\0'; + if (*end) { + start = end; + if (*start != ';') + return 0; + *start++ = '\0'; + while (!ast_strlen_zero(start)) { + end = strchr(start, ';'); + if (end) + *end++ = '\0'; + if (!strncasecmp(start, "privacy=", 8)) + privacy = start + 8; + else if (!strncasecmp(start, "screen=", 7)) + screen = start + 7; + start = end; + } + + if (!strcasecmp(privacy, "full")) { + if (!strcasecmp(screen, "yes")) + callingpres = AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN; + else if (!strcasecmp(screen, "no")) + callingpres = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED; + } else { + if (!strcasecmp(screen, "yes")) + callingpres = AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN; + else if (!strcasecmp(screen, "no")) + callingpres = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED; + } + } + + /* Only return true if the supplied caller id is different */ + if (!strcasecmp(p->cid_num, cid_num) && !strcasecmp(p->cid_name, cid_name) && p->callingpres == callingpres) + return 0; + + ast_string_field_set(p, cid_num, cid_num); + ast_string_field_set(p, cid_name, cid_name); + p->callingpres = callingpres; + + if (p->owner) { + ast_set_callerid(p->owner, cid_num, cid_name, NULL); + p->owner->cid.cid_pres = callingpres; + } + + return 1; +} + /*! \brief Get referring dnis */ -static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq) +static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, char **number, int *reason) { - char tmp[256], *exten, *rexten, *rdomain; - char *params, *reason = NULL; + char tmp[256], *exten, *rexten, *rdomain, *rname = NULL; + char *params, *reason_param = NULL; struct sip_request *req; - + req = oreq ? oreq : &p->initreq; ast_copy_string(tmp, get_header(req, "Diversion"), sizeof(tmp)); if (ast_strlen_zero(tmp)) - return 0; + return -1; - /*! \todo This function does not take user-parameters into consideration. - First look for @, then start looking for ; to find uri-parameters. - */ - params = strchr(tmp, ';'); + if ((params = strchr(tmp, '>'))) { + params = strchr(params, ';'); + } exten = get_in_brackets(tmp); if (!strncasecmp(exten, "sip:", 4)) { @@ -12457,16 +13216,16 @@ while (*params == ';' || *params == ' ') params++; /* Check if we have a reason parameter */ - if ((reason = strcasestr(params, "reason="))) { - reason+=7; + if ((reason_param = strcasestr(params, "reason="))) { + reason_param+=7; /* Remove enclosing double-quotes */ - if (*reason == '"') - ast_strip_quoted(reason, "\"", "\""); - if (!ast_strlen_zero(reason)) { - sip_set_redirstr(p, reason); + if (*reason_param == '"') + ast_strip_quoted(reason_param, "\"", "\""); + if (!ast_strlen_zero(reason_param)) { + sip_set_redirstr(p, reason_param); if (p->owner) { pbx_builtin_setvar_helper(p->owner, "__PRIREDIRECTREASON", p->redircause); - pbx_builtin_setvar_helper(p->owner, "__SIPREDIRECTREASON", reason); + pbx_builtin_setvar_helper(p->owner, "__SIPREDIRECTREASON", reason_param); } } } @@ -12474,14 +13233,33 @@ rdomain = exten; rexten = strsep(&rdomain, "@"); /* trim anything after @ */ - if (p->owner) + if (p->owner) pbx_builtin_setvar_helper(p->owner, "__SIPRDNISDOMAIN", rdomain); if (sip_debug_test_pvt(p)) - ast_verbose("RDNIS for this call is is %s (reason %s)\n", exten, reason ? reason : ""); + ast_verbose("RDNIS for this call is %s (reason %s)\n", exten, reason ? reason_param : ""); - ast_string_field_set(p, rdnis, rexten); + /*ast_string_field_set(p, rdnis, rexten);*/ + if (*tmp == '\"') { + char *end_quote; + rname = tmp + 1; + end_quote = strchr(rname, '\"'); + *end_quote = '\0'; + } + + if (number) { + *number = ast_strdup(rexten); + } + + if (name && rname) { + *name = ast_strdup(rname); + } + + if (reason && !ast_strlen_zero(reason_param)) { + *reason = sip_reason_str_to_code(reason_param); + } + return 0; } @@ -13093,58 +13871,12 @@ return output; } -/*! \brief Get caller id number from Remote-Party-ID header field - * Returns true if number should be restricted (privacy setting found) - * output is set to NULL if no number found - */ -static int get_rpid_num(const char *input, char *output, int maxlen) -{ - char *start; - char *end; - start = strchr(input, ':'); - if (!start) { - output[0] = '\0'; - return 0; - } - start++; - - /* we found "number" */ - ast_copy_string(output, start, maxlen); - output[maxlen-1] = '\0'; - - end = strchr(output, '@'); - if (end) - *end = '\0'; - else - output[0] = '\0'; - if (strstr(input, "privacy=full") || strstr(input, "privacy=uri")) - return AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED; - - return 0; -} - - -/*! \brief helper function for check_{user|peer}_ok() */ -static void replace_cid(struct sip_pvt *p, const char *rpid_num, const char *calleridname) -{ - /* replace callerid if rpid found, and not restricted */ - if (!ast_strlen_zero(rpid_num) && ast_test_flag(&p->flags[0], SIP_TRUSTRPID)) { - char *tmp = ast_strdupa(rpid_num); /* XXX the copy can be done later */ - if (!ast_strlen_zero(calleridname)) - ast_string_field_set(p, cid_name, calleridname); - if (ast_is_shrinkable_phonenumber(tmp)) - ast_shrink_phone_number(tmp); - ast_string_field_set(p, cid_num, tmp); - } -} - /*! \brief Validate device authentication */ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of, struct sip_request *req, int sipmethod, struct sockaddr_in *sin, struct sip_peer **authpeer, - enum xmittype reliable, - char *rpid_num, char *calleridname, char *uri2) + enum xmittype reliable, char *calleridname, char *uri2) { enum check_auth_result res; int debug=sip_debug_test_addr(sin); @@ -13195,7 +13927,7 @@ /* XXX what about p->prefs = peer->prefs; ? */ /* Set Frame packetization */ if (p->rtp) { - ast_rtp_codec_setpref(p->rtp, &peer->prefs); + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, &peer->prefs); p->autoframing = peer->autoframing; } @@ -13208,7 +13940,6 @@ if (p->sipoptions) peer->sipoptions = p->sipoptions; - replace_cid(p, rpid_num, calleridname); do_setnat(p, ast_test_flag(&p->flags[0], SIP_NAT_ROUTE)); ast_string_field_set(p, peersecret, peer->secret); @@ -13217,6 +13948,12 @@ ast_string_field_set(p, mohinterpret, peer->mohinterpret); ast_string_field_set(p, mohsuggest, peer->mohsuggest); ast_string_field_set(p, parkinglot, peer->parkinglot); + ast_string_field_set(p, engine, peer->engine); + if (peer->allowed_methods == SIP_UNKNOWN) { + set_pvt_allowed_methods(p, req); + } else { + p->allowed_methods = peer->allowed_methods; + } if (peer->callingpres) /* Peer calling pres setting will override RPID */ p->callingpres = peer->callingpres; if (peer->maxms && peer->lastms) @@ -13260,14 +13997,18 @@ /* XXX this takes the name from the caller... can we override ? */ ast_string_field_set(p, authname, peer->username); } - if (!ast_strlen_zero(peer->cid_num)) { - char *tmp = ast_strdupa(peer->cid_num); - if (ast_is_shrinkable_phonenumber(tmp)) - ast_shrink_phone_number(tmp); - ast_string_field_set(p, cid_num, tmp); + if (!get_rpid(p, req)) { + if (!ast_strlen_zero(peer->cid_num)) { + char *tmp = ast_strdupa(peer->cid_num); + if (ast_is_shrinkable_phonenumber(tmp)) + ast_shrink_phone_number(tmp); + ast_string_field_set(p, cid_num, tmp); + } + if (!ast_strlen_zero(peer->cid_name)) + ast_string_field_set(p, cid_name, peer->cid_name); + if (peer->callingpres) + p->callingpres = peer->callingpres; } - if (!ast_strlen_zero(peer->cid_name)) - ast_string_field_set(p, cid_name, peer->cid_name); ast_string_field_set(p, fullcontact, peer->fullcontact); if (!ast_strlen_zero(peer->context)) ast_string_field_set(p, context, peer->context); @@ -13284,17 +14025,6 @@ if (p->peercapability) p->jointcapability &= p->peercapability; p->maxcallbitrate = peer->maxcallbitrate; - if (!ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT_ALWAYS) && - (!ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) || - !(p->capability & AST_FORMAT_VIDEO_MASK)) && - p->vrtp) { - ast_rtp_destroy(p->vrtp); - p->vrtp = NULL; - } - if ((!ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT) || !(p->capability & AST_FORMAT_TEXT_MASK)) && p->trtp) { - ast_rtp_destroy(p->trtp); - p->trtp = NULL; - } if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) || (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) p->noncodeccapability |= AST_RTP_DTMF; @@ -13303,6 +14033,14 @@ p->jointnoncodeccapability = p->noncodeccapability; if (p->t38.peercapability) p->t38.jointcapability &= p->t38.peercapability; + if (!dialog_initialize_rtp(p)) { + if (p->rtp) { + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, &peer->prefs); + p->autoframing = peer->autoframing; + } + } else { + res = AUTH_RTP_FAILED; + } } unref_peer(peer, "check_peer_ok: unref_peer: tossing temp ptr to peer from find_peer"); return res; @@ -13315,15 +14053,13 @@ \return 0 on success, non-zero on failure */ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_request *req, - int sipmethod, char *uri, enum xmittype reliable, + int sipmethod, const char *uri, enum xmittype reliable, struct sockaddr_in *sin, struct sip_peer **authpeer) { char from[256]; char *dummy; /* dummy return value for parse_uri */ char *domain; /* dummy return value for parse_uri */ char *of, *of2; - char rpid_num[50]; - const char *rpid; enum check_auth_result res; char calleridname[50]; char *uri2 = ast_strdupa(uri); @@ -13339,11 +14075,6 @@ if (calleridname[0]) ast_string_field_set(p, cid_name, calleridname); - rpid = get_header(req, "Remote-Party-ID"); - memset(rpid_num, 0, sizeof(rpid_num)); - if (!ast_strlen_zero(rpid)) - p->callingpres = get_rpid_num(rpid, rpid_num, sizeof(rpid_num)); - of = get_in_brackets(from); if (ast_strlen_zero(p->exten)) { char *t = uri2; @@ -13417,14 +14148,18 @@ } res = check_peer_ok(p, of, req, sipmethod, sin, - authpeer, reliable, rpid_num, calleridname, uri2); + authpeer, reliable, calleridname, uri2); if (res != AUTH_DONT_KNOW) return res; /* Finally, apply the guest policy */ if (sip_cfg.allowguest) { - replace_cid(p, rpid_num, calleridname); - res = AUTH_SUCCESSFUL; + get_rpid(p, req); + if (!dialog_initialize_rtp(p)) { + res = AUTH_SUCCESSFUL; + } else { + res = AUTH_RTP_FAILED; + } } else if (sip_cfg.alwaysauthreject) res = AUTH_FAKE_AUTH; /* reject with fake authorization request */ else @@ -13441,7 +14176,7 @@ /*! \brief Find user If we get a match, this will add a reference pointer to the user object in ASTOBJ, that needs to be unreferenced */ -static int check_user(struct sip_pvt *p, struct sip_request *req, int sipmethod, char *uri, enum xmittype reliable, struct sockaddr_in *sin) +static int check_user(struct sip_pvt *p, struct sip_request *req, int sipmethod, const char *uri, enum xmittype reliable, struct sockaddr_in *sin) { return check_user_full(p, req, sipmethod, uri, reliable, sin, NULL); } @@ -13458,7 +14193,7 @@ if (y < 0) y = 0; for (x = 0; x < req->lines; x++) { - char *line = REQ_OFFSET_TO_STR(req, line[x]); + const char *line = REQ_OFFSET_TO_STR(req, line[x]); strncat(buf, line, y); /* safe */ y -= strlen(line) + 1; if (y < 0) @@ -13577,7 +14312,7 @@ return "strict"; } -static struct _map_x_s natmodes[] = { +static const struct _map_x_s natmodes[] = { { SIP_NAT_NEVER, "No"}, { SIP_NAT_ROUTE, "Route"}, { SIP_NAT_ALWAYS, "Always"}, @@ -13596,7 +14331,7 @@ delete it. Keeping it enabled generates compiler warnings. */ -static struct _map_x_s natcfgmodes[] = { +static const struct _map_x_s natcfgmodes[] = { { SIP_NAT_NEVER, "never"}, { SIP_NAT_ROUTE, "route"}, { SIP_NAT_ALWAYS, "yes"}, @@ -13617,7 +14352,7 @@ /* Session-Timer Modes */ -static struct _map_x_s stmodes[] = { +static const struct _map_x_s stmodes[] = { { SESSION_TIMER_MODE_ACCEPT, "Accept"}, { SESSION_TIMER_MODE_ORIGINATE, "Originate"}, { SESSION_TIMER_MODE_REFUSE, "Refuse"}, @@ -13635,7 +14370,7 @@ } /* Session-Timer Refreshers */ -static struct _map_x_s strefreshers[] = { +static const struct _map_x_s strefreshers[] = { { SESSION_TIMER_REFRESHER_AUTO, "auto"}, { SESSION_TIMER_REFRESHER_UAC, "uac"}, { SESSION_TIMER_REFRESHER_UAS, "uas"}, @@ -13793,14 +14528,6 @@ #undef FORMAT } -/*! \brief Manager Action SIPShowRegistry description */ -static char mandescr_show_registry[] = -"Description: Lists all registration requests and status\n" -"Registrations will follow as separate events. followed by a final event called\n" -"RegistrationsComplete.\n" -"Variables: \n" -" ActionID: Action ID for this transaction. Will be returned.\n"; - /*! \brief Show SIP registrations in the manager API */ static int manager_show_registry(struct mansession *s, const struct message *m) { @@ -13839,13 +14566,6 @@ return 0; } -static char mandescr_show_peers[] = -"Description: Lists SIP peers in text format with details on current status.\n" -"Peerlist will follow as separate events, followed by a final event called\n" -"PeerlistComplete.\n" -"Variables: \n" -" ActionID: Action ID for this transaction. Will be returned.\n"; - /*! \brief Show SIP peers in the manager API */ /* Inspired from chan_iax2 */ static int manager_sip_show_peers(struct mansession *s, const struct message *m) @@ -14075,10 +14795,10 @@ { struct sip_peer *peer = userobj; int refc = ao2_t_ref(userobj, 0, ""); - int *fd = arg; + struct ast_cli_args *a = (struct ast_cli_args *) arg; - ast_cli(*fd, "name: %s\ntype: peer\nobjflags: %d\nrefcount: %d\n\n", - peer->name, 0, refc); + ast_cli(a->fd, "name: %s\ntype: peer\nobjflags: %d\nrefcount: %d\n\n", + peer->name, 0, refc); return 0; } @@ -14086,10 +14806,10 @@ { struct sip_pvt *pvt = userobj; int refc = ao2_t_ref(userobj, 0, ""); - int *fd = arg; + struct ast_cli_args *a = (struct ast_cli_args *) arg; - ast_cli(*fd, "name: %s\ntype: dialog\nobjflags: %d\nrefcount: %d\n\n", - pvt->callid, 0, refc); + ast_cli(a->fd, "name: %s\ntype: dialog\nobjflags: %d\nrefcount: %d\n\n", + pvt->callid, 0, refc); return 0; } @@ -14113,22 +14833,22 @@ if (a->argc != 3) return CLI_SHOWUSAGE; ast_cli(a->fd, "-= Peer objects: %d static, %d realtime, %d autocreate =-\n\n", speerobjs, rpeerobjs, apeerobjs); - ao2_t_callback(peers, OBJ_NODATA, peer_dump_func, &a->fd, "initiate ao2_callback to dump peers"); + ao2_t_callback(peers, OBJ_NODATA, peer_dump_func, a, "initiate ao2_callback to dump peers"); ast_cli(a->fd, "-= Registry objects: %d =-\n\n", regobjs); ASTOBJ_CONTAINER_DUMP(a->fd, tmp, sizeof(tmp), ®l); ast_cli(a->fd, "-= Dialog objects:\n\n"); - ao2_t_callback(dialogs, OBJ_NODATA, dialog_dump_func, &a->fd, "initiate ao2_callback to dump dialogs"); + ao2_t_callback(dialogs, OBJ_NODATA, dialog_dump_func, a, "initiate ao2_callback to dump dialogs"); return CLI_SUCCESS; } /*! \brief Print call group and pickup group */ -static void print_group(int fd, ast_group_t group, int crlf) +static void print_group(int fd, ast_group_t group, int crlf) { char buf[256]; ast_cli(fd, crlf ? "%s\r\n" : "%s\n", ast_print_group(buf, sizeof(buf), group) ); } /*! \brief mapping between dtmf flags and strings */ -static struct _map_x_s dtmfstr[] = { +static const struct _map_x_s dtmfstr[] = { { SIP_DTMF_RFC2833, "rfc2833" }, { SIP_DTMF_INFO, "info" }, { SIP_DTMF_SHORTINFO, "shortinfo" }, @@ -14149,7 +14869,7 @@ return map_s_x(dtmfstr, str, -1); } -static struct _map_x_s insecurestr[] = { +static const struct _map_x_s insecurestr[] = { { SIP_INSECURE_PORT, "port" }, { SIP_INSECURE_INVITE, "invite" }, { SIP_INSECURE_PORT | SIP_INSECURE_INVITE, "port,invite" }, @@ -14211,7 +14931,20 @@ * that we can wait for the next time around. */ return 0; } - + + /* We absolutely cannot destroy the rtp struct while a bridge is active or we WILL crash */ + if (dialog->rtp && ast_rtp_instance_get_bridged(dialog->rtp)) { + ast_debug(2, "Bridge still active. Delaying destroy of SIP dialog '%s' Method: %s\n", dialog->callid, sip_methods[dialog->method].text); + sip_pvt_unlock(dialog); + return 0; + } + + if (dialog->vrtp && ast_rtp_instance_get_bridged(dialog->vrtp)) { + ast_debug(2, "Bridge still active. Delaying destroy of SIP dialog '%s' Method: %s\n", dialog->callid, sip_methods[dialog->method].text); + sip_pvt_unlock(dialog); + return 0; + } + /* Check RTP timeouts and kill calls if we have a timeout set and do not get RTP */ check_rtp_timeout(dialog, *t); @@ -14220,13 +14953,13 @@ - if that's the case, wait with destruction */ if (dialog->needdestroy && !dialog->packets && !dialog->owner) { /* We absolutely cannot destroy the rtp struct while a bridge is active or we WILL crash */ - if (dialog->rtp && ast_rtp_get_bridged(dialog->rtp)) { + if (dialog->rtp && ast_rtp_instance_get_bridged(dialog->rtp)) { ast_debug(2, "Bridge still active. Delaying destruction of SIP dialog '%s' Method: %s\n", dialog->callid, sip_methods[dialog->method].text); sip_pvt_unlock(dialog); return 0; } - if (dialog->vrtp && ast_rtp_get_bridged(dialog->vrtp)) { + if (dialog->vrtp && ast_rtp_instance_get_bridged(dialog->vrtp)) { ast_debug(2, "Bridge still active. Delaying destroy of SIP dialog '%s' Method: %s\n", dialog->callid, sip_methods[dialog->method].text); sip_pvt_unlock(dialog); return 0; @@ -14259,10 +14992,10 @@ struct sip_peer *peer, *pi; int prunepeer = FALSE; int multi = FALSE; - char *name = NULL; + const char *name = NULL; regex_t regexbuf; struct ao2_iterator i; - static char *choices[] = { "all", "like", NULL }; + static const char * const choices[] = { "all", "like", NULL }; char *cmplt; if (cmd == CLI_INIT) { @@ -14451,12 +15184,6 @@ } #undef FORMAT -static char mandescr_show_peer[] = -"Description: Show one SIP peer with details on current status.\n" -"Variables: \n" -" Peer: The peer name you want to check.\n" -" ActionID: Optional action ID for this AMI transaction.\n"; - /*! \brief Show SIP peers in the manager API */ static int manager_sip_show_peer(struct mansession *s, const struct message *m) { @@ -14716,6 +15443,7 @@ ast_cli(fd, " Sess-Refresh : %s\n", strefresher2str(peer->stimer.st_ref)); ast_cli(fd, " Sess-Expires : %d secs\n", peer->stimer.st_max_se); ast_cli(fd, " Min-Sess : %d secs\n", peer->stimer.st_min_se); + ast_cli(fd, " RTP Engine : %s\n", peer->engine); ast_cli(fd, "\n"); peer = unref_peer(peer, "sip_show_peer: unref_peer: done with peer ptr"); } else if (peer && type == 1) { /* manager listing */ @@ -14763,6 +15491,7 @@ astman_append(s, "SIP-Sess-Refresh: %s\r\n", strefresher2str(peer->stimer.st_ref)); astman_append(s, "SIP-Sess-Expires: %d\r\n", peer->stimer.st_max_se); astman_append(s, "SIP-Sess-Min: %d\r\n", peer->stimer.st_min_se); + astman_append(s, "SIP-RTP-Engine: %s\r\n", peer->engine); /* - is enumerated */ astman_append(s, "SIP-DTMFmode: %s\r\n", dtmfmode2str(ast_test_flag(&peer->flags[0], SIP_DTMF))); @@ -14895,6 +15624,7 @@ ast_cli(a->fd, " Sess-Refresh : %s\n", strefresher2str(user->stimer.st_ref)); ast_cli(a->fd, " Sess-Expires : %d secs\n", user->stimer.st_max_se); ast_cli(a->fd, " Sess-Min-SE : %d secs\n", user->stimer.st_min_se); + ast_cli(a->fd, " RTP Engine : %s\n", user->engine); ast_cli(a->fd, " Codec Order : ("); print_codec_to_cli(a->fd, &user->prefs); @@ -15051,11 +15781,10 @@ #define FORMAT2 "%-15.15s %-11.11s %-8.8s %-10.10s %-10.10s (%-2.2s) %-6.6s %-10.10s %-10.10s ( %%) %-6.6s\n" #define FORMAT "%-15.15s %-11.11s %-8.8s %-10.10u%-1.1s %-10.10u (%-2.2u%%) %-6.6u %-10.10u%-1.1s %-10.10u (%-2.2u%%) %-6.6u\n" struct sip_pvt *cur = __cur; - unsigned int rxcount; - unsigned int txcount; + struct ast_rtp_instance_stats stats; char durbuf[10]; - int duration; - int durh, durm, durs; + int duration; + int durh, durm, durs; struct ast_channel *c = cur->owner; struct __show_chan_arg *arg = __arg; int fd = arg->fd; @@ -15069,10 +15798,9 @@ ast_cli(fd, "%-15.15s %-11.11s (inv state: %s) -- %s\n", ast_inet_ntoa(cur->sa.sin_addr), cur->callid, invitestate2string[cur->invitestate].desc, "-- No RTP active"); return 0; /* don't care, we scan all channels */ } - rxcount = ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXCOUNT); - txcount = ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXCOUNT); - /* Find the duration of this channel */ + ast_rtp_instance_get_stats(cur->rtp, &stats, AST_RTP_INSTANCE_STAT_ALL); + if (c && c->cdr && !ast_tvzero(c->cdr->start)) { duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000); durh = duration / 3600; @@ -15082,21 +15810,21 @@ } else { durbuf[0] = '\0'; } - /* Print stats for every call with RTP */ + ast_cli(fd, FORMAT, ast_inet_ntoa(cur->sa.sin_addr), cur->callid, durbuf, - rxcount > (unsigned int) 100000 ? (unsigned int) (rxcount)/(unsigned int) 1000 : rxcount, - rxcount > (unsigned int) 100000 ? "K":" ", - ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXPLOSS), - rxcount > ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXPLOSS) ? (unsigned int) (ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXPLOSS) / rxcount * 100) : 0, - ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXJITTER), - txcount > (unsigned int) 100000 ? (unsigned int) (txcount)/(unsigned int) 1000 : txcount, - txcount > (unsigned int) 100000 ? "K":" ", - ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXPLOSS), - txcount > ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXPLOSS) ? (unsigned int) (ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXPLOSS)/ txcount * 100) : 0, - ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXJITTER) + stats.rxcount > (unsigned int) 100000 ? (unsigned int) (stats.rxcount)/(unsigned int) 1000 : stats.rxcount, + stats.rxcount > (unsigned int) 100000 ? "K":" ", + stats.rxploss, + stats.rxcount > stats.rxploss ? (stats.rxploss / stats.rxcount * 100) : 0, + stats.rxjitter, + stats.txcount > (unsigned int) 100000 ? (unsigned int) (stats.txcount)/(unsigned int) 1000 : stats.txcount, + stats.txcount > (unsigned int) 100000 ? "K":" ", + stats.txploss, + stats.txcount > stats.txploss ? (stats.txploss / stats.txcount * 100) : 0, + stats.txjitter ); arg->numchans++; @@ -16009,14 +16737,14 @@ } /*! \brief Enable SIP Debugging for a single IP */ -static char *sip_do_debug_ip(int fd, char *arg) +static char *sip_do_debug_ip(int fd, const char *arg) { struct hostent *hp; struct ast_hostent ahp; int port = 0; char *p; - p = arg; + p = ast_strdupa(arg); strsep(&p, ":"); if (p) port = atoi(p); @@ -16038,7 +16766,7 @@ } /*! \brief Turn on SIP debugging for a given peer */ -static char *sip_do_debug_peer(int fd, char *arg) +static char *sip_do_debug_peer(int fd, const char *arg) { struct sip_peer *peer = find_peer(arg, NULL, TRUE, FINDPEERS, FALSE); if (!peer) @@ -16062,7 +16790,7 @@ static char *sip_do_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { int oldsipdebug = sipdebug & sip_debug_console; - char *what; + const char *what; if (cmd == CLI_INIT) { e->command = "sip set debug {on|off|ip|peer}"; @@ -16137,7 +16865,7 @@ for (i = 3; i < a->argc; i++) { struct sip_pvt *p; - if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY))) { + if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY, NULL))) { ast_log(LOG_WARNING, "Unable to build sip pvt data for notify (memory/socket error)\n"); return CLI_FAILURE; } @@ -16155,7 +16883,7 @@ ast_set_flag(&p->flags[0], SIP_OUTGOING); /* Recalculate our side, and recalculate Call ID */ - ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip); + ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip, p); build_via(p); ao2_t_unlink(dialogs, p, "About to change the callid -- remove the old name"); build_callid_pvt(p); @@ -16639,29 +17367,150 @@ .read = function_sipchaninfo_read, }; +static int read_to_parts(struct sip_pvt *p, struct sip_request *req, char **name, char **number) +{ + + char to_header[256]; + char *to_name = NULL; + char *to_number = NULL; + char *separator; + + ast_copy_string(to_header, get_header(req, "To"), sizeof(to_header)); + + /* Let's get that number first! */ + to_number = get_in_brackets(to_header); + + if (!strncasecmp(to_number, "sip:", 4)) { + to_number += 4; + } else if (!strncasecmp(to_number, "sips:", 5)) { + to_number += 5; + } else { + ast_log(LOG_WARNING, "Not a SIP URI? (%s)!\n", to_number); + return -1; + } + + /* Remove the host and such since we just want the number */ + if ((separator = strchr(to_number, '@'))) { + *separator = '\0'; + } + + /* We have the number. Let's get the name now. */ + + if (*to_header == '\"') { + to_name = to_header + 1; + if (!(separator = (char *)find_closing_quote(to_name, NULL))) { + ast_log(LOG_NOTICE, "No closing quote in name section of To: header (%s)\n", to_header); + return -1; + } + *separator = '\0'; + } + + if (number) { + *number = ast_strdup(to_number); + } + if (name && !ast_strlen_zero(to_name)) { + *name = ast_strdup(to_name); + } + + return 0; +} + +/*! \brief update redirecting information for a channel based on headers + * + */ +static void change_redirecting_information(struct sip_pvt *p, struct sip_request *req, struct ast_party_redirecting *redirecting, int set_call_forward) +{ + char *redirecting_from_name = NULL; + char *redirecting_from_number = NULL; + char *redirecting_to_name = NULL; + char *redirecting_to_number = NULL; + int reason = AST_REDIRECTING_REASON_UNCONDITIONAL; + int is_response = req->method == SIP_RESPONSE; + int res = 0; + + res = get_rdnis(p, req, &redirecting_from_name, &redirecting_from_number, &reason); + if (res == -1) { + if (is_response) { + read_to_parts(p, req, &redirecting_from_name, &redirecting_from_number); + } else { + return; + } + } + + /* At this point, all redirecting "from" info should be filled in appropriately + * on to the "to" info + */ + + if (is_response) { + parse_moved_contact(p, req, &redirecting_to_name, &redirecting_to_number, set_call_forward); + } else { + read_to_parts(p, req, &redirecting_to_name, &redirecting_to_number); + } + + if (!ast_strlen_zero(redirecting_from_number)) { + if (redirecting->from.number) { + ast_free(redirecting->from.number); + } + ast_debug(3, "Got redirecting from number %s\n", redirecting_from_number); + redirecting->from.number = redirecting_from_number; + } + if (!ast_strlen_zero(redirecting_from_name)) { + if (redirecting->from.name) { + ast_free(redirecting->from.name); + } + ast_debug(3, "Got redirecting from name %s\n", redirecting_from_name); + redirecting->from.name = redirecting_from_name; + } + if (!ast_strlen_zero(redirecting_to_number)) { + if (redirecting->to.number) { + ast_free(redirecting->to.number); + } + ast_debug(3, "Got redirecting to number %s\n", redirecting_to_number); + redirecting->to.number = redirecting_to_number; + } + if (!ast_strlen_zero(redirecting_to_name)) { + if (redirecting->to.name) { + ast_free(redirecting->to.name); + } + ast_debug(3, "Got redirecting to name %s\n", redirecting_from_number); + redirecting->to.name = redirecting_to_name; + } + redirecting->reason = reason; +} + /*! \brief Parse 302 Moved temporalily response \todo XXX Doesn't redirect over TLS on sips: uri's. If we get a redirect to a SIPS: uri, this needs to be going back to the dialplan (this is a request for a secure signalling path). Note that transport=tls is deprecated, but we need to support it on incoming requests. */ -static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req) +static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req, char **name, char **number, int set_call_forward) { - char tmp[SIPBUFSIZE]; - char *s, *e, *t, *trans; + char contact[SIPBUFSIZE]; + char *contact_name = NULL; + char *contact_number = NULL; + char *separator, *trans; char *domain; enum sip_transport transport = SIP_TRANSPORT_UDP; - ast_copy_string(tmp, get_header(req, "Contact"), sizeof(tmp)); - if ((t = strchr(tmp, ','))) - *t = '\0'; + ast_copy_string(contact, get_header(req, "Contact"), sizeof(contact)); + if ((separator = strchr(contact, ','))) + *separator = '\0'; - s = get_in_brackets(tmp); - if ((trans = strcasestr(s, ";transport="))) do { + /* ooh, a name */ + if (*contact == '"') { + contact_name = contact + 1; + if ((separator = strchr(contact_name, '"'))) { + *separator++ = '\0'; + } + } + + contact_number = get_in_brackets(contact); + if ((trans = strcasestr(contact_number, ";transport="))) { trans += 11; - if ((e = strchr(trans, ';'))) - *e = '\0'; + if ((separator = strchr(trans, ';'))) + *separator = '\0'; if (!strncasecmp(trans, "tcp", 3)) transport = SIP_TRANSPORT_TCP; @@ -16669,66 +17518,84 @@ transport = SIP_TRANSPORT_TLS; else { if (strncasecmp(trans, "udp", 3)) - ast_debug(1, "received contact with an invalid transport, '%s'\n", s); + ast_debug(1, "received contact with an invalid transport, '%s'\n", contact_number); /* This will assume UDP for all unknown transports */ transport = SIP_TRANSPORT_UDP; } - } while(0); - s = remove_uri_parameters(s); + } + contact_number = remove_uri_parameters(contact_number); if (p->socket.tcptls_session) { ao2_ref(p->socket.tcptls_session, -1); p->socket.tcptls_session = NULL; } - p->socket.fd = -1; - p->socket.type = transport; + set_socket_transport(&p->socket, transport); - if (ast_test_flag(&p->flags[0], SIP_PROMISCREDIR)) { + if (set_call_forward && ast_test_flag(&p->flags[0], SIP_PROMISCREDIR)) { char *host = NULL; - if (!strncasecmp(s, "sip:", 4)) - s += 4; - else if (!strncasecmp(s, "sips:", 5)) - s += 5; - e = strchr(s, '/'); - if (e) - *e = '\0'; - if ((host = strchr(s, '@'))) { + if (!strncasecmp(contact_number, "sip:", 4)) + contact_number += 4; + else if (!strncasecmp(contact_number, "sips:", 5)) + contact_number += 5; + separator = strchr(contact_number, '/'); + if (separator) + *separator = '\0'; + if ((host = strchr(contact_number, '@'))) { *host++ = '\0'; - ast_debug(2, "Found promiscuous redirection to 'SIP/%s::::%s@%s'\n", s, get_transport(transport), host); + ast_debug(2, "Found promiscuous redirection to 'SIP/%s::::%s@%s'\n", contact_number, get_transport(transport), host); if (p->owner) - ast_string_field_build(p->owner, call_forward, "SIP/%s::::%s@%s", s, get_transport(transport), host); + ast_string_field_build(p->owner, call_forward, "SIP/%s::::%s@%s", contact_number, get_transport(transport), host); } else { - ast_debug(2, "Found promiscuous redirection to 'SIP/::::%s@%s'\n", get_transport(transport), s); + ast_debug(2, "Found promiscuous redirection to 'SIP/::::%s@%s'\n", get_transport(transport), contact_number); if (p->owner) - ast_string_field_build(p->owner, call_forward, "SIP/::::%s@%s", get_transport(transport), s); + ast_string_field_build(p->owner, call_forward, "SIP/::::%s@%s", get_transport(transport), contact_number); } } else { - e = strchr(tmp, '@'); - if (e) { - *e++ = '\0'; - domain = e; + separator = strchr(contact, '@'); + if (separator) { + *separator++ = '\0'; + domain = separator; } else { /* No username part */ - domain = tmp; + domain = contact; } - e = strchr(tmp, '/'); /* WHEN do we hae a forward slash in the URI? */ - if (e) - *e = '\0'; + separator = strchr(contact, '/'); /* WHEN do we hae a forward slash in the URI? */ + if (separator) + *separator = '\0'; - if (!strncasecmp(s, "sip:", 4)) - s += 4; - else if (!strncasecmp(s, "sips:", 5)) - s += 5; - e = strchr(s, ';'); /* And username ; parameters? */ - if (e) - *e = '\0'; - ast_debug(2, "Received 302 Redirect to extension '%s' (domain %s)\n", s, domain); - if (p->owner) { - pbx_builtin_setvar_helper(p->owner, "SIPDOMAIN", domain); - ast_string_field_set(p->owner, call_forward, s); + if (!strncasecmp(contact_number, "sip:", 4)) + contact_number += 4; + else if (!strncasecmp(contact_number, "sips:", 5)) + contact_number += 5; + separator = strchr(contact_number, ';'); /* And username ; parameters? */ + if (separator) + *separator = '\0'; + if (set_call_forward) { + ast_debug(2, "Received 302 Redirect to extension '%s' (domain %s)\n", contact_number, domain); + if (p->owner) { + pbx_builtin_setvar_helper(p->owner, "SIPDOMAIN", domain); + ast_string_field_set(p->owner, call_forward, contact_number); + } } } + + /* We've gotten the number for the contact, now get the name */ + + if (*contact == '\"') { + contact_name = contact + 1; + if (!(separator = (char *)find_closing_quote(contact_name, NULL))) { + ast_log(LOG_NOTICE, "No closing quote on name in Contact header? %s\n", contact); + } + *separator = '\0'; + } + + if (name && !ast_strlen_zero(contact_name)) { + *name = ast_strdup(contact_name); + } + if (number) { + *number = ast_strdup(contact_number); + } } /*! \brief Check pending actions on SIP call */ @@ -16781,9 +17648,23 @@ return 0; } +/*! + * \brief Handle authentication challenge for SIP UPDATE + * + * This function is only called upon the receipt of a 401/407 response to an UPDATE. + */ +static void handle_response_update(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno) +{ + if (p->options) { + p->options->auth_type = (resp == 401 ? WWW_AUTH : PROXY_AUTH); + } + if ((p->authtries == MAX_AUTHTRIES) || do_proxy_auth(p, req, resp, SIP_UPDATE, 1)) { + ast_log(LOG_NOTICE, "Failed to authenticate on UPDATE to '%s'\n", get_header(&p->initreq, "From")); + } +} /*! \brief Handle SIP response to INVITE dialogue */ -static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno) +static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno) { int outgoing = ast_test_flag(&p->flags[0], SIP_OUTGOING); int res = 0; @@ -16791,12 +17672,28 @@ int reinvite = (p->owner && p->owner->_state == AST_STATE_UP); char *p_hdrval; int rtn; + struct ast_party_connected_line connected; if (reinvite) ast_debug(4, "SIP response %d to RE-invite on %s call %s\n", resp, outgoing ? "outgoing" : "incoming", p->callid); else ast_debug(4, "SIP response %d to standard invite\n", resp); + /* If this is a response to our initial INVITE, we need to set what we can use + * for this peer. + */ + if (!reinvite && p->allowed_methods == SIP_UNKNOWN) { + struct sip_peer *peer = find_peer(p->peername, NULL, 1, FINDPEERS, FALSE); + if (!peer || peer->allowed_methods == SIP_UNKNOWN) { + set_pvt_allowed_methods(p, req); + } else { + p->allowed_methods = peer->allowed_methods; + } + if (peer) { + unref_peer(peer, "handle_response_invite: Getting supported methods from peer"); + } + } + if (p->alreadygone) { /* This call is already gone */ ast_debug(1, "Got response on call that is already terminated: %s (ignoring)\n", p->callid); return; @@ -16809,7 +17706,7 @@ /* RFC3261 says we must treat every 1xx response (but not 100) that we don't recognize as if it was 183. */ - if (resp > 100 && resp < 200 && resp!=101 && resp != 180 && resp != 182 && resp != 183) + if (resp > 100 && resp < 200 && resp!=101 && resp != 180 && resp != 181 && resp != 182 && resp != 183) resp = 183; /* Any response between 100 and 199 is PROCEEDING */ @@ -16837,6 +17734,14 @@ if (!req->ignore && p->invitestate != INV_CANCELLED && sip_cancel_destroy(p)) ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); if (!req->ignore && p->owner) { + if (get_rpid(p, req)) { + ast_party_connected_line_init(&connected); + connected.id.number = (char *) p->cid_num; + connected.id.name = (char *) p->cid_name; + connected.id.number_presentation = p->callingpres; + connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; + ast_channel_queue_connected_line_update(p->owner, &connected); + } ast_queue_control(p->owner, AST_CONTROL_RINGING); if (p->owner->_state != AST_STATE_UP) { ast_setstate(p->owner, AST_STATE_RINGING); @@ -16854,10 +17759,32 @@ check_pendings(p); break; + case 181: /* Call Is Being Forwarded */ + if (!req->ignore && (p->invitestate != INV_CANCELLED) && sip_cancel_destroy(p)) + ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); + if (!req->ignore && p->owner) { + struct ast_party_redirecting redirecting = {{0,},}; + change_redirecting_information(p, req, &redirecting, FALSE); + ast_channel_queue_redirecting_update(p->owner, &redirecting); + } + check_pendings(p); + break; + case 183: /* Session progress */ if (!req->ignore && (p->invitestate != INV_CANCELLED) && sip_cancel_destroy(p)) ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); /* Ignore 183 Session progress without SDP */ + if (!req->ignore && p->owner) { + if (get_rpid(p, req)) { + /* Queue a connected line update */ + ast_party_connected_line_init(&connected); + connected.id.number = (char *) p->cid_num; + connected.id.name = (char *) p->cid_name; + connected.id.number_presentation = p->callingpres; + connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; + ast_channel_queue_connected_line_update(p->owner, &connected); + } + } if (find_sdp(req)) { if (p->invitestate != INV_CANCELLED) p->invitestate = INV_EARLY_MEDIA; @@ -16879,9 +17806,19 @@ if (!reinvite) /* This 200 OK's SDP is not acceptable, so we need to ack, then hangup */ /* For re-invites, we try to recover */ - ast_set_flag(&p->flags[0], SIP_PENDINGBYE); + ast_set_flag(&p->flags[0], SIP_PENDINGBYE); } + if (!req->ignore && p->owner && (get_rpid(p, req) || !reinvite)) { + /* Queue a connected line update */ + ast_party_connected_line_init(&connected); + connected.id.number = (char *) p->cid_num; + connected.id.name = (char *) p->cid_name; + connected.id.number_presentation = p->callingpres; + connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; + ast_channel_queue_connected_line_update(p->owner, &connected); + } + /* Parse contact header for continued conversation */ /* When we get 200 OK, we know which device (and IP) to contact for this call */ /* This is important when we have a SIP proxy between us and the phone */ @@ -17043,7 +17980,7 @@ if (p->udptl && p->t38.state == T38_LOCAL_REINVITE) { change_t38_state(p, T38_DISABLED); /* Try to reset RTP timers */ - ast_rtp_set_rtptimers_onhold(p->rtp); + //ast_rtp_set_rtptimers_onhold(p->rtp); /* Trigger a reinvite back to audio */ transmit_reinvite_with_sdp(p, FALSE, FALSE); @@ -17082,6 +18019,7 @@ } break; + case 405: /* Not allowed */ case 501: /* Not implemented */ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); if (p->owner) @@ -17095,7 +18033,7 @@ /* \brief Handle SIP response in NOTIFY transaction We've sent a NOTIFY, now handle responses to it */ -static void handle_response_notify(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno) +static void handle_response_notify(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno) { switch (resp) { case 200: /* Notify accepted */ @@ -17138,8 +18076,9 @@ } /* \brief Handle SIP response in SUBSCRIBE transaction */ -static void handle_response_subscribe(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno) +static void handle_response_subscribe(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno) { + struct sip_peer *peer; if (!p->mwi) { return; } @@ -17147,6 +18086,15 @@ switch (resp) { case 200: /* Subscription accepted */ ast_debug(3, "Got 200 OK on subscription for MWI\n"); + peer = find_peer(p->peername, NULL, 1, FINDPEERS, FALSE); + if (!peer || peer->allowed_methods == SIP_UNKNOWN) { + set_pvt_allowed_methods(p, req); + } else { + p->allowed_methods = peer->allowed_methods; + } + if (peer) { + unref_peer(peer, "handle_response_subscribe: Getting supported methods"); + } if (p->options) { ast_free(p->options); p->options = NULL; @@ -17199,8 +18147,10 @@ /* \brief Handle SIP response in REFER transaction We've sent a REFER, now handle responses to it */ -static void handle_response_refer(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno) +static void handle_response_refer(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno) { + enum ast_control_transfer message = AST_TRANSFER_FAILED; + /* If no refer structure exists, then do nothing */ if (!p->refer) return; @@ -17220,14 +18170,32 @@ if (ast_strlen_zero(p->authname)) { ast_log(LOG_WARNING, "Asked to authenticate REFER to %s:%d but we have no matching peer or realm auth!\n", ast_inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port)); + if (p->owner) { + ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message)); + } pvt_set_needdestroy(p, "unable to authenticate REFER"); } if (p->authtries > 1 || do_proxy_auth(p, req, resp, SIP_REFER, 0)) { ast_log(LOG_NOTICE, "Failed to authenticate on REFER to '%s'\n", get_header(&p->initreq, "From")); p->refer->status = REFER_NOAUTH; - pvt_set_needdestroy(p, "failed to authenticat REFER"); + if (p->owner) { + ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message)); + } + pvt_set_needdestroy(p, "failed to authenticate REFER"); } break; + + case 405: /* Method not allowed */ + /* Return to the current call onhold */ + /* Status flag needed to be reset */ + ast_log(LOG_NOTICE, "SIP transfer to %s failed, REFER not allowed. \n", p->refer->refer_to); + pvt_set_needdestroy(p, "received 405 response"); + p->refer->status = REFER_FAILED; + if (p->owner) { + ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message)); + } + break; + case 481: /* Call leg does not exist */ /* A transfer with Replaces did not work */ @@ -17246,17 +18214,23 @@ ast_log(LOG_NOTICE, "SIP transfer to %s failed, call miserably fails. \n", p->refer->refer_to); pvt_set_needdestroy(p, "received 500/501 response"); p->refer->status = REFER_FAILED; + if (p->owner) { + ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message)); + } break; case 603: /* Transfer declined */ ast_log(LOG_NOTICE, "SIP transfer to %s declined, call miserably fails. \n", p->refer->refer_to); p->refer->status = REFER_FAILED; pvt_set_needdestroy(p, "received 603 response"); + if (p->owner) { + ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message)); + } break; } } /*! \brief Handle responses on REGISTER to services */ -static int handle_response_register(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno) +static int handle_response_register(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno) { int expires, expires_ms; struct sip_registry *r; @@ -17271,7 +18245,7 @@ break; case 403: /* Forbidden */ ast_log(LOG_WARNING, "Forbidden - wrong password on authentication for REGISTER for '%s' to '%s'\n", p->registry->username, p->registry->hostname); - AST_SCHED_DEL(sched, r->timeout); + AST_SCHED_DEL_UNREF(sched, r->timeout, registry_unref(r, "reg ptr unref from handle_response_register 403")); r->regstate = REG_STATE_NOAUTH; pvt_set_needdestroy(p, "received 403 response"); break; @@ -17281,7 +18255,7 @@ if (r->call) r->call = dialog_unref(r->call, "unsetting registry->call pointer-- case 404"); r->regstate = REG_STATE_REJECTED; - AST_SCHED_DEL(sched, r->timeout); + AST_SCHED_DEL_UNREF(sched, r->timeout, registry_unref(r, "reg ptr unref from handle_response_register 404")); break; case 407: /* Proxy auth */ if (p->authtries == MAX_AUTHTRIES || do_register_auth(p, req, resp)) { @@ -17300,8 +18274,7 @@ case 423: /* Interval too brief */ r->expiry = atoi(get_header(req, "Min-Expires")); ast_log(LOG_WARNING, "Got 423 Interval too brief for service %s@%s, minimum is %d seconds\n", p->registry->username, p->registry->hostname, r->expiry); - AST_SCHED_DEL(sched, r->timeout); - r->timeout = -1; + AST_SCHED_DEL_UNREF(sched, r->timeout, registry_unref(r, "reg ptr unref from handle_response_register 423")); if (r->call) { r->call = dialog_unref(r->call, "unsetting registry->call pointer-- case 423"); pvt_set_needdestroy(p, "received 423 response"); @@ -17322,7 +18295,7 @@ if (r->call) r->call = dialog_unref(r->call, "unsetting registry->call pointer-- case 479"); r->regstate = REG_STATE_REJECTED; - AST_SCHED_DEL(sched, r->timeout); + AST_SCHED_DEL_UNREF(sched, r->timeout, registry_unref(r, "reg ptr unref from handle_response_register 479")); break; case 200: /* 200 OK */ if (!r) { @@ -17339,7 +18312,7 @@ if (r->timeout > -1) { ast_debug(1, "Cancelling timeout %d\n", r->timeout); } - AST_SCHED_DEL(sched, r->timeout); + AST_SCHED_DEL_UNREF(sched, r->timeout, registry_unref(r, "reg ptr unref from handle_response_register 200")); if (r->call) r->call = dialog_unref(r->call, "unsetting registry->call pointer-- case 200"); p->registry = registry_unref(p->registry, "unref registry entry p->registry"); @@ -17347,14 +18320,12 @@ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); /* p->needdestroy = 1; */ - /* set us up for re-registering */ - /* figure out how long we got registered for */ - AST_SCHED_DEL(sched, r->expire); - - /* according to section 6.13 of RFC, contact headers override - expires headers, so check those first */ + /* set us up for re-registering + * figure out how long we got registered for + * according to section 6.13 of RFC, contact headers override + * expires headers, so check those first */ expires = 0; - + /* XXX todo: try to save the extra call */ if (!ast_strlen_zero(get_header(req, "Contact"))) { const char *contact = NULL; @@ -17398,9 +18369,6 @@ registry_unref(_data,"unref in REPLACE del fail"), registry_unref(r,"unref in REPLACE add fail"), registry_addref(r,"The Addition side of REPLACE")); - /* it is clear that we would not want to destroy the registry entry if we just - scheduled a callback and recorded it in there! */ - /* since we never bumped the count, we shouldn't decrement it! registry_unref(r, "unref registry ptr r"); if this gets deleted, p->registry will be a bad pointer! */ } return 1; } @@ -17445,6 +18413,12 @@ manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: %s\r\nTime: %d\r\n", peer->name, s, pingtime); + if (!is_reachable) { + peer->allowed_methods = SIP_UNKNOWN; + } else { + set_pvt_allowed_methods(p, req); + peer->allowed_methods = p->allowed_methods; + } if (is_reachable && sip_cfg.regextenonqualify) register_peer_exten(peer, TRUE); } @@ -17465,18 +18439,18 @@ { /* Immediately stop RTP, VRTP and UDPTL as applicable */ if (p->rtp) - ast_rtp_stop(p->rtp); + ast_rtp_instance_stop(p->rtp); if (p->vrtp) - ast_rtp_stop(p->vrtp); + ast_rtp_instance_stop(p->vrtp); if (p->trtp) - ast_rtp_stop(p->trtp); + ast_rtp_instance_stop(p->trtp); if (p->udptl) ast_udptl_stop(p->udptl); } /*! \brief Handle SIP response in dialogue \note only called by handle_incoming */ -static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno) +static void handle_response(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno) { struct ast_channel *owner; int sipmethod; @@ -17486,6 +18460,7 @@ char *c_copy = ast_strdupa(c); /* Skip the Cseq and its subsequent spaces */ const char *msg = ast_skip_blanks(ast_skip_nonblanks(c_copy)); + struct sip_peer *peer; if (!msg) msg = ""; @@ -17546,6 +18521,7 @@ case 183: /* 183 Session Progress */ case 180: /* 180 Ringing */ case 182: /* 182 Queued */ + case 181: /* 181 Call Is Being Forwarded */ if (sipmethod == SIP_INVITE) handle_response_invite(p, resp, rest, req, seqno); break; @@ -17585,7 +18561,9 @@ handle_response_subscribe(p, resp, rest, req, seqno); else if (p->registry && sipmethod == SIP_REGISTER) res = handle_response_register(p, resp, rest, req, seqno); - else if (sipmethod == SIP_BYE) { + else if (sipmethod == SIP_UPDATE) { + handle_response_update(p, resp, rest, req, seqno); + } else if (sipmethod == SIP_BYE) { if (p->options) p->options->auth_type = resp; if (ast_strlen_zero(p->authname)) { @@ -17684,7 +18662,13 @@ pvt_set_needdestroy(p, "received 491 response"); } break; + case 405: case 501: /* Not Implemented */ + mark_method_unallowed(&p->allowed_methods, sipmethod); + if ((peer = find_peer(p->peername, 0, 1, FINDPEERS, FALSE))) { + peer->allowed_methods = p->allowed_methods; + unref_peer(peer, "handle_response: marking a specific method as unallowed"); + } if (sipmethod == SIP_INVITE) handle_response_invite(p, resp, rest, req, seqno); else if (sipmethod == SIP_REFER) @@ -17713,7 +18697,11 @@ case 301: /* Moved permanently */ case 302: /* Moved temporarily */ case 305: /* Use Proxy */ - parse_moved_contact(p, req); + { + struct ast_party_redirecting redirecting = {{0,},}; + change_redirecting_information(p, req, &redirecting, TRUE); + ast_channel_set_redirecting(p->owner, &redirecting); + } /* Fall through */ case 486: /* Busy here */ case 600: /* Busy everywhere */ @@ -18173,7 +19161,7 @@ } /*! \brief Handle incoming notifications */ -static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, char *e) +static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, const char *e) { /* This is mostly a skeleton for future improvements */ /* Mostly created to return proper answers on notifications on outbound REFER's */ @@ -18287,7 +19275,11 @@ if (!success) { ast_log(LOG_NOTICE, "Transfer failed. Sorry. Nothing further to do with this call\n"); } - + + if (p->owner) { + enum ast_control_transfer message = success ? AST_TRANSFER_SUCCESS : AST_TRANSFER_FAILED; + ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message)); + } /* Confirm that we received this packet */ transmit_response(p, "200 OK", req); } else if (p->mwi && !strcmp(event, "message-summary")) { @@ -18405,7 +19397,7 @@ /* We should answer something here. If we are here, the call we are replacing exists, so an accepted can't harm */ - transmit_response_with_sdp(p, "200 OK", req, XMIT_RELIABLE, FALSE); + transmit_response_with_sdp(p, "200 OK", req, XMIT_RELIABLE, FALSE, FALSE); /* Do something more clever here */ ast_channel_unlock(c); sip_pvt_unlock(p->refer->refer_call); @@ -18439,7 +19431,7 @@ Targetcall is not touched by the masq */ /* Answer the incoming call and set channel to UP state */ - transmit_response_with_sdp(p, "200 OK", req, XMIT_RELIABLE, FALSE); + transmit_response_with_sdp(p, "200 OK", req, XMIT_RELIABLE, FALSE, FALSE); ast_setstate(c, AST_STATE_UP); @@ -18839,13 +19831,44 @@ return 0; } -/*! \brief Handle incoming INVITE request -\note If the INVITE has a Replaces header, it is part of an +/*! + * \brief bare-bones support for SIP UPDATE + * + * XXX This is not even close to being RFC 3311-compliant. We don't advertise + * that we support the UPDATE method, so no one should ever try sending us + * an UPDATE anyway. However, Asterisk can send an UPDATE to change connected + * line information, so we need to be prepared to handle this. The way we distinguish + * such an UPDATE is through the X-Asterisk-rpid-update header. + * + * Actually updating the media session may be some future work. + */ +static int handle_request_update(struct sip_pvt *p, struct sip_request *req) +{ + if (ast_strlen_zero(get_header(req, "X-Asterisk-rpid-update"))) { + transmit_response(p, "501 Method Not Implemented", req); + return 0; + } + if (get_rpid(p, req)) { + struct ast_party_connected_line connected; + ast_party_connected_line_init(&connected); + connected.id.number = (char *) p->cid_num; + connected.id.name = (char *) p->cid_name; + connected.id.number_presentation = p->callingpres; + connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER; + ast_channel_queue_connected_line_update(p->owner, &connected); + } + transmit_response(p, "200 OK", req); + return 0; +} + +/*! + * \brief Handle incoming INVITE request + * \note If the INVITE has a Replaces header, it is part of an * attended transfer. If so, we do not go through the dial - * plan but tries to find the active call and masquerade + * plan but try to find the active call and masquerade * into it */ -static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin, int *recount, char *e, int *nounlock) +static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin, int *recount, const char *e, int *nounlock) { int res = 1; int gotdest; @@ -18908,8 +19931,8 @@ /* If pedantic is on, we need to check the tags. If they're different, this is in fact a forked call through a SIP proxy somewhere. */ int different; - char *initial_rlPart2 = REQ_OFFSET_TO_STR(&p->initreq, rlPart2); - char *this_rlPart2 = REQ_OFFSET_TO_STR(req, rlPart2); + const char *initial_rlPart2 = REQ_OFFSET_TO_STR(&p->initreq, rlPart2); + const char *this_rlPart2 = REQ_OFFSET_TO_STR(req, rlPart2); if (sip_cfg.pedanticsipchecking) different = sip_uri_cmp(initial_rlPart2, this_rlPart2); else @@ -18961,19 +19984,19 @@ __sip_ack(p, p->lastinvite, 1, 0); } else { /* We already have a pending invite. Sorry. You are on hold. */ - p->glareinvite = seqno; /* must hold on to this seqno to process ack and retransmit correctly */ + p->glareinvite = seqno; if (p->rtp && find_sdp(req)) { struct sockaddr_in sin; if (get_ip_and_port_from_sdp(req, SDP_AUDIO, &sin)) { ast_log(LOG_WARNING, "Failed to set an alternate media source on glared reinvite. Audio may not work properly on this call.\n"); } else { - ast_rtp_set_alt_peer(p->rtp, &sin); + ast_rtp_instance_set_alt_remote_address(p->rtp, &sin); } if (p->vrtp) { if (get_ip_and_port_from_sdp(req, SDP_VIDEO, &sin)) { ast_log(LOG_WARNING, "Failed to set an alternate media source on glared reinvite. Video may not work properly on this call.\n"); } else { - ast_rtp_set_alt_peer(p->vrtp, &sin); + ast_rtp_instance_set_alt_remote_address(p->vrtp, &sin); } } } @@ -19136,6 +20159,16 @@ parse_ok_contact(p, req); } else { /* Re-invite on existing call */ ast_clear_flag(&p->flags[0], SIP_OUTGOING); /* This is now an inbound dialog */ + if (get_rpid(p, req)) { + struct ast_party_connected_line connected; + + ast_party_connected_line_init(&connected); + connected.id.number = (char *) p->cid_num; + connected.id.name = (char *) p->cid_name; + connected.id.number_presentation = p->callingpres; + connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER; + ast_channel_queue_connected_line_update(p->owner, &connected); + } /* Handle SDP here if we already have an owner */ if (find_sdp(req)) { if (process_sdp(p, req, SDP_T38_INITIATE)) { @@ -19158,6 +20191,7 @@ if (!p->lastinvite && !req->ignore && !p->owner) { /* This is a new invite */ /* Handle authentication if this is our first invite */ + struct ast_party_redirecting redirecting = {{0,},}; res = check_user(p, req, SIP_INVITE, e, XMIT_RELIABLE, sin); if (res == AUTH_CHALLENGE_SENT) { p->invitestate = INV_COMPLETED; /* Needs to restart in another INVITE transaction */ @@ -19220,13 +20254,13 @@ return 0; } gotdest = get_destination(p, NULL); /* Get destination right away */ - get_rdnis(p, NULL); /* Get redirect information */ + change_redirecting_information(p, req, &redirecting, FALSE); /*Will return immediately if no Diversion header is present */ extract_uri(p, req); /* Get the Contact URI */ build_contact(p); /* Build our contact header */ if (p->rtp) { - ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); - ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); + ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); + ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); } if (!replace_id && gotdest) { /* No matching extension found */ @@ -19264,9 +20298,11 @@ if (c) { /* Pre-lock the call */ ast_channel_lock(c); + ast_channel_set_redirecting(c, &redirecting); } } } else { + struct ast_party_redirecting redirecting = {{0,},}; if (sipdebug) { if (!req->ignore) ast_debug(2, "Got a SIP re-invite for call %s\n", p->callid); @@ -19276,6 +20312,10 @@ if (!req->ignore) reinvite = 1; c = p->owner; + change_redirecting_information(p, req, &redirecting, FALSE); /*Will return immediately if no Diversion header is present */ + if (c) { + ast_channel_set_redirecting(c, &redirecting); + } } /* Session-Timers */ @@ -19486,7 +20526,6 @@ c->hangupcause = AST_CAUSE_CALL_REJECTED; } else { sip_pvt_unlock(p); - ast_setstate(c, AST_STATE_DOWN); c->hangupcause = AST_CAUSE_NORMAL_CLEARING; } p->invitestate = INV_COMPLETED; @@ -19528,7 +20567,7 @@ } else if (p->t38.state == T38_DISABLED) { /* If this is not a re-invite or something to ignore - it's critical */ ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); - transmit_response_with_sdp(p, "200 OK", req, (reinvite ? XMIT_RELIABLE : (req->ignore ? XMIT_UNRELIABLE : XMIT_CRITICAL)), p->session_modify == TRUE ? FALSE:TRUE); + transmit_response_with_sdp(p, "200 OK", req, (reinvite ? XMIT_RELIABLE : (req->ignore ? XMIT_UNRELIABLE : XMIT_CRITICAL)), p->session_modify == TRUE ? FALSE : TRUE, FALSE); } p->invitestate = INV_TERMINATED; @@ -19564,6 +20603,8 @@ /* Chan 2: Call from Asterisk to target */ int res = 0; struct sip_pvt *targetcall_pvt; + struct ast_party_connected_line connected_to_transferee; + struct ast_party_connected_line connected_to_target; /* Check if the call ID of the replaces header does exist locally */ if (!(targetcall_pvt = get_sip_pvt_byid_locked(transferer->refer->replaces_callid, transferer->refer->replaces_callid_totag, @@ -19631,6 +20672,13 @@ transferer->callid, target.chan1->name, target.chan1->uniqueid); + ast_party_connected_line_init(&connected_to_transferee); + ast_party_connected_line_init(&connected_to_target); + /* No need to lock current->chan1 here since it was locked in sipsock_read */ + ast_party_connected_line_copy(&connected_to_transferee, ¤t->chan1->connected); + /* No need to lock target.chan1 here since it was locked in get_sip_pvt_byid_locked */ + ast_party_connected_line_copy(&connected_to_target, &target.chan1->connected); + connected_to_target.source = connected_to_transferee.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER; res = attempt_transfer(current, &target); sip_pvt_unlock(targetcall_pvt); if (res) { @@ -19655,7 +20703,30 @@ ast_debug(1, "SIP attended transfer: Unlocking channel %s\n", targetcall_pvt->owner->name); ast_channel_unlock(targetcall_pvt->owner); } + + /* By forcing the masquerade, we know that target.chan1 and target.chan2 are bridged. We then + * can queue connected line updates where they need to go. + * + * No need to lock target.chan1 here since it was previously locked in get_sip_pvt_byid_locked + */ + if (target.chan1->masq) { + /* If the channel thread already did the masquerade, then we don't need to do anything */ + ast_do_masquerade(target.chan1); + } + if (target.chan2) { + ast_channel_queue_connected_line_update(target.chan1, &connected_to_transferee); + ast_channel_queue_connected_line_update(target.chan2, &connected_to_target); + } else { + /* Since target.chan1 isn't actually connected to another channel, there is no way for us + * to queue a frame so that its connected line status will be updated. Instead, we have to + * change it directly. Since we are not the channel thread, we cannot run a connected line + * interception macro on target.chan1 + */ + ast_channel_update_connected_line(target.chan1, &connected_to_target); + } } + ast_party_connected_line_free(&connected_to_target); + ast_party_connected_line_free(&connected_to_transferee); if (targetcall_pvt) ao2_t_ref(targetcall_pvt, -1, "drop targetcall_pvt"); return 1; @@ -20040,6 +21111,30 @@ else sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); if (p->initreq.len > 0) { + struct sip_pkt *pkt, *prev_pkt; + /* If the CANCEL we are receiving is a retransmission, and we already have scheduled + * a reliable 487, then we don't want to schedule another one on top of the previous + * one. + * + * As odd as this may sound, we can't rely on the previously-transmitted "reliable" + * response in this situation. What if we've sent all of our reliable responses + * already and now all of a sudden, we get this second CANCEL? + * + * The only way to do this correctly is to cancel our previously-scheduled reliably- + * transmitted response and send a new one in its place. + */ + for (pkt = p->packets, prev_pkt = NULL; pkt; prev_pkt = pkt, pkt = pkt->next) { + if (pkt->seqno == p->lastinvite && pkt->response_code == 487) { + AST_SCHED_DEL(sched, pkt->retransid); + if (prev_pkt) { + prev_pkt->next = pkt->next; + } else { + p->packets = pkt->next; + } + ast_free(pkt); + break; + } + } transmit_response_reliable(p, "487 Request Terminated", &p->initreq); transmit_response(p, "200 OK", req); return 1; @@ -20052,7 +21147,7 @@ static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *preparse, char *buf, size_t buflen) { struct sip_pvt *p = chan->tech_pvt; - char *all = "", *parse = ast_strdupa(preparse); + char *parse = ast_strdupa(preparse); int res = 0; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(param); @@ -20069,6 +21164,10 @@ memset(buf, 0, buflen); + if (p == NULL) { + return -1; + } + if (!strcasecmp(args.param, "peerip")) { ast_copy_string(buf, p->sa.sin_addr.s_addr ? ast_inet_ntoa(p->sa.sin_addr) : "", buflen); } else if (!strcasecmp(args.param, "recvip")) { @@ -20090,61 +21189,70 @@ args.type = "audio"; if (!strcasecmp(args.type, "audio")) - ast_rtp_get_peer(p->rtp, &sin); + ast_rtp_instance_get_remote_address(p->rtp, &sin); else if (!strcasecmp(args.type, "video")) - ast_rtp_get_peer(p->vrtp, &sin); + ast_rtp_instance_get_remote_address(p->vrtp, &sin); else if (!strcasecmp(args.type, "text")) - ast_rtp_get_peer(p->trtp, &sin); + ast_rtp_instance_get_remote_address(p->trtp, &sin); else return -1; snprintf(buf, buflen, "%s:%d", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); } else if (!strcasecmp(args.param, "rtpqos")) { - struct ast_rtp_quality qos; - struct ast_rtp *rtp = p->rtp; - - memset(&qos, 0, sizeof(qos)); + struct ast_rtp_instance *rtp = NULL; - if (ast_strlen_zero(args.type)) + if (ast_strlen_zero(args.type)) { args.type = "audio"; - if (ast_strlen_zero(args.field)) - args.field = "all"; - - if (!strcasecmp(args.type, "AUDIO")) { - all = ast_rtp_get_quality(rtp = p->rtp, &qos, RTPQOS_SUMMARY); - } else if (!strcasecmp(args.type, "VIDEO")) { - all = ast_rtp_get_quality(rtp = p->vrtp, &qos, RTPQOS_SUMMARY); - } else if (!strcasecmp(args.type, "TEXT")) { - all = ast_rtp_get_quality(rtp = p->trtp, &qos, RTPQOS_SUMMARY); + } + + if (!strcasecmp(args.type, "audio")) { + rtp = p->rtp; + } else if (!strcasecmp(args.type, "video")) { + rtp = p->vrtp; + } else if (!strcasecmp(args.type, "text")) { + rtp = p->trtp; } else { - return -1; + return -1; } - - if (!strcasecmp(args.field, "local_ssrc")) - snprintf(buf, buflen, "%u", qos.local_ssrc); - else if (!strcasecmp(args.field, "local_lostpackets")) - snprintf(buf, buflen, "%u", qos.local_lostpackets); - else if (!strcasecmp(args.field, "local_jitter")) - snprintf(buf, buflen, "%.0f", qos.local_jitter * 1000.0); - else if (!strcasecmp(args.field, "local_count")) - snprintf(buf, buflen, "%u", qos.local_count); - else if (!strcasecmp(args.field, "remote_ssrc")) - snprintf(buf, buflen, "%u", qos.remote_ssrc); - else if (!strcasecmp(args.field, "remote_lostpackets")) - snprintf(buf, buflen, "%u", qos.remote_lostpackets); - else if (!strcasecmp(args.field, "remote_jitter")) - snprintf(buf, buflen, "%.0f", qos.remote_jitter * 1000.0); - else if (!strcasecmp(args.field, "remote_count")) - snprintf(buf, buflen, "%u", qos.remote_count); - else if (!strcasecmp(args.field, "rtt")) - snprintf(buf, buflen, "%.0f", qos.rtt * 1000.0); - else if (!strcasecmp(args.field, "all")) - ast_copy_string(buf, all, buflen); - else if (!ast_rtp_get_qos(rtp, args.field, buf, buflen)) - ; - else { - ast_log(LOG_WARNING, "Unrecognized argument '%s' to %s\n", preparse, funcname); - return -1; + + if (ast_strlen_zero(args.field) || !strcasecmp(args.field, "all")) { + char quality_buf[AST_MAX_USER_FIELD], *quality; + + if (!(quality = ast_rtp_instance_get_quality(rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { + return -1; + } + + ast_copy_string(buf, quality_buf, buflen); + return res; + } else { + struct ast_rtp_instance_stats stats; + + if (ast_rtp_instance_get_stats(rtp, &stats, AST_RTP_INSTANCE_STAT_ALL)) { + return -1; + } + + if (!strcasecmp(args.field, "local_ssrc")) { + snprintf(buf, buflen, "%u", stats.local_ssrc); + } else if (!strcasecmp(args.field, "local_lostpackets")) { + snprintf(buf, buflen, "%u", stats.rxploss); + } else if (!strcasecmp(args.field, "local_jitter")) { + snprintf(buf, buflen, "%u", stats.rxjitter); + } else if (!strcasecmp(args.field, "local_count")) { + snprintf(buf, buflen, "%u", stats.rxcount); + } else if (!strcasecmp(args.field, "remote_ssrc")) { + snprintf(buf, buflen, "%u", stats.remote_ssrc); + } else if (!strcasecmp(args.field, "remote_lostpackets")) { + snprintf(buf, buflen, "%u", stats.txploss); + } else if (!strcasecmp(args.field, "remote_jitter")) { + snprintf(buf, buflen, "%u", stats.txjitter); + } else if (!strcasecmp(args.field, "remote_count")) { + snprintf(buf, buflen, "%u", stats.txcount); + } else if (!strcasecmp(args.field, "rtt")) { + snprintf(buf, buflen, "%u", stats.rtt); + } else { + ast_log(LOG_WARNING, "Unrecognized argument '%s' to %s\n", preparse, funcname); + return -1; + } } } else { res = -1; @@ -20176,10 +21284,10 @@ /* Get RTCP quality before end of call */ if (p->do_history || p->owner) { - struct ast_channel *bridge = p->owner ? ast_bridged_channel(p->owner) : NULL; - char *videoqos, *textqos; + char quality_buf[AST_MAX_USER_FIELD], *quality; + struct ast_channel *bridge = p->owner ? ast_bridged_channel(p->owner) : NULL; - /* We need to get the lock on bridge because ast_rtp_set_vars will attempt + /* We need to get the lock on bridge because ast_rtp_instance_set_stats_vars will attempt * to lock the bridge. This may get hairy... */ while (bridge && ast_channel_trylock(bridge)) { @@ -20193,51 +21301,52 @@ bridge = p->owner ? ast_bridged_channel(p->owner) : NULL; } - if (p->rtp) { + + if (p->rtp && (quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { if (p->do_history) { - char *audioqos, - *audioqos_jitter, - *audioqos_loss, - *audioqos_rtt; + append_history(p, "RTCPaudio", "Quality:%s", quality); - audioqos = ast_rtp_get_quality(p->rtp, NULL, RTPQOS_SUMMARY); - audioqos_jitter = ast_rtp_get_quality(p->rtp, NULL, RTPQOS_JITTER); - audioqos_loss = ast_rtp_get_quality(p->rtp, NULL, RTPQOS_LOSS); - audioqos_rtt = ast_rtp_get_quality(p->rtp, NULL, RTPQOS_RTT); + if ((quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER, quality_buf, sizeof(quality_buf)))) { + append_history(p, "RTCPaudioJitter", "Quality:%s", quality); + } + if ((quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS, quality_buf, sizeof(quality_buf)))) { + append_history(p, "RTCPaudioLoss", "Quality:%s", quality); + } + if ((quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT, quality_buf, sizeof(quality_buf)))) { + append_history(p, "RTCPaudioRTT", "Quality:%s", quality); + } + } - append_history(p, "RTCPaudio", "Quality:%s", audioqos); - append_history(p, "RTCPaudioJitter", "Quality:%s", audioqos_jitter); - append_history(p, "RTCPaudioLoss", "Quality:%s", audioqos_loss); - append_history(p, "RTCPaudioRTT", "Quality:%s", audioqos_rtt); - } - if (p->owner) { - ast_rtp_set_vars(p->owner, p->rtp); + ast_rtp_instance_set_stats_vars(p->owner, p->rtp); } + } if (bridge) { struct sip_pvt *q = bridge->tech_pvt; - if (IS_SIP_TECH(bridge->tech) && q && q->rtp) - ast_rtp_set_vars(bridge, q->rtp); + if (IS_SIP_TECH(bridge->tech) && q && q->rtp) { + ast_rtp_instance_set_stats_vars(bridge, q->rtp); + } ast_channel_unlock(bridge); } - if (p->vrtp) { - videoqos = ast_rtp_get_quality(p->vrtp, NULL, RTPQOS_SUMMARY); - if (p->do_history) - append_history(p, "RTCPvideo", "Quality:%s", videoqos); - if (p->owner) - pbx_builtin_setvar_helper(p->owner, "RTPVIDEOQOS", videoqos); + if (p->vrtp && (quality = ast_rtp_instance_get_quality(p->vrtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { + if (p->do_history) { + append_history(p, "RTCPvideo", "Quality:%s", quality); + } + if (p->owner) { + pbx_builtin_setvar_helper(p->owner, "RTPVIDEOQOS", quality); + } } - - if (p->trtp) { - textqos = ast_rtp_get_quality(p->trtp, NULL, RTPQOS_SUMMARY); - if (p->do_history) - append_history(p, "RTCPtext", "Quality:%s", textqos); - if (p->owner) - pbx_builtin_setvar_helper(p->owner, "RTPTEXTQOS", textqos); + if (p->trtp && (quality = ast_rtp_instance_get_quality(p->trtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { + if (p->do_history) { + append_history(p, "RTCPtext", "Quality:%s", quality); + } + if (p->owner) { + pbx_builtin_setvar_helper(p->owner, "RTPTEXTQOS", quality); + } } } @@ -20304,7 +21413,7 @@ } /*! \brief Handle incoming SUBSCRIBE request */ -static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, char *e) +static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, const char *e) { int gotdest = 0; int res = 0; @@ -20435,16 +21544,20 @@ make_our_tag(p->tag, sizeof(p->tag)); if (!strcmp(event, "presence") || !strcmp(event, "dialog")) { /* Presence, RFC 3842 */ + unsigned int pidf_xml; + if (authpeer) /* We do not need the authpeer any more */ unref_peer(authpeer, "unref_peer, from handle_request_subscribe (authpeer 2)"); /* Header from Xten Eye-beam Accept: multipart/related, application/rlmi+xml, application/pidf+xml, application/xpidf+xml */ - /* Polycom phones only handle xpidf+xml, even if they say they can - handle pidf+xml as well - */ - if (strstr(p->useragent, "Polycom")) { + + pidf_xml = strstr(acceptheader, "application/pidf+xml") ? 1 : 0; + + /* Older versions of Polycom firmware will claim pidf+xml, but really + * they only support xpidf+xml. */ + if (pidf_xml && strstr(p->useragent, "Polycom")) { p->subscribed = XPIDF_XML; - } else if (strstr(acceptheader, "application/pidf+xml")) { + } else if (pidf_xml) { p->subscribed = PIDF_XML; /* RFC 3863 format */ } else if (strstr(acceptheader, "application/dialog-info+xml")) { p->subscribed = DIALOG_INFO_XML; @@ -20624,7 +21737,7 @@ } /*! \brief Handle incoming REGISTER request */ -static int handle_request_register(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, char *e) +static int handle_request_register(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, const char *e) { enum check_auth_result res; @@ -20692,7 +21805,7 @@ int respid; int res = 0; int debug = sip_debug_test_pvt(p); - char *e; + const char *e; int error = 0; /* Get Method and Cseq */ @@ -20734,10 +21847,10 @@ */ int ret = 0; - if (p->ocseq < seqno && seqno != p->lastnoninvite) { + if (p->ocseq < seqno && p->lastinvite != seqno && p->lastnoninvite != seqno) { ast_debug(1, "Ignoring out of order response %d (expecting %d)\n", seqno, p->ocseq); ret = -1; - } else if (p->ocseq != seqno && seqno != p->lastnoninvite) { + } else if (p->ocseq != seqno && p->lastinvite != seqno && p->lastnoninvite != seqno) { /* ignore means "don't do anything with it" but still have to * respond appropriately. * But in this case this is a response already, so we really @@ -20869,6 +21982,9 @@ case SIP_NOTIFY: res = handle_request_notify(p, req, sin, seqno, e); break; + case SIP_UPDATE: + res = handle_request_update(p, req); + break; case SIP_ACK: /* Make sure we don't ignore this */ if (seqno == p->pendinginvite) { @@ -21029,8 +22145,8 @@ } req.len = res; - req.socket.fd = sipsock; - req.socket.type = SIP_TRANSPORT_UDP; + req.socket.fd = sipsock; + set_socket_transport(&req.socket, SIP_TRANSPORT_UDP); req.socket.tcptls_session = NULL; req.socket.port = bindaddr.sin_port; @@ -21378,13 +22494,13 @@ p = dialog_ref(peer->mwipvt, "sip_send_mwi_to_peer: Setting dialog ptr p from peer->mwipvt-- should this be done?"); } else { /* Build temporary dialog for this message */ - if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY))) + if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY, NULL))) return -1; /* If we don't set the socket type to 0, then create_addr_from_peer will fail immediately if the peer * uses any transport other than UDP. We set the type to 0 here and then let create_addr_from_peer copy * the peer's socket information to the sip_pvt we just allocated */ - p->socket.type = 0; + set_socket_transport(&p->socket, 0); if (create_addr_from_peer(p, peer)) { /* Maybe they're not registered, etc. */ dialog_unlink_all(p, TRUE, TRUE); @@ -21393,10 +22509,15 @@ return 0; } /* Recalculate our side, and recalculate Call ID */ - ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip); + ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip, p); build_via(p); ao2_t_unlink(dialogs, p, "About to change the callid -- remove the old name"); build_callid_pvt(p); + if (!ast_strlen_zero(peer->mwi_from)) { + ast_string_field_set(p, mwi_from, peer->mwi_from); + } else if (!ast_strlen_zero(default_mwi_from)) { + ast_string_field_set(p, mwi_from, default_mwi_from); + } ao2_t_link(dialogs, p, "Linking in under new name"); /* Destroy this session after 32 secs */ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); @@ -21426,15 +22547,8 @@ return; /* If we have no timers set, return now */ - if ((ast_rtp_get_rtpkeepalive(dialog->rtp) == 0) && (ast_rtp_get_rtptimeout(dialog->rtp) == 0) && (ast_rtp_get_rtpholdtimeout(dialog->rtp) == 0)) + if (!ast_rtp_instance_get_timeout(dialog->rtp) && !ast_rtp_instance_get_hold_timeout(dialog->rtp)) { return; - - /* Check AUDIO RTP keepalives */ - if (dialog->lastrtptx && ast_rtp_get_rtpkeepalive(dialog->rtp) && - (t > dialog->lastrtptx + ast_rtp_get_rtpkeepalive(dialog->rtp))) { - /* Need to send an empty RTP packet */ - dialog->lastrtptx = time(NULL); - ast_rtp_sendcng(dialog->rtp, 0); } /*! \todo Check video RTP keepalives @@ -21444,16 +22558,10 @@ */ /* Check AUDIO RTP timers */ - if (dialog->lastrtprx && (ast_rtp_get_rtptimeout(dialog->rtp) || ast_rtp_get_rtpholdtimeout(dialog->rtp)) && - (t > dialog->lastrtprx + ast_rtp_get_rtptimeout(dialog->rtp))) { - - /* Might be a timeout now -- see if we're on hold */ - struct sockaddr_in sin; - ast_rtp_get_peer(dialog->rtp, &sin); - if (!ast_test_flag(&dialog->flags[1], SIP_PAGE2_CALL_ONHOLD) || (ast_rtp_get_rtpholdtimeout(dialog->rtp) && - (t > dialog->lastrtprx + ast_rtp_get_rtpholdtimeout(dialog->rtp)))) { + if (dialog->lastrtprx && (ast_rtp_instance_get_timeout(dialog->rtp) || ast_rtp_instance_get_hold_timeout(dialog->rtp)) && (t > dialog->lastrtprx + ast_rtp_instance_get_timeout(dialog->rtp))) { + if (!ast_test_flag(&dialog->flags[1], SIP_PAGE2_CALL_ONHOLD) || (ast_rtp_instance_get_hold_timeout(dialog->rtp) && (t > dialog->lastrtprx + ast_rtp_instance_get_hold_timeout(dialog->rtp)))) { /* Needs a hangup */ - if (ast_rtp_get_rtptimeout(dialog->rtp)) { + if (ast_rtp_instance_get_timeout(dialog->rtp)) { while (dialog->owner && ast_channel_trylock(dialog->owner)) { sip_pvt_unlock(dialog); usleep(1); @@ -21468,11 +22576,11 @@ has already been requested and we don't want to repeatedly request hangups */ - ast_rtp_set_rtptimeout(dialog->rtp, 0); - ast_rtp_set_rtpholdtimeout(dialog->rtp, 0); + ast_rtp_instance_set_timeout(dialog->rtp, 0); + ast_rtp_instance_set_hold_timeout(dialog->rtp, 0); if (dialog->vrtp) { - ast_rtp_set_rtptimeout(dialog->vrtp, 0); - ast_rtp_set_rtpholdtimeout(dialog->vrtp, 0); + ast_rtp_instance_set_timeout(dialog->vrtp, 0); + ast_rtp_instance_set_hold_timeout(dialog->vrtp, 0); } } } @@ -21959,7 +23067,7 @@ peer->call = dialog_unref(peer->call, "unref dialog peer->call"); /* peer->call = sip_destroy(peer->call); */ } - if (!(p = sip_alloc(NULL, NULL, 0, SIP_OPTIONS))) { + if (!(p = sip_alloc(NULL, NULL, 0, SIP_OPTIONS, NULL))) { return -1; } peer->call = dialog_ref(p, "copy sip alloc from p to peer->call"); @@ -21980,7 +23088,7 @@ ast_string_field_set(p, tohost, ast_inet_ntoa(peer->addr.sin_addr)); /* Recalculate our side, and recalculate Call ID */ - ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip); + ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip, p); build_via(p); ao2_t_unlink(dialogs, p, "About to change the callid -- remove the old name"); build_callid_pvt(p); @@ -22154,7 +23262,7 @@ } ast_debug(1, "Asked to create a SIP channel with formats: %s\n", ast_getformatname_multiple(tmp, sizeof(tmp), oldformat)); - if (!(p = sip_alloc(NULL, NULL, 0, SIP_INVITE))) { + if (!(p = sip_alloc(NULL, NULL, 0, SIP_INVITE, NULL))) { ast_log(LOG_ERROR, "Unable to build sip pvt data for '%s' (Out of memory or socket error)\n", dest); *cause = AST_CAUSE_SWITCH_CONGESTION; return NULL; @@ -22223,8 +23331,7 @@ host = tmp; } - p->socket.fd = -1; - p->socket.type = transport; + set_socket_transport(&p->socket, transport); /* We now have host = peer name, DNS host name or DNS domain (for SRV) @@ -22242,7 +23349,7 @@ if (ast_strlen_zero(p->peername) && ext) ast_string_field_set(p, peername, ext); /* Recalculate our side, and recalculate Call ID */ - ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip); + ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip, p); build_via(p); ao2_t_unlink(dialogs, p, "About to change the callid -- remove the old name"); build_callid_pvt(p); @@ -22326,7 +23433,19 @@ ast_set2_flag(&flags[0], ast_true(v->value), SIP_TRUSTRPID); } else if (!strcasecmp(v->name, "sendrpid")) { ast_set_flag(&mask[0], SIP_SENDRPID); - ast_set2_flag(&flags[0], ast_true(v->value), SIP_SENDRPID); + if (!strcasecmp(v->value, "pai")) { + ast_set_flag(&flags[0], SIP_SENDRPID_PAI); + } else if (!strcasecmp(v->value, "rpid")) { + ast_set_flag(&flags[0], SIP_SENDRPID_RPID); + } else if (ast_true(v->value)) { + ast_set_flag(&flags[0], SIP_SENDRPID_RPID); + } + } else if (!strcasecmp(v->name, "rpid_update")) { + ast_set_flag(&mask[1], SIP_PAGE2_RPID_UPDATE); + ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_RPID_UPDATE); + } else if (!strcasecmp(v->name, "rpid_immediate")) { + ast_set_flag(&mask[1], SIP_PAGE2_RPID_IMMEDIATE); + ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_RPID_IMMEDIATE); } else if (!strcasecmp(v->name, "g726nonstandard")) { ast_set_flag(&mask[0], SIP_G726_NONSTANDARD); ast_set2_flag(&flags[0], ast_true(v->value), SIP_G726_NONSTANDARD); @@ -22623,8 +23742,7 @@ peer->expire = -1; peer->pokeexpire = -1; peer->addr.sin_port = htons(STANDARD_SIP_PORT); - peer->socket.type = SIP_TRANSPORT_UDP; - peer->socket.fd = -1; + set_socket_transport(&peer->socket, SIP_TRANSPORT_UDP); } peer->type = SIP_TYPE_PEER; ast_copy_flags(&peer->flags[0], &global_flags[0], SIP_FLAGS_TO_COPY); @@ -22634,6 +23752,7 @@ ast_string_field_set(peer, language, default_language); ast_string_field_set(peer, mohinterpret, default_mohinterpret); ast_string_field_set(peer, mohsuggest, default_mohsuggest); + ast_string_field_set(peer, engine, default_engine); peer->addr.sin_family = AF_INET; peer->defaddr.sin_family = AF_INET; peer->capability = global_capability; @@ -22861,6 +23980,8 @@ ast_callerid_split(v->value, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num)); ast_string_field_set(peer, cid_name, cid_name); ast_string_field_set(peer, cid_num, cid_num); + } else if (!strcasecmp(v->name, "mwi_from")) { + ast_string_field_set(peer, mwi_from, v->value); } else if (!strcasecmp(v->name, "fullname")) { ast_string_field_set(peer, cid_name, v->value); } else if (!strcasecmp(v->name, "cid_number")) { @@ -22984,6 +24105,8 @@ ast_string_field_set(peer, mohsuggest, v->value); } else if (!strcasecmp(v->name, "parkinglot")) { ast_string_field_set(peer, parkinglot, v->value); + } else if (!strcasecmp(v->name, "rtp_engine")) { + ast_string_field_set(peer, engine, v->value); } else if (!strcasecmp(v->name, "mailbox")) { add_peer_mailboxes(peer, v->value); } else if (!strcasecmp(v->name, "hasvoicemail")) { @@ -23010,6 +24133,8 @@ int error = ast_parse_allow_disallow(&peer->prefs, &peer->capability, v->value, FALSE); if (error) ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value); + } else if (!strcasecmp(v->name, "preferred_codec_only")) { + ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_PREFERRED_CODEC); } else if (!strcasecmp(v->name, "registertrying")) { ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_REGISTERTRYING); } else if (!strcasecmp(v->name, "autoframing")) { @@ -23128,7 +24253,7 @@ if (((peer->socket.type != peer->default_outbound_transport) && (peer->expire == -1)) || !(peer->socket.type & peer->transports) || !(peer->socket.type)) { - set_peer_transport(peer, peer->default_outbound_transport); + set_socket_transport(&peer->socket, peer->default_outbound_transport); } if (fullcontact->used > 0) { @@ -23308,21 +24433,21 @@ /* First, destroy all outstanding registry calls */ /* This is needed, since otherwise active registry entries will not be destroyed */ ASTOBJ_CONTAINER_TRAVERSE(®l, 1, do { /* regl is locked */ - - /* avoid a deadlock in the unlink_all call, if iterator->call's (a dialog) registry entry - is this registry entry. In other words, if the dialog we are pointing to points back to - us, then if we get a lock on this object, and try to UNREF it, we will deadlock, because - we already ... NO. This is not the problem. */ + ASTOBJ_RDLOCK(iterator); /* now regl is locked, and the object is also locked */ if (iterator->call) { ast_debug(3, "Destroying active SIP dialog for registry %s@%s\n", iterator->username, iterator->hostname); /* This will also remove references to the registry */ dialog_unlink_all(iterator->call, TRUE, TRUE); iterator->call = dialog_unref(iterator->call, "remove iterator->call from registry traversal"); - /* iterator->call = sip_destroy(iterator->call); */ } + if (iterator->expire > -1) { + AST_SCHED_DEL_UNREF(sched, iterator->expire, registry_unref(iterator, "reg ptr unref from reload config")); + } + if (iterator->timeout > -1) { + AST_SCHED_DEL_UNREF(sched, iterator->timeout, registry_unref(iterator, "reg ptr unref from reload config")); + } ASTOBJ_UNLOCK(iterator); - } while(0)); /* Then, actually destroy users and registry */ @@ -23330,20 +24455,21 @@ ast_debug(4, "--------------- Done destroying registry list\n"); ao2_t_callback(peers, OBJ_NODATA, peer_markall_func, NULL, "callback to mark all peers"); } - + /* Reset certificate handling for TLS sessions */ if (reason != CHANNEL_MODULE_LOAD) { ast_free(default_tls_cfg.certfile); + ast_free(default_tls_cfg.pvtfile); ast_free(default_tls_cfg.cipher); ast_free(default_tls_cfg.cafile); ast_free(default_tls_cfg.capath); } default_tls_cfg.certfile = ast_strdup(AST_CERTFILE); /*XXX Not sure if this is useful */ + default_tls_cfg.pvtfile = ast_strdup(""); default_tls_cfg.cipher = ast_strdup(""); default_tls_cfg.cafile = ast_strdup(""); default_tls_cfg.capath = ast_strdup(""); - /* Initialize copy of current global_regcontext for later use in removing stale contexts */ ast_copy_string(oldcontexts, global_regcontext, sizeof(oldcontexts)); oldregcontext = oldcontexts; @@ -23403,6 +24529,7 @@ ast_copy_string(default_notifymime, DEFAULT_NOTIFYMIME, sizeof(default_notifymime)); ast_copy_string(sip_cfg.realm, S_OR(ast_config_AST_SYSTEM_NAME, DEFAULT_REALM), sizeof(sip_cfg.realm)); ast_copy_string(default_callerid, DEFAULT_CALLERID, sizeof(default_callerid)); + ast_copy_string(default_mwi_from, DEFAULT_MWI_FROM, sizeof(default_mwi_from)); sip_cfg.compactheaders = DEFAULT_COMPACTHEADERS; global_reg_timeout = DEFAULT_REGISTRATION_TIMEOUT; global_regattempts_max = 0; @@ -23444,6 +24571,7 @@ ast_set_flag(&global_flags[0], SIP_DTMF_RFC2833); /*!< Default DTMF setting: RFC2833 */ ast_set_flag(&global_flags[0], SIP_NAT_RFC3581); /*!< NAT support if requested by device with rport */ ast_set_flag(&global_flags[0], SIP_CAN_REINVITE); /*!< Allow re-invites */ + ast_copy_string(default_engine, DEFAULT_ENGINE, sizeof(default_engine)); /* Debugging settings, always default to off */ dumphistory = FALSE; @@ -23478,13 +24606,18 @@ if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) continue; + /* handle tls conf */ + if (!ast_tls_read_conf(&default_tls_cfg, &sip_tls_desc, v->name, v->value)) { + continue; + } + if (!strcasecmp(v->name, "context")) { ast_copy_string(sip_cfg.default_context, v->value, sizeof(sip_cfg.default_context)); } else if (!strcasecmp(v->name, "subscribecontext")) { ast_copy_string(sip_cfg.default_subscribecontext, v->value, sizeof(sip_cfg.default_subscribecontext)); - } else if (!strcasecmp(v->name, "callcounter")) { + } else if (!strcasecmp(v->name, "callcounter")) { global_callcounter = ast_true(v->value) ? 1 : 0; - } else if (!strcasecmp(v->name, "allowguest")) { + } else if (!strcasecmp(v->name, "allowguest")) { sip_cfg.allowguest = ast_true(v->value) ? 1 : 0; } else if (!strcasecmp(v->name, "realm")) { ast_copy_string(sip_cfg.realm, v->value, sizeof(sip_cfg.realm)); @@ -23502,7 +24635,7 @@ } else if (!strcasecmp(v->name, "allowtransfer")) { sip_cfg.allowtransfer = ast_true(v->value) ? TRANSFER_OPENFORALL : TRANSFER_CLOSED; } else if (!strcasecmp(v->name, "rtcachefriends")) { - ast_set2_flag(&global_flags[1], ast_true(v->value), SIP_PAGE2_RTCACHEFRIENDS); + ast_set2_flag(&global_flags[1], ast_true(v->value), SIP_PAGE2_RTCACHEFRIENDS); } else if (!strcasecmp(v->name, "rtsavesysname")) { sip_cfg.rtsave_sysname = ast_true(v->value); } else if (!strcasecmp(v->name, "rtupdate")) { @@ -23525,7 +24658,7 @@ while ((trans = strsep(&val, ","))) { trans = ast_skip_blanks(trans); - if (!strncasecmp(trans, "udp", 3)) + if (!strncasecmp(trans, "udp", 3)) default_transports |= SIP_TRANSPORT_UDP; else if (!strncasecmp(trans, "tcp", 3)) default_transports |= SIP_TRANSPORT_TCP; @@ -23546,28 +24679,6 @@ ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of %s\n", v->name, v->value, v->lineno, config); sip_tcp_desc.local_address.sin_family = family; ast_debug(2, "Setting TCP socket address to %s\n", v->value); - } else if (!strcasecmp(v->name, "tlsenable")) { - default_tls_cfg.enabled = ast_true(v->value) ? TRUE : FALSE; - sip_tls_desc.local_address.sin_family = AF_INET; - } else if (!strcasecmp(v->name, "tlscertfile")) { - ast_free(default_tls_cfg.certfile); - default_tls_cfg.certfile = ast_strdup(v->value); - } else if (!strcasecmp(v->name, "tlscipher")) { - ast_free(default_tls_cfg.cipher); - default_tls_cfg.cipher = ast_strdup(v->value); - } else if (!strcasecmp(v->name, "tlscafile")) { - ast_free(default_tls_cfg.cafile); - default_tls_cfg.cafile = ast_strdup(v->value); - } else if (!strcasecmp(v->name, "tlscapath")) { - ast_free(default_tls_cfg.capath); - default_tls_cfg.capath = ast_strdup(v->value); - } else if (!strcasecmp(v->name, "tlsverifyclient")) { - ast_set2_flag(&default_tls_cfg.flags, ast_true(v->value), AST_SSL_VERIFY_CLIENT); - } else if (!strcasecmp(v->name, "tlsdontverifyserver")) { - ast_set2_flag(&default_tls_cfg.flags, ast_true(v->value), AST_SSL_DONT_VERIFY_SERVER); - } else if (!strcasecmp(v->name, "tlsbindaddr")) { - if (ast_parse_arg(v->value, PARSE_INADDR, &sip_tls_desc.local_address)) - ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of %s\n", v->name, v->value, v->lineno, config); } else if (!strcasecmp(v->name, "dynamic_exclude_static") || !strcasecmp(v->name, "dynamic_excludes_static")) { global_dynamic_exclude_static = ast_true(v->value); } else if (!strcasecmp(v->name, "contactpermit") || !strcasecmp(v->name, "contactdeny")) { @@ -23584,7 +24695,7 @@ i = 0; ast_set2_flag(&global_flags[1], i || ast_true(v->value), SIP_PAGE2_RTAUTOCLEAR); } else if (!strcasecmp(v->name, "usereqphone")) { - ast_set2_flag(&global_flags[0], ast_true(v->value), SIP_USEREQPHONE); + ast_set2_flag(&global_flags[0], ast_true(v->value), SIP_USEREQPHONE); } else if (!strcasecmp(v->name, "relaxdtmf")) { global_relaxdtmf = ast_true(v->value); } else if (!strcasecmp(v->name, "vmexten")) { @@ -23643,6 +24754,8 @@ sip_cfg.regextenonqualify = ast_true(v->value); } else if (!strcasecmp(v->name, "callerid")) { ast_copy_string(default_callerid, v->value, sizeof(default_callerid)); + } else if (!strcasecmp(v->name, "mwi_from")) { + ast_copy_string(default_mwi_from, v->value, sizeof(default_mwi_from)); } else if (!strcasecmp(v->name, "fromdomain")) { ast_copy_string(default_fromdomain, v->value, sizeof(default_fromdomain)); } else if (!strcasecmp(v->name, "outboundproxy")) { @@ -23758,6 +24871,8 @@ int error = ast_parse_allow_disallow(&default_prefs, &global_capability, v->value, FALSE); if (error) ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value); + } else if (!strcasecmp(v->name, "preferred_codec_only")) { + ast_set2_flag(&global_flags[1], ast_true(v->value), SIP_PAGE2_PREFERRED_CODEC); } else if (!strcasecmp(v->name, "autoframing")) { global_autoframing = ast_true(v->value); } else if (!strcasecmp(v->name, "allowexternaldomains")) { @@ -24182,165 +25297,183 @@ return 0; } -/*! \brief Returns null if we can't reinvite audio (part of RTP interface) */ -static enum ast_rtp_get_result sip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) +static enum ast_rtp_glue_result sip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) { - struct sip_pvt *p = NULL; - enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL; + struct sip_pvt *p = NULL; + enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_LOCAL; - if (!(p = chan->tech_pvt)) - return AST_RTP_GET_FAILED; - - sip_pvt_lock(p); - if (!(p->rtp)) { - sip_pvt_unlock(p); - return AST_RTP_GET_FAILED; + if (!(p = chan->tech_pvt)) { + return AST_RTP_GLUE_RESULT_FORBID; } - *rtp = p->rtp; + sip_pvt_lock(p); + if (!(p->rtp)) { + sip_pvt_unlock(p); + return AST_RTP_GLUE_RESULT_FORBID; + } - if (ast_rtp_getnat(*rtp) && !ast_test_flag(&p->flags[0], SIP_CAN_REINVITE_NAT)) - res = AST_RTP_TRY_PARTIAL; - else if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) - res = AST_RTP_TRY_NATIVE; - else if (ast_test_flag(&global_jbconf, AST_JB_FORCED)) - res = AST_RTP_GET_FAILED; + ao2_ref(p->rtp, +1); + *instance = p->rtp; - sip_pvt_unlock(p); + if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE | SIP_CAN_REINVITE_NAT)) { + res = AST_RTP_GLUE_RESULT_REMOTE; + } else if (ast_test_flag(&global_jbconf, AST_JB_FORCED)) { + res = AST_RTP_GLUE_RESULT_FORBID; + } - return res; + sip_pvt_unlock(p); + + return res; } -/*! \brief Returns null if we can't reinvite video (part of RTP interface) */ -static enum ast_rtp_get_result sip_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) +static enum ast_rtp_glue_result sip_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) { struct sip_pvt *p = NULL; - enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL; - - if (!(p = chan->tech_pvt)) - return AST_RTP_GET_FAILED; + enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_FORBID; + if (!(p = chan->tech_pvt)) { + return AST_RTP_GLUE_RESULT_FORBID; + } + sip_pvt_lock(p); if (!(p->vrtp)) { sip_pvt_unlock(p); - return AST_RTP_GET_FAILED; + return AST_RTP_GLUE_RESULT_FORBID; } - *rtp = p->vrtp; + ao2_ref(p->vrtp, +1); + *instance = p->vrtp; - if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) - res = AST_RTP_TRY_NATIVE; + if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) { + res = AST_RTP_GLUE_RESULT_REMOTE; + } sip_pvt_unlock(p); return res; } -/*! \brief Returns null if we can't reinvite text (part of RTP interface) */ -static enum ast_rtp_get_result sip_get_trtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) +static enum ast_rtp_glue_result sip_get_trtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) { - struct sip_pvt *p = NULL; - enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL; - - if (!(p = chan->tech_pvt)) - return AST_RTP_GET_FAILED; + struct sip_pvt *p = NULL; + enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_FORBID; - sip_pvt_lock(p); - if (!(p->trtp)) { - sip_pvt_unlock(p); - return AST_RTP_GET_FAILED; - } + if (!(p = chan->tech_pvt)) { + return AST_RTP_GLUE_RESULT_FORBID; + } - *rtp = p->trtp; + sip_pvt_lock(p); + if (!(p->trtp)) { + sip_pvt_unlock(p); + return AST_RTP_GLUE_RESULT_FORBID; + } - if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) - res = AST_RTP_TRY_NATIVE; + ao2_ref(p->trtp, +1); + *instance = p->trtp; - sip_pvt_unlock(p); + if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) { + res = AST_RTP_GLUE_RESULT_REMOTE; + } - return res; + sip_pvt_unlock(p); + + return res; } -/*! \brief Set the RTP peer for this call */ -static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) +static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *instance, struct ast_rtp_instance *vinstance, struct ast_rtp_instance *tinstance, int codecs, int nat_active) { - struct sip_pvt *p; - int changed = 0; + struct sip_pvt *p; + int changed = 0; - p = chan->tech_pvt; - if (!p) - return -1; + p = chan->tech_pvt; + if (!p) + return -1; /* Disable early RTP bridge */ if (!ast_bridged_channel(chan) && !sip_cfg.directrtpsetup) /* We are in early state */ return 0; - sip_pvt_lock(p); - if (p->alreadygone) { - /* If we're destroyed, don't bother */ - sip_pvt_unlock(p); - return 0; - } + sip_pvt_lock(p); + if (p->alreadygone) { + /* If we're destroyed, don't bother */ + sip_pvt_unlock(p); + return 0; + } - /* if this peer cannot handle reinvites of the media stream to devices - that are known to be behind a NAT, then stop the process now + /* if this peer cannot handle reinvites of the media stream to devices + that are known to be behind a NAT, then stop the process now */ - if (nat_active && !ast_test_flag(&p->flags[0], SIP_CAN_REINVITE_NAT)) { - sip_pvt_unlock(p); - return 0; - } + if (nat_active && !ast_test_flag(&p->flags[0], SIP_CAN_REINVITE_NAT)) { + sip_pvt_unlock(p); + return 0; + } - if (rtp) { - changed |= ast_rtp_get_peer(rtp, &p->redirip); - } else if (p->redirip.sin_addr.s_addr || ntohs(p->redirip.sin_port) != 0) { - memset(&p->redirip, 0, sizeof(p->redirip)); - changed = 1; - } - if (vrtp) { - changed |= ast_rtp_get_peer(vrtp, &p->vredirip); - } else if (p->vredirip.sin_addr.s_addr || ntohs(p->vredirip.sin_port) != 0) { - memset(&p->vredirip, 0, sizeof(p->vredirip)); - changed = 1; - } - if (trtp) { - changed |= ast_rtp_get_peer(trtp, &p->tredirip); - } else if (p->tredirip.sin_addr.s_addr || ntohs(p->tredirip.sin_port) != 0) { - memset(&p->tredirip, 0, sizeof(p->tredirip)); - changed = 1; - } - if (codecs && (p->redircodecs != codecs)) { - p->redircodecs = codecs; - changed = 1; - } - if (changed && !ast_test_flag(&p->flags[0], SIP_GOTREFER) && !ast_test_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER)) { - if (chan->_state != AST_STATE_UP) { /* We are in early state */ - if (p->do_history) - append_history(p, "ExtInv", "Initial invite sent with remote bridge proposal."); - ast_debug(1, "Early remote bridge setting SIP '%s' - Sending media to %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip.sin_addr)); - } else if (!p->pendinginvite) { /* We are up, and have no outstanding invite */ - ast_debug(3, "Sending reinvite on SIP '%s' - It's audio soon redirected to IP %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip.sin_addr)); - transmit_reinvite_with_sdp(p, FALSE, FALSE); - } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) { - ast_debug(3, "Deferring reinvite on SIP '%s' - It's audio will be redirected to IP %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip.sin_addr)); - /* We have a pending Invite. Send re-invite when we're done with the invite */ - ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); - } - } - /* Reset lastrtprx timer */ - p->lastrtprx = p->lastrtptx = time(NULL); - sip_pvt_unlock(p); - return 0; + if (instance) { + changed |= ast_rtp_instance_get_remote_address(instance, &p->redirip); + } else if (p->redirip.sin_addr.s_addr || ntohs(p->redirip.sin_port) != 0) { + memset(&p->redirip, 0, sizeof(p->redirip)); + changed = 1; + } + if (vinstance) { + changed |= ast_rtp_instance_get_remote_address(vinstance, &p->vredirip); + } else if (p->vredirip.sin_addr.s_addr || ntohs(p->vredirip.sin_port) != 0) { + memset(&p->vredirip, 0, sizeof(p->vredirip)); + changed = 1; + } + if (tinstance) { + changed |= ast_rtp_instance_get_remote_address(tinstance, &p->tredirip); + } else if (p->tredirip.sin_addr.s_addr || ntohs(p->tredirip.sin_port) != 0) { + memset(&p->tredirip, 0, sizeof(p->tredirip)); + changed = 1; + } + if (codecs && (p->redircodecs != codecs)) { + p->redircodecs = codecs; + changed = 1; + } + if (changed && !ast_test_flag(&p->flags[0], SIP_GOTREFER) && !ast_test_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER)) { + if (chan->_state != AST_STATE_UP) { /* We are in early state */ + if (p->do_history) + append_history(p, "ExtInv", "Initial invite sent with remote bridge proposal."); + ast_debug(1, "Early remote bridge setting SIP '%s' - Sending media to %s\n", p->callid, ast_inet_ntoa(instance ? p->redirip.sin_addr : p->ourip.sin_addr)); + } else if (!p->pendinginvite) { /* We are up, and have no outstanding invite */ + ast_debug(3, "Sending reinvite on SIP '%s' - It's audio soon redirected to IP %s\n", p->callid, ast_inet_ntoa(instance ? p->redirip.sin_addr : p->ourip.sin_addr)); + transmit_reinvite_with_sdp(p, FALSE, FALSE); + } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) { + ast_debug(3, "Deferring reinvite on SIP '%s' - It's audio will be redirected to IP %s\n", p->callid, ast_inet_ntoa(instance ? p->redirip.sin_addr : p->ourip.sin_addr)); + /* We have a pending Invite. Send re-invite when we're done with the invite */ + ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); + } + } + /* Reset lastrtprx timer */ + p->lastrtprx = p->lastrtptx = time(NULL); + sip_pvt_unlock(p); + return 0; } +static int sip_get_codec(struct ast_channel *chan) +{ + struct sip_pvt *p = chan->tech_pvt; + return p->peercapability ? p->peercapability : p->capability; +} + +static struct ast_rtp_glue sip_rtp_glue = { + .type = "SIP", + .get_rtp_info = sip_get_rtp_peer, + .get_vrtp_info = sip_get_vrtp_peer, + .get_trtp_info = sip_get_trtp_peer, + .update_peer = sip_set_rtp_peer, + .get_codec = sip_get_codec, +}; + static char *app_dtmfmode = "SIPDtmfMode"; static char *app_sipaddheader = "SIPAddHeader"; static char *app_sipremoveheader = "SIPRemoveHeader"; /*! \brief Set the DTMFmode for an outbound SIP call (application) */ -static int sip_dtmfmode(struct ast_channel *chan, void *data) +static int sip_dtmfmode(struct ast_channel *chan, const char *data) { struct sip_pvt *p; - char *mode = data; + const char *mode = data; if (!data) { ast_log(LOG_WARNING, "This application requires the argument: info, inband, rfc2833\n"); @@ -24377,17 +25510,12 @@ } else ast_log(LOG_WARNING, "I don't know about this dtmf mode: %s\n", mode); if (p->rtp) - ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); - if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) { - if (!p->vad) { - p->vad = ast_dsp_new(); - ast_dsp_set_features(p->vad, DSP_FEATURE_DIGIT_DETECT); - } + ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); + if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) || + (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) { + enable_digit_detect(p); } else { - if (p->vad) { - ast_dsp_free(p->vad); - p->vad = NULL; - } + disable_digit_detect(p); } sip_pvt_unlock(p); ast_channel_unlock(chan); @@ -24395,12 +25523,13 @@ } /*! \brief Add a SIP header to an outbound INVITE */ -static int sip_addheader(struct ast_channel *chan, void *data) +static int sip_addheader(struct ast_channel *chan, const char *data) { int no = 0; int ok = FALSE; char varbuf[30]; - char *inbuf = data, *subbuf; + const char *inbuf = data; + char *subbuf; if (ast_strlen_zero(inbuf)) { ast_log(LOG_WARNING, "This application requires the argument: Header\n"); @@ -24434,7 +25563,7 @@ } /*! \brief Remove SIP headers added previously with SipAddHeader application */ -static int sip_removeheader(struct ast_channel *chan, void *data) +static int sip_removeheader(struct ast_channel *chan, const char *data) { struct ast_var_t *newvariable; struct varshead *headp; @@ -24521,17 +25650,15 @@ sip_scheddestroy(p, SIP_TRANS_TIMEOUT); /* Make sure we stop send this reply. */ sip_alreadygone(p); + + if (p->owner) { + enum ast_control_transfer message = AST_TRANSFER_SUCCESS; + ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message)); + } /* hangup here */ return 0; } -/*! \brief Return SIP UA's codec (part of the RTP interface) */ -static int sip_get_codec(struct ast_channel *chan) -{ - struct sip_pvt *p = chan->tech_pvt; - return p->jointcapability ? p->jointcapability : p->capability; -} - /*! \brief Send a poke to all known peers */ static void sip_poke_all_peers(void) { @@ -24739,12 +25866,12 @@ /* Register all CLI functions for SIP */ ast_cli_register_multiple(cli_sip, ARRAY_LEN(cli_sip)); - /* Tell the RTP subdriver that we're here */ - ast_rtp_proto_register(&sip_rtp); - /* Tell the UDPTL subdriver that we're here */ ast_udptl_proto_register(&sip_udptl); + /* Tell the RTP engine about our RTP glue */ + ast_rtp_glue_register(&sip_rtp_glue); + /* Register dialplan applications */ ast_register_application_xml(app_dtmfmode, sip_dtmfmode); ast_register_application_xml(app_sipaddheader, sip_addheader); @@ -24757,16 +25884,11 @@ ast_custom_function_register(&checksipdomain_function); /* Register manager commands */ - ast_manager_register2("SIPpeers", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_sip_show_peers, - "List SIP peers (text format)", mandescr_show_peers); - ast_manager_register2("SIPshowpeer", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_sip_show_peer, - "Show SIP peer (text format)", mandescr_show_peer); - ast_manager_register2("SIPqualifypeer", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_sip_qualify_peer, - "Show SIP peer (text format)", mandescr_show_peer); /*! \todo Fix this XXX This must be all wrong XXXX */ - ast_manager_register2("SIPshowregistry", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_show_registry, - "Show SIP registrations (text format)", mandescr_show_registry); - ast_manager_register2("SIPnotify", EVENT_FLAG_SYSTEM, manager_sipnotify, - "Send a SIP notify", mandescr_sipnotify); + ast_manager_register_xml("SIPpeers", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_sip_show_peers); + ast_manager_register_xml("SIPshowpeer", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_sip_show_peer); + ast_manager_register_xml("SIPqualifypeer", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_sip_qualify_peer); + ast_manager_register_xml("SIPshowregistry", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_show_registry); + ast_manager_register_xml("SIPnotify", EVENT_FLAG_SYSTEM, manager_sipnotify); sip_poke_all_peers(); sip_send_all_registers(); sip_send_all_mwi_subscriptions(); @@ -24816,12 +25938,12 @@ /* Unregister CLI commands */ ast_cli_unregister_multiple(cli_sip, ARRAY_LEN(cli_sip)); - /* Disconnect from the RTP subsystem */ - ast_rtp_proto_unregister(&sip_rtp); - /* Disconnect from UDPTL */ ast_udptl_proto_unregister(&sip_udptl); + /* Disconnect from RTP engine */ + ast_rtp_glue_unregister(&sip_rtp_glue); + /* Unregister AMI actions */ ast_manager_unregister("SIPpeers"); ast_manager_unregister("SIPshowpeer"); @@ -24880,6 +26002,8 @@ if (default_tls_cfg.certfile) ast_free(default_tls_cfg.certfile); + if (default_tls_cfg.pvtfile) + ast_free(default_tls_cfg.pvtfile); if (default_tls_cfg.cipher) ast_free(default_tls_cfg.cipher); if (default_tls_cfg.cafile) Index: channels/chan_agent.c =================================================================== --- a/channels/chan_agent.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/chan_agent.c (.../trunk) (revision 202568) @@ -52,7 +52,6 @@ #include "asterisk/pbx.h" #include "asterisk/sched.h" #include "asterisk/io.h" -#include "asterisk/rtp.h" #include "asterisk/acl.h" #include "asterisk/callerid.h" #include "asterisk/file.h" @@ -157,9 +156,6 @@ MusicOnHold class - - The callback extension for the Agent (AgentCallbackLogin) - The name of the active channel for the Agent (AgentLogin) @@ -168,6 +164,34 @@ + + + Lists agents and their status. + + + + + + Will list info about all possible agents. + + + + + Sets an agent as no longer logged in. + + + + + Agent ID of the agent to log off. + + + Set to true to not hangup existing calls. + + + + Sets an agent as no longer logged in. + + ***/ static const char tdesc[] = "Call Agent Proxy Channel"; @@ -176,16 +200,6 @@ static const char app[] = "AgentLogin"; static const char app3[] = "AgentMonitorOutgoing"; -static const char mandescr_agents[] = -"Description: Will list info about all possible agents.\n" -"Variables: NONE\n"; - -static const char mandescr_agent_logoff[] = -"Description: Sets an agent as no longer logged in.\n" -"Variables: (Names marked with * are required)\n" -" *Agent: Agent ID of the agent to log off\n" -" Soft: Set to 'true' to not hangup existing calls\n"; - static char moh[80] = "default"; #define AST_MAX_AGENT 80 /*!< Agent ID or Password max length */ @@ -195,9 +209,6 @@ static const char pa_family[] = "Agents"; /*!< Persistent Agents astdb family */ #define PA_MAX_LEN 2048 /*!< The maximum length of each persistent member agent database entry */ -static int persistent_agents = 0; /*!< queues.conf [general] option */ -static void dump_agents(void); - #define DEFAULT_ACCEPTDTMF '#' #define DEFAULT_ENDDTMF '*' @@ -258,7 +269,6 @@ ast_cond_t app_complete_cond; volatile int app_sleep_cond; /**< Sleep condition for the login app */ struct ast_channel *owner; /**< Agent */ - char loginchan[80]; /**< channel they logged in from */ char logincallerid[80]; /**< Caller ID they had when they logged in */ struct ast_channel *chan; /**< Channel we use */ unsigned int flags; /**< Flags show if settings were applied with channel vars */ @@ -302,7 +312,6 @@ /*--- Forward declarations */ static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause); static int agent_devicestate(void *data); -static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand); static int agent_digit_begin(struct ast_channel *ast, char digit); static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration); static int agent_call(struct ast_channel *ast, char *dest, int timeout); @@ -315,7 +324,6 @@ static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen); static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge); -static void set_agentbycallerid(const char *callerid, const char *agent); static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state); static struct ast_channel* agent_get_base_channel(struct ast_channel *chan); static int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base); @@ -464,8 +472,9 @@ /* Release ownership of the agent to other threads (presumably running the login app). */ p->app_lock_flag = 0; ast_cond_signal(&p->app_complete_cond); - if (chan) - ast_channel_free(chan); + if (chan) { + chan = ast_channel_release(chan); + } if (p->dead) { ast_mutex_destroy(&p->lock); ast_mutex_destroy(&p->app_lock); @@ -521,7 +530,6 @@ struct agent_pvt *p = ast->tech_pvt; struct ast_frame *f = NULL; static struct ast_frame answer_frame = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER }; - const char *status; int cur_time = time(NULL); ast_mutex_lock(&p->lock); CHECK_FORMATS(ast, p); @@ -535,33 +543,9 @@ } else f = &ast_null_frame; if (!f) { - /* If there's a channel, hang it up (if it's on a callback) make it NULL */ + /* If there's a channel, make it NULL */ if (p->chan) { p->chan->_bridge = NULL; - /* Note that we don't hangup if it's not a callback because Asterisk will do it - for us when the PBX instance that called login finishes */ - if (!ast_strlen_zero(p->loginchan)) { - if (p->chan) - ast_debug(1, "Bridge on '%s' being cleared (2)\n", p->chan->name); - if (p->owner->_state != AST_STATE_UP) { - int howlong = cur_time - p->start; - if (p->autologoff && howlong >= p->autologoff) { - p->loginstart = 0; - ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong); - agent_logoff_maintenance(p, p->loginchan, (cur_time = p->loginstart), ast->uniqueid, "Autologoff"); - } - } - status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS"); - if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) { - long logintime = cur_time - p->loginstart; - p->loginstart = 0; - ast_log(LOG_NOTICE, "Agent read: '%s' is not available now, auto logoff\n", p->name); - agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail"); - } - ast_hangup(p->chan); - if (p->wrapuptime && p->acknowledged) - p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000)); - } p->chan = NULL; ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent); p->acknowledged = 0; @@ -577,7 +561,6 @@ int howlong = cur_time - p->start; if (p->autologoff && (howlong >= p->autologoff)) { ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong); - agent_logoff_maintenance(p, p->loginchan, (cur_time - p->loginstart), ast->uniqueid, "Autologoff"); agent_logoff(p->agent, 0); } } @@ -766,17 +749,6 @@ if (newstate) ast_setstate(ast, newstate); return res; - } else if (!ast_strlen_zero(p->loginchan)) { - time(&p->start); - /* Call on this agent */ - ast_verb(3, "outgoing agentcall, to agent '%s', on '%s'\n", p->agent, p->chan->name); - ast_set_callerid(p->chan, - ast->cid.cid_num, ast->cid.cid_name, NULL); - ast_channel_inherit_variables(ast, p->chan); - res = ast_call(p->chan, p->loginchan, 0); - CLEANUP(ast,p); - ast_mutex_unlock(&p->lock); - return res; } ast_verb(3, "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name); ast_debug(3, "Playing beep, lang '%s'\n", p->chan->language); @@ -805,9 +777,9 @@ } if(!res) { /* Call is immediately up, or might need ack */ - if (p->ackcall > 1) + if (p->ackcall) { newstate = AST_STATE_RINGING; - else { + } else { newstate = AST_STATE_UP; if (recordagentcalls) agent_start_monitoring(ast, 0); @@ -822,19 +794,6 @@ return res; } -/*! \brief store/clear the global variable that stores agentid based on the callerid */ -static void set_agentbycallerid(const char *callerid, const char *agent) -{ - char buf[AST_MAX_BUF]; - - /* if there is no Caller ID, nothing to do */ - if (ast_strlen_zero(callerid)) - return; - - snprintf(buf, sizeof(buf), "%s_%s", GETAGENTBYCALLERID, callerid); - pbx_builtin_setvar_helper(NULL, buf, agent); -} - /*! \brief return the channel or base channel if one exists. This function assumes the channel it is called on is already locked */ struct ast_channel* agent_get_base_channel(struct ast_channel *chan) { @@ -873,7 +832,7 @@ { struct agent_pvt *p = ast->tech_pvt; int howlong = 0; - const char *status; + ast_mutex_lock(&p->lock); p->owner = NULL; ast->tech_pvt = NULL; @@ -898,37 +857,7 @@ if (p->chan) { p->chan->_bridge = NULL; /* If they're dead, go ahead and hang up on the agent now */ - if (!ast_strlen_zero(p->loginchan)) { - /* Store last disconnect time */ - if (p->wrapuptime) - p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000)); - else - p->lastdisc = ast_tv(0,0); - if (p->chan) { - status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS"); - if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) { - long logintime = time(NULL) - p->loginstart; - p->loginstart = 0; - ast_log(LOG_NOTICE, "Agent hangup: '%s' is not available now, auto logoff\n", p->name); - agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail"); - } - /* Recognize the hangup and pass it along immediately */ - ast_hangup(p->chan); - p->chan = NULL; - ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent); - } - ast_debug(1, "Hungup, howlong is %d, autologoff is %d\n", howlong, p->autologoff); - if ((p->deferlogoff) || (howlong && p->autologoff && (howlong > p->autologoff))) { - long logintime = time(NULL) - p->loginstart; - p->loginstart = 0; - if (!p->deferlogoff) - ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong); - p->deferlogoff = 0; - agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Autologoff"); - if (persistent_agents) - dump_agents(); - } - } else if (p->dead) { + if (p->dead) { ast_channel_lock(p->chan); ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT); ast_channel_unlock(p->chan); @@ -944,10 +873,7 @@ /* Only register a device state change if the agent is still logged in */ if (!p->loginstart) { - p->loginchan[0] = '\0'; p->logincallerid[0] = '\0'; - if (persistent_agents) - dump_agents(); } else { ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent); } @@ -975,10 +901,8 @@ ast_mutex_unlock(&p->lock); } /* Release ownership of the agent to other threads (presumably running the login app). */ - if (ast_strlen_zero(p->loginchan)) { - p->app_lock_flag = 0; - ast_cond_signal(&p->app_complete_cond); - } + p->app_lock_flag = 0; + ast_cond_signal(&p->app_complete_cond); } return 0; } @@ -1115,7 +1039,7 @@ alreadylocked = p->app_lock_flag; p->app_lock_flag = 1; - if(ast_strlen_zero(p->loginchan) && alreadylocked) { + if (alreadylocked) { if (p->chan) { ast_queue_frame(p->chan, &ast_null_frame); ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */ @@ -1126,24 +1050,12 @@ p->owner = NULL; tmp->tech_pvt = NULL; p->app_sleep_cond = 1; - ast_channel_free( tmp ); + tmp = ast_channel_release(tmp); ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */ p->app_lock_flag = 0; ast_cond_signal(&p->app_complete_cond); return NULL; } - } else if (!ast_strlen_zero(p->loginchan)) { - if (p->chan) - ast_queue_frame(p->chan, &ast_null_frame); - if (!p->chan) { - ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n"); - p->owner = NULL; - tmp->tech_pvt = NULL; - p->app_sleep_cond = 1; - ast_channel_free( tmp ); - ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */ - return NULL; - } } if (p->chan) ast_indicate(p->chan, AST_CONTROL_UNHOLD); @@ -1162,7 +1074,6 @@ struct ast_config *ucfg; struct ast_variable *v; struct agent_pvt *p; - const char *general_val; const char *catname; const char *hasagent; int genhasagent; @@ -1205,8 +1116,6 @@ savecallsin[0] = '\0'; /* Read in [general] section for persistence */ - if ((general_val = ast_variable_retrieve(cfg, "general", "persistentagents"))) - persistent_agents = ast_true(general_val); multiplelogin = ast_true(ast_variable_retrieve(cfg, "general", "multiplelogin")); /* Read in the [agents] section */ @@ -1222,12 +1131,9 @@ if (autologoff < 0) autologoff = 0; } else if (!strcasecmp(v->name, "ackcall")) { - if (!strcasecmp(v->value, "always")) - ackcall = 2; - else if (ast_true(v->value)) + if (ast_true(v->value) || !strcasecmp(v->value, "always")) { ackcall = 1; - else - ackcall = 0; + } } else if (!strcasecmp(v->name, "endcall")) { endcall = ast_true(v->value); } else if (!strcasecmp(v->name, "acceptdtmf")) { @@ -1354,7 +1260,7 @@ if (needlock) AST_LIST_UNLOCK(&agents); if (parent && chan) { - if (newlyavailable->ackcall > 1) { + if (newlyavailable->ackcall) { /* Don't do beep here */ res = 0; } else { @@ -1452,8 +1358,7 @@ AST_LIST_LOCK(&agents); AST_LIST_TRAVERSE(&agents, p, list) { ast_mutex_lock(&p->lock); - if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent)) && - ast_strlen_zero(p->loginchan)) { + if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) { if (p->chan) hasagent++; now = ast_tvnow(); @@ -1476,23 +1381,16 @@ AST_LIST_TRAVERSE(&agents, p, list) { ast_mutex_lock(&p->lock); if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) { - if (p->chan || !ast_strlen_zero(p->loginchan)) + if (p->chan) { hasagent++; + } now = ast_tvnow(); -#if 0 - ast_log(LOG_NOTICE, "Time now: %ld, Time of lastdisc: %ld\n", now.tv_sec, p->lastdisc.tv_sec); -#endif if (!p->lastdisc.tv_sec || (now.tv_sec >= p->lastdisc.tv_sec)) { p->lastdisc = ast_tv(0, 0); /* Agent must be registered, but not have any active call, and not be in a waiting state */ if (!p->owner && p->chan) { /* Could still get a fixed agent */ chan = agent_new(p, AST_STATE_DOWN); - } else if (!p->owner && !ast_strlen_zero(p->loginchan)) { - /* Adjustable agent */ - p->chan = ast_request("Local", format, p->loginchan, cause); - if (p->chan) - chan = agent_new(p, AST_STATE_DOWN); } if (chan) { ast_mutex_unlock(&p->lock); @@ -1545,7 +1443,6 @@ { const char *id = astman_get_header(m,"ActionID"); char idText[256] = ""; - char chanbuf[256]; struct agent_pvt *p; char *username = NULL; char *loginChan = NULL; @@ -1571,16 +1468,7 @@ /* Set a default status. It 'should' get changed. */ status = "AGENT_UNKNOWN"; - if (!ast_strlen_zero(p->loginchan) && !p->chan) { - loginChan = p->loginchan; - talkingto = "n/a"; - talkingtoChan = "n/a"; - status = "AGENT_IDLE"; - if (p->acknowledged) { - snprintf(chanbuf, sizeof(chanbuf), " %s (Confirmed)", p->loginchan); - loginChan = chanbuf; - } - } else if (p->chan) { + if (p->chan) { loginChan = ast_strdupa(p->chan->name); if (p->owner && p->owner->_bridge) { talkingto = p->chan->cid.cid_num; @@ -1621,49 +1509,9 @@ return 0; } -static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand) -{ - char *tmp = NULL; - char agent[AST_MAX_AGENT]; - - if (!ast_strlen_zero(logcommand)) - tmp = logcommand; - else - tmp = ast_strdupa(""); - - snprintf(agent, sizeof(agent), "Agent/%s", p->agent); - - if (!ast_strlen_zero(uniqueid)) { - manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff", - "Agent: %s\r\n" - "Reason: %s\r\n" - "Loginchan: %s\r\n" - "Logintime: %ld\r\n" - "Uniqueid: %s\r\n", - p->agent, tmp, loginchan, logintime, uniqueid); - } else { - manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff", - "Agent: %s\r\n" - "Reason: %s\r\n" - "Loginchan: %s\r\n" - "Logintime: %ld\r\n", - p->agent, tmp, loginchan, logintime); - } - - ast_queue_log("NONE", ast_strlen_zero(uniqueid) ? "NONE" : uniqueid, agent, "AGENTCALLBACKLOGOFF", "%s|%ld|%s", loginchan, logintime, tmp); - set_agentbycallerid(p->logincallerid, NULL); - p->loginchan[0] ='\0'; - p->logincallerid[0] = '\0'; - ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent); - if (persistent_agents) - dump_agents(); - -} - static int agent_logoff(const char *agent, int soft) { struct agent_pvt *p; - long logintime; int ret = -1; /* Return -1 if no agent if found */ AST_LIST_LOCK(&agents); @@ -1693,10 +1541,6 @@ ast_mutex_unlock(&p->lock); } else p->deferlogoff = 1; - } else { - logintime = time(NULL) - p->loginstart; - p->loginstart = 0; - agent_logoff_maintenance(p, p->loginchan, logintime, NULL, "CommandLogoff"); } break; } @@ -1709,7 +1553,7 @@ static char *agent_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { int ret; - char *agent; + const char *agent; switch (cmd) { case CLI_INIT: @@ -1838,15 +1682,6 @@ else strcpy(talkingto, " is idle"); online_agents++; - } else if (!ast_strlen_zero(p->loginchan)) { - if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0 || !(p->lastdisc.tv_sec)) - snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan); - else - snprintf(location, sizeof(location) - 20, "wrapping up at '%s'", p->loginchan); - talkingto[0] = '\0'; - online_agents++; - if (p->acknowledged) - strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1); } else { strcpy(location, "not logged in"); talkingto[0] = '\0'; @@ -1912,13 +1747,6 @@ strcpy(talkingto, " is idle"); agent_status = 1; online_agents++; - } else if (!ast_strlen_zero(p->loginchan)) { - snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan); - talkingto[0] = '\0'; - agent_status = 1; - online_agents++; - if (p->acknowledged) - strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1); } if (!ast_strlen_zero(p->moh)) snprintf(music, sizeof(music), " (musiconhold is '%s')", p->moh); @@ -1957,7 +1785,7 @@ * \returns * \sa agentmonitoroutgoing_exec(), load_module(). */ -static int login_exec(struct ast_channel *chan, void *data) +static int login_exec(struct ast_channel *chan, const char *data) { int res=0; int tries = 0; @@ -2066,12 +1894,11 @@ /* Set Channel Specific Agent Overrides */ if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) { - if (!strcasecmp(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"), "always")) - p->ackcall = 2; - else if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) + if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) { p->ackcall = 1; - else + } else { p->ackcall = 0; + } tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL"); ast_verb(3, "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n", tmpoptions, p->ackcall, p->agent); ast_set_flag(p, AGENT_FLAG_ACKCALL); @@ -2117,7 +1944,6 @@ long logintime; snprintf(agent, sizeof(agent), "Agent/%s", p->agent); - p->loginchan[0] = '\0'; p->logincallerid[0] = '\0'; p->acknowledged = 0; @@ -2160,10 +1986,11 @@ ast_getformatname(chan->readformat), ast_getformatname(chan->writeformat)); /* Login this channel and wait for it to go away */ p->chan = chan; - if (p->ackcall > 1) + if (p->ackcall) { check_beep(p, 0); - else + } else { check_availability(p, 0); + } ast_mutex_unlock(&p->lock); AST_LIST_UNLOCK(&agents); ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent); @@ -2188,10 +2015,11 @@ ast_debug(1, "Wrapup time for %s expired!\n", p->agent); p->lastdisc = ast_tv(0, 0); ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent); - if (p->ackcall > 1) + if (p->ackcall) { check_beep(p, 0); - else + } else { check_availability(p, 0); + } } } ast_mutex_unlock(&p->lock); @@ -2204,11 +2032,12 @@ ast_mutex_unlock(&p->app_lock); ast_mutex_lock(&p->lock); ast_mutex_unlock(&p->lock); - if (p->ackcall > 1) + if (p->ackcall) { res = agent_ack_sleep(p); - else + } else { res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p ); - if ((p->ackcall > 1) && (res == 1)) { + } + if (p->ackcall && (res == 1)) { AST_LIST_LOCK(&agents); ast_mutex_lock(&p->lock); check_availability(p, 0); @@ -2285,7 +2114,7 @@ * \returns * \sa login_exec(), load_module(). */ -static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data) +static int agentmonitoroutgoing_exec(struct ast_channel *chan, const char *data) { int exitifnoagentid = 0; int nowarnings = 0; @@ -2335,84 +2164,6 @@ return 0; } -/*! - * \brief Dump AgentCallbackLogin agents to the ASTdb database for persistence - */ -static void dump_agents(void) -{ - struct agent_pvt *cur_agent = NULL; - char buf[256]; - - AST_LIST_TRAVERSE(&agents, cur_agent, list) { - if (cur_agent->chan) - continue; - - if (!ast_strlen_zero(cur_agent->loginchan)) { - snprintf(buf, sizeof(buf), "%s;%s", cur_agent->loginchan, cur_agent->logincallerid); - if (ast_db_put(pa_family, cur_agent->agent, buf)) - ast_log(LOG_WARNING, "failed to create persistent entry in ASTdb for %s!\n", buf); - else - ast_debug(1, "Saved Agent: %s on %s\n", cur_agent->agent, cur_agent->loginchan); - } else { - /* Delete - no agent or there is an error */ - ast_db_del(pa_family, cur_agent->agent); - } - } -} - -/*! - * \brief Reload the persistent agents from astdb. - */ -static void reload_agents(void) -{ - char *agent_num; - struct ast_db_entry *db_tree; - struct ast_db_entry *entry; - struct agent_pvt *cur_agent; - char agent_data[256]; - char *parse; - char *agent_chan; - char *agent_callerid; - - db_tree = ast_db_gettree(pa_family, NULL); - - AST_LIST_LOCK(&agents); - for (entry = db_tree; entry; entry = entry->next) { - agent_num = entry->key + strlen(pa_family) + 2; - AST_LIST_TRAVERSE(&agents, cur_agent, list) { - ast_mutex_lock(&cur_agent->lock); - if (strcmp(agent_num, cur_agent->agent) == 0) - break; - ast_mutex_unlock(&cur_agent->lock); - } - if (!cur_agent) { - ast_db_del(pa_family, agent_num); - continue; - } else - ast_mutex_unlock(&cur_agent->lock); - if (!ast_db_get(pa_family, agent_num, agent_data, sizeof(agent_data)-1)) { - ast_debug(1, "Reload Agent from AstDB: %s on %s\n", cur_agent->agent, agent_data); - parse = agent_data; - agent_chan = strsep(&parse, ";"); - agent_callerid = strsep(&parse, ";"); - ast_copy_string(cur_agent->loginchan, agent_chan, sizeof(cur_agent->loginchan)); - if (agent_callerid) { - ast_copy_string(cur_agent->logincallerid, agent_callerid, sizeof(cur_agent->logincallerid)); - set_agentbycallerid(cur_agent->logincallerid, cur_agent->agent); - } else - cur_agent->logincallerid[0] = '\0'; - if (cur_agent->loginstart == 0) - time(&cur_agent->loginstart); - ast_devstate_changed(AST_DEVICE_UNKNOWN, "Agent/%s", cur_agent->agent); - } - } - AST_LIST_UNLOCK(&agents); - if (db_tree) { - ast_log(LOG_NOTICE, "Agents successfully reloaded from database.\n"); - ast_db_freetree(db_tree); - } -} - /*! \brief Part of PBX channel interface */ static int agent_devicestate(void *data) { @@ -2441,7 +2192,7 @@ } else { if (res == AST_DEVICE_BUSY) res = AST_DEVICE_INUSE; - if (p->chan || !ast_strlen_zero(p->loginchan)) { + if (p->chan) { if (res == AST_DEVICE_INVALID) res = AST_DEVICE_UNKNOWN; } else if (res == AST_DEVICE_INVALID) @@ -2506,8 +2257,9 @@ if (!strcasecmp(args.item, "status")) { char *status = "LOGGEDOUT"; - if (agent->chan || !ast_strlen_zero(agent->loginchan)) - status = "LOGGEDIN"; + if (agent->chan) { + status = "LOGGEDIN"; + } ast_copy_string(buf, status, len); } else if (!strcasecmp(args.item, "password")) ast_copy_string(buf, agent->password, len); @@ -2522,15 +2274,16 @@ if (tmp) *tmp = '\0'; } - } else if (!strcasecmp(args.item, "exten")) - ast_copy_string(buf, agent->loginchan, len); + } else if (!strcasecmp(args.item, "exten")) { + buf[0] = '\0'; + } AST_LIST_UNLOCK(&agents); return 0; } -struct ast_custom_function agent_function = { +static struct ast_custom_function agent_function = { .name = "AGENT", .read = function_agent, }; @@ -2553,15 +2306,13 @@ /* Read in the config */ if (!read_agent_config(0)) return AST_MODULE_LOAD_DECLINE; - if (persistent_agents) - reload_agents(); /* Dialplan applications */ ast_register_application_xml(app, login_exec); ast_register_application_xml(app3, agentmonitoroutgoing_exec); /* Manager commands */ - ast_manager_register2("Agents", EVENT_FLAG_AGENT, action_agents, "Lists agents and their status", mandescr_agents); - ast_manager_register2("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff, "Sets an agent as no longer logged in", mandescr_agent_logoff); + ast_manager_register_xml("Agents", EVENT_FLAG_AGENT, action_agents); + ast_manager_register_xml("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff); /* CLI Commands */ ast_cli_register_multiple(cli_agents, ARRAY_LEN(cli_agents)); @@ -2574,11 +2325,7 @@ static int reload(void) { - if (!read_agent_config(1)) { - if (persistent_agents) - reload_agents(); - } - return 0; + return read_agent_config(1); } static int unload_module(void) Index: channels/chan_console.c =================================================================== --- a/channels/chan_console.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/chan_console.c (.../trunk) (revision 202568) @@ -797,6 +797,7 @@ if (pvt->owner) { /* already in a call */ int i; struct ast_frame f = { AST_FRAME_DTMF, 0 }; + const char *s; if (a->argc == e->args) { /* argument is mandatory here */ ast_cli(a->fd, "Already in a call. You can only dial digits until you hangup.\n"); @@ -837,8 +838,7 @@ } else ast_cli(a->fd, "No such extension '%s' in context '%s'\n", mye, myc); - if (s) - free(s); + free(s); unref_pvt(pvt); @@ -883,7 +883,7 @@ static char *cli_console_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - char *s; + const char *s; struct console_pvt *pvt = get_active_pvt(); char *res = CLI_SUCCESS; Index: channels/Makefile =================================================================== --- a/channels/Makefile (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/Makefile (.../trunk) (revision 202568) @@ -69,6 +69,7 @@ rm -f h323/Makefile $(if $(filter chan_iax2,$(EMBEDDED_MODS)),modules.link,chan_iax2.so): iax2-parser.o iax2-provision.o +$(if $(filter chan_dahdi,$(EMBEDDED_MODS)),modules.link,chan_dahdi.so): sig_analog.o ifneq ($(filter chan_h323,$(EMBEDDED_MODS)),) modules.link: h323/libchanh323.a Index: channels/chan_iax2.c =================================================================== --- a/channels/chan_iax2.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/chan_iax2.c (.../trunk) (revision 202568) @@ -88,6 +88,7 @@ #include "asterisk/event.h" #include "asterisk/astobj2.h" #include "asterisk/timing.h" +#include "asterisk/taskprocessor.h" #include "iax2.h" #include "iax2-parser.h" @@ -174,6 +175,47 @@ + + + List IAX peers. + + + + + + + + + + List IAX Peers. + + + + + + List all the IAX peers. + + + + + Show IAX Netstats. + + + + Show IAX channels network statistics. + + + + + Show IAX registrations. + + + + + + Show IAX registrations. + + ***/ /* Define SCHED_MULTITHREADED to run the scheduler in a special @@ -212,10 +254,10 @@ /*! \brief Maximum transmission unit for the UDP packet in the trunk not to be fragmented. This is based on 1516 - ethernet - ip - udp - iax minus one g711 frame = 1240 */ -#define MAX_TRUNK_MTU 1240 +#define MAX_TRUNK_MTU 1240 -static int global_max_trunk_mtu; /*!< Maximum MTU, 0 if not used */ -static int trunk_timed, trunk_untimed, trunk_maxmtu, trunk_nmaxmtu ; /*!< Trunk MTU statistics */ +static int global_max_trunk_mtu; /*!< Maximum MTU, 0 if not used */ +static int trunk_timed, trunk_untimed, trunk_maxmtu, trunk_nmaxmtu ; /*!< Trunk MTU statistics */ #define DEFAULT_CONTEXT "default" @@ -263,27 +305,27 @@ static struct ast_netsock_list *outsock; /*!< used if sourceaddress specified and bindaddr == INADDR_ANY */ static int defaultsockfd = -1; -int (*iax2_regfunk)(const char *username, int onoff) = NULL; +static int (*iax2_regfunk)(const char *username, int onoff) = NULL; /* Ethernet, etc */ -#define IAX_CAPABILITY_FULLBANDWIDTH 0xFFFF +#define IAX_CAPABILITY_FULLBANDWIDTH 0xFFFF /* T1, maybe ISDN */ -#define IAX_CAPABILITY_MEDBANDWIDTH (IAX_CAPABILITY_FULLBANDWIDTH & \ - ~AST_FORMAT_SLINEAR & \ - ~AST_FORMAT_SLINEAR16 & \ - ~AST_FORMAT_SIREN7 & \ - ~AST_FORMAT_SIREN14 & \ - ~AST_FORMAT_ULAW & \ - ~AST_FORMAT_ALAW & \ - ~AST_FORMAT_G722) +#define IAX_CAPABILITY_MEDBANDWIDTH (IAX_CAPABILITY_FULLBANDWIDTH & \ + ~AST_FORMAT_SLINEAR & \ + ~AST_FORMAT_SLINEAR16 & \ + ~AST_FORMAT_SIREN7 & \ + ~AST_FORMAT_SIREN14 & \ + ~AST_FORMAT_ULAW & \ + ~AST_FORMAT_ALAW & \ + ~AST_FORMAT_G722) /* A modem */ -#define IAX_CAPABILITY_LOWBANDWIDTH (IAX_CAPABILITY_MEDBANDWIDTH & \ - ~AST_FORMAT_G726 & \ - ~AST_FORMAT_G726_AAL2 & \ - ~AST_FORMAT_ADPCM) +#define IAX_CAPABILITY_LOWBANDWIDTH (IAX_CAPABILITY_MEDBANDWIDTH & \ + ~AST_FORMAT_G726 & \ + ~AST_FORMAT_G726_AAL2 & \ + ~AST_FORMAT_ADPCM) -#define IAX_CAPABILITY_LOWFREE (IAX_CAPABILITY_LOWBANDWIDTH & \ - ~AST_FORMAT_G723_1) +#define IAX_CAPABILITY_LOWFREE (IAX_CAPABILITY_LOWBANDWIDTH & \ + ~AST_FORMAT_G723_1) #define DEFAULT_MAXMS 2000 /* Must be faster than 2 seconds by default */ @@ -292,7 +334,7 @@ /* if a pvt has encryption setup done and is running on the call */ #define IAX_CALLENCRYPTED(pvt) \ - (ast_test_flag(pvt, IAX_ENCRYPTED) && ast_test_flag(pvt, IAX_KEYPOPULATED)) + (ast_test_flag64(pvt, IAX_ENCRYPTED) && ast_test_flag64(pvt, IAX_KEYPOPULATED)) #define IAX_DEBUGDIGEST(msg, key) do { \ int idx; \ @@ -332,7 +374,7 @@ static int delayreject = 0; static int iax2_encryption = 0; -static struct ast_flags globalflags = { 0 }; +static struct ast_flags64 globalflags = { 0 }; static pthread_t netthreadid = AST_PTHREADT_NULL; @@ -347,40 +389,39 @@ struct iax2_context *next; }; -enum iax2_flags { - IAX_HASCALLERID = (1 << 0), /*!< CallerID has been specified */ - IAX_DELME = (1 << 1), /*!< Needs to be deleted */ - IAX_TEMPONLY = (1 << 2), /*!< Temporary (realtime) */ - IAX_TRUNK = (1 << 3), /*!< Treat as a trunk */ - IAX_NOTRANSFER = (1 << 4), /*!< Don't native bridge */ - IAX_USEJITTERBUF = (1 << 5), /*!< Use jitter buffer */ - IAX_DYNAMIC = (1 << 6), /*!< dynamic peer */ - IAX_SENDANI = (1 << 7), /*!< Send ANI along with CallerID */ - /* (1 << 8) is currently unused due to the deprecation of an old option. Go ahead, take it! */ - IAX_ALREADYGONE = (1 << 9), /*!< Already disconnected */ - IAX_PROVISION = (1 << 10), /*!< This is a provisioning request */ - IAX_QUELCH = (1 << 11), /*!< Whether or not we quelch audio */ - IAX_ENCRYPTED = (1 << 12), /*!< Whether we should assume encrypted tx/rx */ - IAX_KEYPOPULATED = (1 << 13), /*!< Whether we have a key populated */ - IAX_CODEC_USER_FIRST = (1 << 14), /*!< are we willing to let the other guy choose the codec? */ - IAX_CODEC_NOPREFS = (1 << 15), /*!< Force old behaviour by turning off prefs */ - IAX_CODEC_NOCAP = (1 << 16), /*!< only consider requested format and ignore capabilities*/ - IAX_RTCACHEFRIENDS = (1 << 17), /*!< let realtime stay till your reload */ - IAX_RTUPDATE = (1 << 18), /*!< Send a realtime update */ - IAX_RTAUTOCLEAR = (1 << 19), /*!< erase me on expire */ - IAX_FORCEJITTERBUF = (1 << 20), /*!< Force jitterbuffer, even when bridged to a channel that can take jitter */ - IAX_RTIGNOREREGEXPIRE = (1 << 21), /*!< When using realtime, ignore registration expiration */ - IAX_TRUNKTIMESTAMPS = (1 << 22), /*!< Send trunk timestamps */ - IAX_TRANSFERMEDIA = (1 << 23), /*!< When doing IAX2 transfers, transfer media only */ - IAX_MAXAUTHREQ = (1 << 24), /*!< Maximum outstanding AUTHREQ restriction is in place */ - IAX_DELAYPBXSTART = (1 << 25), /*!< Don't start a PBX on the channel until the peer sends us a - response, so that we've achieved a three-way handshake with - them before sending voice or anything else*/ - IAX_ALLOWFWDOWNLOAD = (1 << 26), /*!< Allow the FWDOWNL command? */ - IAX_IMMEDIATE = (1 << 27), /*!< Allow immediate off-hook to extension s */ - IAX_FORCE_ENCRYPT = (1 << 28), /*!< Forces call encryption, if encryption not possible hangup */ -}; +#define IAX_HASCALLERID (uint64_t)(1 << 0) /*!< CallerID has been specified */ +#define IAX_DELME (uint64_t)(1 << 1) /*!< Needs to be deleted */ +#define IAX_TEMPONLY (uint64_t)(1 << 2) /*!< Temporary (realtime) */ +#define IAX_TRUNK (uint64_t)(1 << 3) /*!< Treat as a trunk */ +#define IAX_NOTRANSFER (uint64_t)(1 << 4) /*!< Don't native bridge */ +#define IAX_USEJITTERBUF (uint64_t)(1 << 5) /*!< Use jitter buffer */ +#define IAX_DYNAMIC (uint64_t)(1 << 6) /*!< dynamic peer */ +#define IAX_SENDANI (uint64_t)(1 << 7) /*!< Send ANI along with CallerID */ +#define IAX_RTSAVE_SYSNAME (uint64_t)(1 << 8) /*!< Save Systname on Realtime Updates */ +#define IAX_ALREADYGONE (uint64_t)(1 << 9) /*!< Already disconnected */ +#define IAX_PROVISION (uint64_t)(1 << 10) /*!< This is a provisioning request */ +#define IAX_QUELCH (uint64_t)(1 << 11) /*!< Whether or not we quelch audio */ +#define IAX_ENCRYPTED (uint64_t)(1 << 12) /*!< Whether we should assume encrypted tx/rx */ +#define IAX_KEYPOPULATED (uint64_t)(1 << 13) /*!< Whether we have a key populated */ +#define IAX_CODEC_USER_FIRST (uint64_t)(1 << 14) /*!< are we willing to let the other guy choose the codec? */ +#define IAX_CODEC_NOPREFS (uint64_t)(1 << 15) /*!< Force old behaviour by turning off prefs */ +#define IAX_CODEC_NOCAP (uint64_t)(1 << 16) /*!< only consider requested format and ignore capabilities*/ +#define IAX_RTCACHEFRIENDS (uint64_t)(1 << 17) /*!< let realtime stay till your reload */ +#define IAX_RTUPDATE (uint64_t)(1 << 18) /*!< Send a realtime update */ +#define IAX_RTAUTOCLEAR (uint64_t)(1 << 19) /*!< erase me on expire */ +#define IAX_FORCEJITTERBUF (uint64_t)(1 << 20) /*!< Force jitterbuffer, even when bridged to a channel that can take jitter */ +#define IAX_RTIGNOREREGEXPIRE (uint64_t)(1 << 21) /*!< When using realtime, ignore registration expiration */ +#define IAX_TRUNKTIMESTAMPS (uint64_t)(1 << 22) /*!< Send trunk timestamps */ +#define IAX_TRANSFERMEDIA (uint64_t)(1 << 23) /*!< When doing IAX2 transfers, transfer media only */ +#define IAX_MAXAUTHREQ (uint64_t)(1 << 24) /*!< Maximum outstanding AUTHREQ restriction is in place */ +#define IAX_DELAYPBXSTART (uint64_t)(1 << 25) /*!< Don't start a PBX on the channel until the peer sends us a response, so that we've achieved a three-way handshake with them before sending voice or anything else */ +#define IAX_ALLOWFWDOWNLOAD (uint64_t)(1 << 26) /*!< Allow the FWDOWNL command? */ +#define IAX_IMMEDIATE (uint64_t)(1 << 27) /*!< Allow immediate off-hook to extension s */ +#define IAX_SENDCONNECTEDLINE (uint64_t)(1 << 28) /*!< Allow sending of connected line updates */ +#define IAX_RECVCONNECTEDLINE (uint64_t)(1 << 29) /*!< Allow receiving of connected line updates */ +#define IAX_FORCE_ENCRYPT (uint64_t)(1 << 30) /*!< Forces call encryption, if encryption not possible hangup */ + static int global_rtautoclear = 120; static int reload_config(void); @@ -399,12 +440,12 @@ AST_STRING_FIELD(cid_name); AST_STRING_FIELD(parkinglot); /*!< Default parkinglot for device */ ); - + int authmethods; int encmethods; int amaflags; int adsi; - unsigned int flags; + uint64_t flags; int capability; int maxauthreq; /*!< Maximum allowed outstanding AUTHREQs */ int curauthreq; /*!< Current number of outstanding AUTHREQs */ @@ -442,7 +483,7 @@ int sockfd; /*!< Socket to use for transmission */ struct in_addr mask; int adsi; - unsigned int flags; + uint64_t flags; /* Dynamic Registration fields */ struct sockaddr_in defaddr; /*!< Default address if there is one */ @@ -543,10 +584,10 @@ /* Don't retry more frequently than every 10 ms, or less frequently than every 5 seconds */ #define MIN_RETRY_TIME 100 -#define MAX_RETRY_TIME 10000 +#define MAX_RETRY_TIME 10000 -#define MAX_JITTER_BUFFER 50 -#define MIN_JITTER_BUFFER 10 +#define MAX_JITTER_BUFFER 50 +#define MIN_JITTER_BUFFER 10 #define DEFAULT_TRUNKDATA 640 * 10 /*!< 40ms, uncompressed linear * 10 channels */ @@ -632,7 +673,7 @@ /*! The jitterbuffer */ jitterbuf *jb; /*! active jb read scheduler id */ - int jbid; + int jbid; /*! LAG */ int lag; /*! Error, as discovered by the manager */ @@ -714,7 +755,7 @@ /*! Associated peer for poking */ struct iax2_peer *peerpoke; /*! IAX_ flags */ - unsigned int flags; + uint64_t flags; int adsi; /*! Transferring status */ @@ -733,7 +774,7 @@ /*! Who we are bridged to */ unsigned short bridgecallno; - + int pingid; /*!< Transmit PING request */ int lagid; /*!< Retransmit lag request */ int autoid; /*!< Auto hangup for Dialplan requestor */ @@ -765,9 +806,13 @@ * \note The contents of this list do not need to be explicitly destroyed * on module unload. This is because all active calls are destroyed, and * all frames in this queue will get destroyed as a part of that process. + * + * \note Contents protected by the iaxsl[] locks */ -static AST_LIST_HEAD_STATIC(frame_queue, iax_frame); +static AST_LIST_HEAD_NOLOCK(, iax_frame) frame_queue[IAX_MAX_CALLS]; +static struct ast_taskprocessor *transmit_processor; + /*! * This module will get much higher performance when doing a lot of * user and peer lookups if the number of buckets is increased from 1. @@ -824,7 +869,7 @@ static struct iax2_peer *realtime_peer(const char *peername, struct sockaddr_in *sin); static int ast_cli_netstats(struct mansession *s, int fd, int limit_fmt); -static char *complete_iax2_peers(const char *line, const char *word, int pos, int state, int flags); +static char *complete_iax2_peers(const char *line, const char *word, int pos, int state, uint64_t flags); static char *complete_iax2_unregister(const char *line, const char *word, int pos, int state); enum iax2_thread_iostate { @@ -1045,7 +1090,7 @@ static int iax2_hangup(struct ast_channel *c); static int iax2_indicate(struct ast_channel *c, int condition, const void *data, size_t datalen); static int iax2_poke_peer(struct iax2_peer *peer, int heldcall); -static int iax2_provision(struct sockaddr_in *end, int sockfd, char *dest, const char *template, int force); +static int iax2_provision(struct sockaddr_in *end, int sockfd, const char *dest, const char *template, int force); static int iax2_send(struct chan_iax2_pvt *pvt, struct ast_frame *f, unsigned int ts, int seqno, int now, int transfer, int final); static int iax2_sendhtml(struct ast_channel *c, int subclass, const char *data, int datalen); static int iax2_sendimage(struct ast_channel *c, struct ast_frame *img); @@ -1545,7 +1590,7 @@ static void iax2_destroy_helper(struct chan_iax2_pvt *pvt) { /* Decrement AUTHREQ count if needed */ - if (ast_test_flag(pvt, IAX_MAXAUTHREQ)) { + if (ast_test_flag64(pvt, IAX_MAXAUTHREQ)) { struct iax2_user *user; struct iax2_user tmp_user = { .name = pvt->username, @@ -1554,10 +1599,10 @@ user = ao2_find(users, &tmp_user, OBJ_POINTER); if (user) { ast_atomic_fetchadd_int(&user->curauthreq, -1); - user_unref(user); + user_unref(user); } - ast_clear_flag(pvt, IAX_MAXAUTHREQ); + ast_clear_flag64(pvt, IAX_MAXAUTHREQ); } /* No more pings or lagrq's */ AST_SCHED_DEL_SPINLOCK(ast_sched_thread_get_context(sched), pvt->pingid, &iaxsl[pvt->callno]); @@ -1595,21 +1640,19 @@ struct iax_frame *cur = NULL; ast_mutex_lock(&iaxsl[pvt->callno]); + iax2_destroy_helper(pvt); - ast_mutex_unlock(&iaxsl[pvt->callno]); /* Already gone */ - ast_set_flag(pvt, IAX_ALREADYGONE); + ast_set_flag64(pvt, IAX_ALREADYGONE); - AST_LIST_LOCK(&frame_queue); - AST_LIST_TRAVERSE(&frame_queue, cur, list) { + AST_LIST_TRAVERSE(&frame_queue[pvt->callno], cur, list) { /* Cancel any pending transmissions */ - if (cur->callno == pvt->callno) { - cur->retries = -1; - } + cur->retries = -1; } - AST_LIST_UNLOCK(&frame_queue); + ast_mutex_unlock(&iaxsl[pvt->callno]); + if (pvt->reg) { pvt->reg->callno = 0; } @@ -1954,7 +1997,7 @@ break; } ast_mutex_unlock(&iaxsl[x]); - + if (x == start - 1) { break; } @@ -1980,7 +2023,7 @@ iaxs[x]->pingid = iax2_sched_add(sched, ping_time * 1000, send_ping, (void *)(long)x); iaxs[x]->lagid = iax2_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)(long)x); iaxs[x]->amaflags = amaflags; - ast_copy_flags(iaxs[x], &globalflags, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_FORCE_ENCRYPT); + ast_copy_flags64(iaxs[x], &globalflags, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE | IAX_FORCE_ENCRYPT); ast_string_field_set(iaxs[x], accountcode, accountcode); ast_string_field_set(iaxs[x], mohinterpret, mohinterpret); ast_string_field_set(iaxs[x], mohsuggest, mohsuggest); @@ -2202,7 +2245,7 @@ return -1; } fwh = (struct ast_iax2_firmware_header*)mmap(NULL, stbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (fwh == (void *) -1) { + if (fwh == MAP_FAILED) { ast_log(LOG_WARNING, "mmap failed: %s\n", strerror(errno)); close(fd); return -1; @@ -2367,7 +2410,7 @@ struct iax_frame *fr = data; fr->retrans = -1; ast_clear_flag(&fr->af, AST_FRFLAG_HAS_TIMING_INFO); - if (iaxs[fr->callno] && !ast_test_flag(iaxs[fr->callno], IAX_ALREADYGONE)) + if (iaxs[fr->callno] && !ast_test_flag64(iaxs[fr->callno], IAX_ALREADYGONE)) iax2_queue_frame(fr->callno, &fr->af); /* Free our iax frame */ iax2_frame_free(fr); @@ -2466,9 +2509,9 @@ if (!pvt) return -1; - if (!ast_test_flag(pvt, IAX_ALREADYGONE)) { + if (!ast_test_flag64(pvt, IAX_ALREADYGONE)) { iax2_destroy_helper(pvt); - ast_set_flag(pvt, IAX_ALREADYGONE); + ast_set_flag64(pvt, IAX_ALREADYGONE); } if ((c = pvt->owner)) { @@ -2630,17 +2673,16 @@ f->retries = -1; freeme = 1; } - if (callno) - ast_mutex_unlock(&iaxsl[callno]); - /* Do not try again */ + if (freeme) { /* Don't attempt delivery, just remove it from the queue */ - AST_LIST_LOCK(&frame_queue); - AST_LIST_REMOVE(&frame_queue, f, list); - AST_LIST_UNLOCK(&frame_queue); + AST_LIST_REMOVE(&frame_queue[callno], f, list); + ast_mutex_unlock(&iaxsl[callno]); f->retrans = -1; /* Free the IAX frame */ iax2_frame_free(f); + } else if (callno) { + ast_mutex_unlock(&iaxsl[callno]); } } @@ -2657,7 +2699,7 @@ { struct iax2_peer *peer = NULL; struct iax2_user *user = NULL; - static char *choices[] = { "all", NULL }; + static const char * const choices[] = { "all", NULL }; char *cmplt; switch (cmd) { @@ -2688,8 +2730,8 @@ user = find_user(a->argv[3]); if (peer || user) { if (peer) { - if (ast_test_flag(peer, IAX_RTCACHEFRIENDS)) { - ast_set_flag(peer, IAX_RTAUTOCLEAR); + if (ast_test_flag64(peer, IAX_RTCACHEFRIENDS)) { + ast_set_flag64(peer, IAX_RTAUTOCLEAR); expire_registry(peer_ref(peer)); ast_cli(a->fd, "Peer %s was removed from the cache.\n", a->argv[3]); } else { @@ -2698,8 +2740,8 @@ peer_unref(peer); } if (user) { - if (ast_test_flag(user, IAX_RTCACHEFRIENDS)) { - ast_set_flag(user, IAX_RTAUTOCLEAR); + if (ast_test_flag64(user, IAX_RTCACHEFRIENDS)) { + ast_set_flag64(user, IAX_RTAUTOCLEAR); ast_cli(a->fd, "User %s was removed from the cache.\n", a->argv[3]); } else { ast_cli(a->fd, "User %s is not eligible for this operation.\n", a->argv[3]); @@ -2864,8 +2906,8 @@ ast_cli(a->fd, " Context : %s\n", peer->context); ast_cli(a->fd, " Parking lot : %s\n", peer->parkinglot); ast_cli(a->fd, " Mailbox : %s\n", peer->mailbox); - ast_cli(a->fd, " Dynamic : %s\n", ast_test_flag(peer, IAX_DYNAMIC) ? "Yes" : "No"); - ast_cli(a->fd, " Trunk : %s\n", ast_test_flag(peer, IAX_TRUNK) ? "Yes" : "No"); + ast_cli(a->fd, " Dynamic : %s\n", ast_test_flag64(peer, IAX_DYNAMIC) ? "Yes" : "No"); + ast_cli(a->fd, " Trunk : %s\n", ast_test_flag64(peer, IAX_TRUNK) ? "Yes" : "No"); ast_cli(a->fd, " Encryption : %s\n", peer->encmethods ? ast_str_buffer(encmethods) : "No"); ast_cli(a->fd, " Callerid : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), peer->cid_name, peer->cid_num, "")); ast_cli(a->fd, " Expire : %d\n", peer->expire); @@ -2905,7 +2947,7 @@ return CLI_SUCCESS; } -static char *complete_iax2_peers(const char *line, const char *word, int pos, int state, int flags) +static char *complete_iax2_peers(const char *line, const char *word, int pos, int state, uint64_t flags) { int which = 0; struct iax2_peer *peer; @@ -2916,7 +2958,7 @@ i = ao2_iterator_init(peers, 0); while ((peer = ao2_iterator_next(&i))) { if (!strncasecmp(peer->name, word, wordlen) && ++which > state - && (!flags || ast_test_flag(peer, flags))) { + && (!flags || ast_test_flag64(peer, flags))) { res = ast_strdup(peer->name); peer_unref(peer); break; @@ -2930,7 +2972,7 @@ static char *handle_cli_iax2_show_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct iax_frame *cur; - int cnt = 0, dead = 0, final = 0; + int cnt = 0, dead = 0, final = 0, i = 0; switch (cmd) { case CLI_INIT: @@ -2946,15 +2988,17 @@ if (a->argc != 3) return CLI_SHOWUSAGE; - AST_LIST_LOCK(&frame_queue); - AST_LIST_TRAVERSE(&frame_queue, cur, list) { - if (cur->retries < 0) - dead++; - if (cur->final) - final++; - cnt++; + for (i = 0; i < ARRAY_LEN(frame_queue); i++) { + ast_mutex_lock(&iaxsl[i]); + AST_LIST_TRAVERSE(&frame_queue[i], cur, list) { + if (cur->retries < 0) + dead++; + if (cur->final) + final++; + cnt++; + } + ast_mutex_unlock(&iaxsl[i]); } - AST_LIST_UNLOCK(&frame_queue); ast_cli(a->fd, " IAX Statistics\n"); ast_cli(a->fd, "---------------------\n"); @@ -3188,7 +3232,7 @@ /* queue the frame: For consistency, we would call __do_deliver here, but __do_deliver wants an iax_frame, * which we'd need to malloc, and then it would free it. That seems like a drag */ - if (!ast_test_flag(iaxs[callno], IAX_ALREADYGONE)) { + if (!ast_test_flag64(iaxs[callno], IAX_ALREADYGONE)) { iax2_queue_frame(callno, &af); /* iax2_queue_frame() could cause the call to disappear */ pvt = iaxs[callno]; @@ -3258,7 +3302,7 @@ type = JB_TYPE_SILENCE; } - if ( (!ast_test_flag(iaxs[fr->callno], IAX_USEJITTERBUF)) ) { + if ( (!ast_test_flag64(iaxs[fr->callno], IAX_USEJITTERBUF)) ) { if (tsout) *tsout = fr->ts; __do_deliver(fr); @@ -3270,7 +3314,7 @@ /* if the user hasn't requested we force the use of the jitterbuffer, and we're bridged to * a channel that can accept jitter, then flush and suspend the jb, and send this frame straight through */ - if ( (!ast_test_flag(iaxs[fr->callno], IAX_FORCEJITTERBUF)) && owner && bridge && (bridge->tech->properties & AST_CHAN_TP_WANTSJITTER) ) { + if ( (!ast_test_flag64(iaxs[fr->callno], IAX_FORCEJITTERBUF)) && owner && bridge && (bridge->tech->properties & AST_CHAN_TP_WANTSJITTER) ) { jb_frame frame; /* deliver any frames in the jb */ @@ -3311,23 +3355,39 @@ return 0; } -static int iax2_transmit(struct iax_frame *fr) +static int transmit_frame(void *data) { - /* Lock the queue and place this packet at the end */ - /* By setting this to 0, the network thread will send it for us, and - queue retransmission if necessary */ - fr->sentyet = 0; - AST_LIST_LOCK(&frame_queue); - AST_LIST_INSERT_TAIL(&frame_queue, fr, list); - AST_LIST_UNLOCK(&frame_queue); - /* Wake up the network and scheduler thread */ - if (netthreadid != AST_PTHREADT_NULL) - pthread_kill(netthreadid, SIGURG); - ast_sched_thread_poke(sched); + struct iax_frame *fr = data; + + ast_mutex_lock(&iaxsl[fr->callno]); + + fr->sentyet = 1; + + if (iaxs[fr->callno]) { + send_packet(fr); + } + + if (fr->retries < 0) { + ast_mutex_unlock(&iaxsl[fr->callno]); + /* No retransmit requested */ + iax_frame_free(fr); + } else { + /* We need reliable delivery. Schedule a retransmission */ + AST_LIST_INSERT_TAIL(&frame_queue[fr->callno], fr, list); + ast_mutex_unlock(&iaxsl[fr->callno]); + fr->retries++; + fr->retrans = iax2_sched_add(sched, fr->retrytime, attempt_transmit, fr); + } + return 0; } +static int iax2_transmit(struct iax_frame *fr) +{ + fr->sentyet = 0; + return ast_taskprocessor_push(transmit_processor, transmit_frame, fr); +} static int iax2_digit_begin(struct ast_channel *c, char digit) { @@ -3422,8 +3482,8 @@ if (!var) return NULL; - peer = build_peer(peername, var, NULL, ast_test_flag((&globalflags), IAX_RTCACHEFRIENDS) ? 0 : 1); - + peer = build_peer(peername, var, NULL, ast_test_flag64((&globalflags), IAX_RTCACHEFRIENDS) ? 0 : 1); + if (!peer) { ast_variables_destroy(var); return NULL; @@ -3455,27 +3515,27 @@ if (!peer) return NULL; - if (ast_test_flag((&globalflags), IAX_RTCACHEFRIENDS)) { - ast_copy_flags(peer, &globalflags, IAX_RTAUTOCLEAR|IAX_RTCACHEFRIENDS); - if (ast_test_flag(peer, IAX_RTAUTOCLEAR)) { - if (peer->expire > -1) { - if (!ast_sched_thread_del(sched, peer->expire)) { - peer->expire = -1; - peer_unref(peer); - } - } - peer->expire = iax2_sched_add(sched, (global_rtautoclear) * 1000, expire_registry, peer_ref(peer)); - if (peer->expire == -1) - peer_unref(peer); + if (ast_test_flag64((&globalflags), IAX_RTCACHEFRIENDS)) { + ast_copy_flags64(peer, &globalflags, IAX_RTAUTOCLEAR|IAX_RTCACHEFRIENDS); + if (ast_test_flag64(peer, IAX_RTAUTOCLEAR)) { + if (peer->expire > -1) { + if (!ast_sched_thread_del(sched, peer->expire)) { + peer->expire = -1; + peer_unref(peer); + } + } + peer->expire = iax2_sched_add(sched, (global_rtautoclear) * 1000, expire_registry, peer_ref(peer)); + if (peer->expire == -1) + peer_unref(peer); } ao2_link(peers, peer); - if (ast_test_flag(peer, IAX_DYNAMIC)) + if (ast_test_flag64(peer, IAX_DYNAMIC)) reg_source_db(peer); } else { - ast_set_flag(peer, IAX_TEMPONLY); + ast_set_flag64(peer, IAX_TEMPONLY); } - if (!ast_test_flag(&globalflags, IAX_RTIGNOREREGEXPIRE) && dynamic) { + if (!ast_test_flag64(&globalflags, IAX_RTIGNOREREGEXPIRE) && dynamic) { time(&nowtime); if ((nowtime - regseconds) > IAX_DEFAULT_REG_EXPIRE) { memset(&peer->addr, 0, sizeof(peer->addr)); @@ -3546,18 +3606,18 @@ tmp = tmp->next; } - user = build_user(username, var, NULL, !ast_test_flag((&globalflags), IAX_RTCACHEFRIENDS)); + user = build_user(username, var, NULL, !ast_test_flag64((&globalflags), IAX_RTCACHEFRIENDS)); ast_variables_destroy(var); if (!user) return NULL; - if (ast_test_flag((&globalflags), IAX_RTCACHEFRIENDS)) { - ast_set_flag(user, IAX_RTCACHEFRIENDS); + if (ast_test_flag64((&globalflags), IAX_RTCACHEFRIENDS)) { + ast_set_flag64(user, IAX_RTCACHEFRIENDS); ao2_link(users, user); } else { - ast_set_flag(user, IAX_TEMPONLY); + ast_set_flag64(user, IAX_TEMPONLY); } return user; @@ -3567,17 +3627,24 @@ { char port[10]; char regseconds[20]; - + const char *sysname = ast_config_AST_SYSTEM_NAME; + char *syslabel = NULL; + + if (ast_strlen_zero(sysname)) /* No system name, disable this */ + sysname = NULL; + else if (ast_test_flag64(&globalflags, IAX_RTSAVE_SYSNAME)) + syslabel = "regserver"; + snprintf(regseconds, sizeof(regseconds), "%d", (int)regtime); snprintf(port, sizeof(port), "%d", ntohs(sin->sin_port)); ast_update_realtime("iaxpeers", "name", peername, "ipaddr", ast_inet_ntoa(sin->sin_addr), "port", port, - "regseconds", regseconds, SENTINEL); + "regseconds", regseconds, syslabel, sysname, SENTINEL); /* note syslable can be NULL */ } struct create_addr_info { int capability; - unsigned int flags; + uint64_t flags; int maxtime; int encmethods; int found; @@ -3588,6 +3655,8 @@ char outkey[80]; char timezone[80]; char prefs[32]; + char cid_num[80]; + char cid_name[80]; char context[AST_MAX_CONTEXT]; char peercontext[AST_MAX_CONTEXT]; char mohinterpret[MAX_MUSICCLASS]; @@ -3600,7 +3669,7 @@ int res = -1; struct ast_codec_pref ourprefs; - ast_clear_flag(cai, IAX_SENDANI | IAX_TRUNK); + ast_clear_flag64(cai, IAX_SENDANI | IAX_TRUNK); cai->sockfd = defaultsockfd; cai->maxtime = 0; sin->sin_family = AF_INET; @@ -3631,7 +3700,7 @@ if (peer->maxms && ((peer->lastms > peer->maxms) || (peer->lastms < 0))) goto return_unref; - ast_copy_flags(cai, peer, IAX_SENDANI | IAX_TRUNK | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_FORCE_ENCRYPT); + ast_copy_flags64(cai, peer, IAX_SENDANI | IAX_TRUNK | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE | IAX_FORCE_ENCRYPT); cai->maxtime = peer->maxms; cai->capability = peer->capability; cai->encmethods = peer->encmethods; @@ -3649,6 +3718,8 @@ ast_copy_string(cai->username, peer->username, sizeof(cai->username)); ast_copy_string(cai->timezone, peer->zonetag, sizeof(cai->timezone)); ast_copy_string(cai->outkey, peer->outkey, sizeof(cai->outkey)); + ast_copy_string(cai->cid_num, peer->cid_num, sizeof(cai->cid_num)); + ast_copy_string(cai->cid_name, peer->cid_name, sizeof(cai->cid_name)); ast_copy_string(cai->mohinterpret, peer->mohinterpret, sizeof(cai->mohinterpret)); ast_copy_string(cai->mohsuggest, peer->mohsuggest, sizeof(cai->mohsuggest)); if (ast_strlen_zero(peer->dbsecret)) { @@ -3838,7 +3909,7 @@ ast_log(LOG_WARNING, "No address associated with '%s'\n", pds.peer); return -1; } - if (ast_strlen_zero(cai.secret) && ast_test_flag(iaxs[callno], IAX_FORCE_ENCRYPT)) { + if (ast_strlen_zero(cai.secret) && ast_test_flag64(iaxs[callno], IAX_FORCE_ENCRYPT)) { ast_log(LOG_WARNING, "Call terminated. No secret given and force encrypt enabled\n"); return -1; } @@ -3857,8 +3928,8 @@ if (pds.port) sin.sin_port = htons(atoi(pds.port)); - l = c->cid.cid_num; - n = c->cid.cid_name; + l = c->connected.id.number; + n = c->connected.id.name; /* Now build request */ memset(&ied, 0, sizeof(ied)); @@ -3875,21 +3946,21 @@ if (l) { iax_ie_append_str(&ied, IAX_IE_CALLING_NUMBER, l); - iax_ie_append_byte(&ied, IAX_IE_CALLINGPRES, c->cid.cid_pres); + iax_ie_append_byte(&ied, IAX_IE_CALLINGPRES, c->connected.id.number_presentation); } else { if (n) - iax_ie_append_byte(&ied, IAX_IE_CALLINGPRES, c->cid.cid_pres); + iax_ie_append_byte(&ied, IAX_IE_CALLINGPRES, c->connected.id.number_presentation); else iax_ie_append_byte(&ied, IAX_IE_CALLINGPRES, AST_PRES_NUMBER_NOT_AVAILABLE); } - iax_ie_append_byte(&ied, IAX_IE_CALLINGTON, c->cid.cid_ton); + iax_ie_append_byte(&ied, IAX_IE_CALLINGTON, c->connected.id.number_type); iax_ie_append_short(&ied, IAX_IE_CALLINGTNS, c->cid.cid_tns); if (n) iax_ie_append_str(&ied, IAX_IE_CALLING_NAME, n); - if (ast_test_flag(iaxs[callno], IAX_SENDANI) && c->cid.cid_ani) - iax_ie_append_str(&ied, IAX_IE_CALLING_ANI, c->cid.cid_ani); + if (ast_test_flag64(iaxs[callno], IAX_SENDANI) && c->connected.ani) + iax_ie_append_str(&ied, IAX_IE_CALLING_ANI, c->connected.ani); if (!ast_strlen_zero(c->language)) iax_ie_append_str(&ied, IAX_IE_LANGUAGE, c->language); @@ -3999,7 +4070,7 @@ ast_mutex_lock(&iaxsl[callno]); if (callno && iaxs[callno]) { ast_debug(1, "We're hanging up %s now...\n", c->name); - alreadygone = ast_test_flag(iaxs[callno], IAX_ALREADYGONE); + alreadygone = ast_test_flag64(iaxs[callno], IAX_ALREADYGONE); /* Send the hangup unless we have had a transmission error or are already gone */ iax_ie_append_byte(&ied, IAX_IE_CAUSECODE, (unsigned char)c->hangupcause); if (!iaxs[callno]->error && !alreadygone) { @@ -4068,6 +4139,10 @@ /* these two cannot be sent, because they require a result */ errno = ENOSYS; return -1; + case AST_OPTION_FORMAT_READ: + case AST_OPTION_FORMAT_WRITE: + case AST_OPTION_MAKE_COMPATIBLE: + return -1; case AST_OPTION_OPRMODE: errno = EINVAL; return -1; @@ -4150,8 +4225,8 @@ if (IAX_CALLENCRYPTED(iaxs[callno0]) || IAX_CALLENCRYPTED(iaxs[callno1])) { ast_debug(1, "transfers are not supported for encrypted calls at this time"); - ast_set_flag(iaxs[callno0], IAX_NOTRANSFER); - ast_set_flag(iaxs[callno1], IAX_NOTRANSFER); + ast_set_flag64(iaxs[callno0], IAX_NOTRANSFER); + ast_set_flag64(iaxs[callno1], IAX_NOTRANSFER); return 0; } @@ -4257,10 +4332,10 @@ return AST_BRIDGE_FAILED_NOWARN; } /* check if transfered and if we really want native bridging */ - if (!transferstarted && !ast_test_flag(iaxs[callno0], IAX_NOTRANSFER) && !ast_test_flag(iaxs[callno1], IAX_NOTRANSFER)) { + if (!transferstarted && !ast_test_flag64(iaxs[callno0], IAX_NOTRANSFER) && !ast_test_flag64(iaxs[callno1], IAX_NOTRANSFER)) { /* Try the transfer */ if (iax2_start_transfer(callno0, callno1, (flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1)) || - ast_test_flag(iaxs[callno0], IAX_TRANSFERMEDIA) | ast_test_flag(iaxs[callno1], IAX_TRANSFERMEDIA))) + ast_test_flag64(iaxs[callno0], IAX_TRANSFERMEDIA) | ast_test_flag64(iaxs[callno1], IAX_TRANSFERMEDIA))) ast_log(LOG_WARNING, "Unable to start the transfer\n"); transferstarted = 1; } @@ -4385,6 +4460,11 @@ ast_moh_stop(c); goto done; } + break; + case AST_CONTROL_CONNECTED_LINE: + if (!ast_test_flag64(pvt, IAX_SENDCONNECTEDLINE)) + goto done; + break; } res = send_command(pvt, AST_FRAME_CONTROL, condition, 0, data, datalen, -1); @@ -4400,6 +4480,7 @@ unsigned short callno = PTR_TO_CALLNO(c->tech_pvt); struct iax_ie_data ied = { "", }; char tmp[256], *context; + enum ast_control_transfer message = AST_TRANSFER_SUCCESS; ast_copy_string(tmp, dest, sizeof(tmp)); context = strchr(tmp, '@'); if (context) { @@ -4410,6 +4491,7 @@ if (context) iax_ie_append_str(&ied, IAX_IE_CALLED_CONTEXT, context); ast_debug(1, "Transferring '%s' to '%s'\n", c->name, dest); + ast_queue_control_data(c, AST_CONTROL_TRANSFER, &message, sizeof(message)); return send_command_locked(callno, AST_FRAME_IAX, IAX_COMMAND_TRANSFER, 0, ied.buf, ied.pos, -1); } @@ -4423,7 +4505,7 @@ while ((peer = ao2_iterator_next(&i))) { if ((peer->addr.sin_addr.s_addr == sin.sin_addr.s_addr) && (peer->addr.sin_port == sin.sin_port)) { - res = ast_test_flag(peer, IAX_TRUNK); + res = ast_test_flag64(peer, IAX_TRUNK); peer_unref(peer); break; } @@ -4453,7 +4535,7 @@ if (tmp) { /* unlock and relock iaxsl[callno] to preserve locking order */ ast_mutex_unlock(&iaxsl[callno]); - ast_channel_free(tmp); + tmp = ast_channel_release(tmp); ast_mutex_lock(&iaxsl[callno]); } return NULL; @@ -4835,7 +4917,7 @@ /* Append to meta frame */ ptr = tpeer->trunkdata + IAX2_TRUNK_PREFACE + tpeer->trunkdatalen; - if (ast_test_flag(&globalflags, IAX_TRUNKTIMESTAMPS)) { + if (ast_test_flag64(&globalflags, IAX_TRUNKTIMESTAMPS)) { mtm = (struct ast_iax2_meta_trunk_mini *)ptr; mtm->len = htons(f->datalen); mtm->mini.callno = htons(pvt->callno); @@ -5038,7 +5120,7 @@ static int decrypt_frame(int callno, struct ast_iax2_full_hdr *fh, struct ast_frame *f, int *datalen) { int res=-1; - if (!ast_test_flag(iaxs[callno], IAX_KEYPOPULATED)) { + if (!ast_test_flag64(iaxs[callno], IAX_KEYPOPULATED)) { /* Search for possible keys, given secrets */ struct MD5Context md5; unsigned char digest[16]; @@ -5054,7 +5136,7 @@ build_encryption_keys(digest, iaxs[callno]); res = decode_frame(&iaxs[callno]->dcx, fh, f, datalen); if (!res) { - ast_set_flag(iaxs[callno], IAX_KEYPOPULATED); + ast_set_flag64(iaxs[callno], IAX_KEYPOPULATED); break; } } @@ -5110,7 +5192,7 @@ iax2_key_rotate(pvt); } - if ((ast_test_flag(pvt, IAX_TRUNK) || + if ((ast_test_flag64(pvt, IAX_TRUNK) || (((fts & 0xFFFF0000L) == (lastsent & 0xFFFF0000L)) || ((fts & 0xFFFF0000L) == ((lastsent + 0x10000) & 0xFFFF0000L)))) /* High two bytes are the same on timestamp, or sending on a trunk */ && @@ -5151,7 +5233,7 @@ if (now) { fr = &frb.fr2; } else - fr = iax_frame_new(DIRECTION_OUTGRESS, ast_test_flag(pvt, IAX_ENCRYPTED) ? f->datalen + 32 : f->datalen, (f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_VIDEO)); + fr = iax_frame_new(DIRECTION_OUTGRESS, ast_test_flag64(pvt, IAX_ENCRYPTED) ? f->datalen + 32 : f->datalen, (f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_VIDEO)); if (!fr) { ast_log(LOG_WARNING, "Out of memory\n"); return -1; @@ -5208,8 +5290,8 @@ pvt->svoiceformat = f->subclass; else if (f->frametype == AST_FRAME_VIDEO) pvt->svideoformat = f->subclass & ~0x1; - if (ast_test_flag(pvt, IAX_ENCRYPTED)) { - if (ast_test_flag(pvt, IAX_KEYPOPULATED)) { + if (ast_test_flag64(pvt, IAX_ENCRYPTED)) { + if (ast_test_flag64(pvt, IAX_KEYPOPULATED)) { if (fr->transfer) iax_outputframe(fr, NULL, 2, &pvt->transfer, fr->datalen - sizeof(struct ast_iax2_full_hdr)); else @@ -5228,7 +5310,7 @@ } else res = iax2_transmit(fr); } else { - if (ast_test_flag(pvt, IAX_TRUNK)) { + if (ast_test_flag64(pvt, IAX_TRUNK)) { iax2_trunk_queue(pvt, fr); res = 0; } else if (fr->af.frametype == AST_FRAME_VIDEO) { @@ -5256,8 +5338,8 @@ fr->retries = -1; if (pvt->transferring == TRANSFER_MEDIAPASS) fr->transfer = 1; - if (ast_test_flag(pvt, IAX_ENCRYPTED)) { - if (ast_test_flag(pvt, IAX_KEYPOPULATED)) { + if (ast_test_flag64(pvt, IAX_ENCRYPTED)) { + if (ast_test_flag64(pvt, IAX_KEYPOPULATED)) { encrypt_frame(&pvt->ecx, (struct ast_iax2_full_hdr *)mh, pvt->semirand, &fr->datalen); } else ast_log(LOG_WARNING, "Supposed to send packet encrypted, but no key?\n"); @@ -5313,21 +5395,21 @@ user_unref(user), user = ao2_iterator_next(&i)) { if (havepattern && regexec(®exbuf, user->name, 0, NULL, 0)) continue; - + if (!ast_strlen_zero(user->secret)) { - ast_copy_string(auth,user->secret, sizeof(auth)); + ast_copy_string(auth,user->secret, sizeof(auth)); } else if (!ast_strlen_zero(user->inkeys)) { - snprintf(auth, sizeof(auth), "Key: %-15.15s ", user->inkeys); - } else + snprintf(auth, sizeof(auth), "Key: %-15.15s ", user->inkeys); + } else ast_copy_string(auth, "-no secret-", sizeof(auth)); - - if(ast_test_flag(user,IAX_CODEC_NOCAP)) + + if(ast_test_flag64(user, IAX_CODEC_NOCAP)) pstr = "REQ Only"; - else if(ast_test_flag(user,IAX_CODEC_NOPREFS)) + else if(ast_test_flag64(user, IAX_CODEC_NOPREFS)) pstr = "Disabled"; else - pstr = ast_test_flag(user,IAX_CODEC_USER_FIRST) ? "Caller" : "Host"; - + pstr = ast_test_flag64(user, IAX_CODEC_USER_FIRST) ? "Caller" : "Host"; + ast_cli(a->fd, FORMAT2, user->name, auth, user->authmethods, user->contexts ? user->contexts->context : DEFAULT_CONTEXT, user->ha ? "Yes" : "No", pstr); @@ -5341,7 +5423,7 @@ #undef FORMAT2 } -static int __iax2_show_peers(int manager, int fd, struct mansession *s, int argc, char *argv[]) +static int __iax2_show_peers(int manager, int fd, struct mansession *s, const int argc, const char * const argv[]) { regex_t regexbuf; int havepattern = 0; @@ -5441,17 +5523,17 @@ name, peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "-none-", ntohs(peer->addr.sin_port), - ast_test_flag(peer, IAX_DYNAMIC) ? "yes" : "no", - ast_test_flag(peer, IAX_TRUNK) ? "yes" : "no", + ast_test_flag64(peer, IAX_DYNAMIC) ? "yes" : "no", + ast_test_flag64(peer, IAX_TRUNK) ? "yes" : "no", peer->encmethods ? ast_str_buffer(encmethods) : "no", status); } else { ast_cli(fd, FORMAT, name, peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)", - ast_test_flag(peer, IAX_DYNAMIC) ? "(D)" : "(S)", + ast_test_flag64(peer, IAX_DYNAMIC) ? "(D)" : "(S)", nm, ntohs(peer->addr.sin_port), - ast_test_flag(peer, IAX_TRUNK) ? "(T)" : " ", + ast_test_flag64(peer, IAX_TRUNK) ? "(T)" : " ", peer->encmethods ? "(E)" : " ", status, term); @@ -5672,14 +5754,14 @@ /*! \brief callback to display iax peers in manager */ static int manager_iax2_show_peers(struct mansession *s, const struct message *m) { - char *a[] = { "iax2", "show", "users" }; + static const char * const a[] = { "iax2", "show", "peers" }; const char *id = astman_get_header(m,"ActionID"); char idtext[256] = ""; if (!ast_strlen_zero(id)) snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id); astman_send_ack(s, m, "Peer status list will follow"); - return __iax2_show_peers(1, -1, s, 3, a ); + return __iax2_show_peers(1, -1, s, 3, a); } /*! \brief callback to display iax peers in manager format */ @@ -5714,8 +5796,8 @@ ast_copy_string(nm, ast_inet_ntoa(peer->mask), sizeof(nm)); astman_append(s, "Mask: %s\r\n", nm); astman_append(s, "Port: %d\r\n", ntohs(peer->addr.sin_port)); - astman_append(s, "Dynamic: %s\r\n", ast_test_flag(peer, IAX_DYNAMIC) ? "Yes" : "No"); - astman_append(s, "Trunk: %s\r\n", ast_test_flag(peer, IAX_TRUNK) ? "Yes" : "No"); + astman_append(s, "Dynamic: %s\r\n", ast_test_flag64(peer, IAX_DYNAMIC) ? "Yes" : "No"); + astman_append(s, "Trunk: %s\r\n", ast_test_flag64(peer, IAX_TRUNK) ? "Yes" : "No"); astman_append(s, "Encryption: %s\r\n", peer->encmethods ? ast_str_buffer(encmethods) : "No"); peer_status(peer, status, sizeof(status)); astman_append(s, "Status: %s\r\n\r\n", status); @@ -5868,7 +5950,7 @@ if (iaxs[x]) { int lag, jitter, localdelay; jb_info jbinfo; - if (ast_test_flag(iaxs[x], IAX_USEJITTERBUF)) { + if (ast_test_flag64(iaxs[x], IAX_USEJITTERBUF)) { jb_getinfo(iaxs[x]->jb, &jbinfo); jitter = jbinfo.jitter; localdelay = jbinfo.current - jbinfo.min; @@ -5921,7 +6003,7 @@ iax_frame_subclass2str(iaxs[x]->first_iax_message & ~MARK_IAX_SUBCLASS_TX, first_message, sizeof(first_message)); iax_frame_subclass2str(iaxs[x]->last_iax_message & ~MARK_IAX_SUBCLASS_TX, last_message, sizeof(last_message)); - if(ast_test_flag(iaxs[x], IAX_USEJITTERBUF)) { + if(ast_test_flag64(iaxs[x], IAX_USEJITTERBUF)) { jb_getinfo(iaxs[x]->jb, &jbinfo); localjitter = jbinfo.jitter; localdelay = jbinfo.current - jbinfo.min; @@ -6121,12 +6203,12 @@ if (iaxs[callno]) { /* If there's an outstanding error, return failure now */ if (!iaxs[callno]->error) { - if (ast_test_flag(iaxs[callno], IAX_ALREADYGONE)) + if (ast_test_flag64(iaxs[callno], IAX_ALREADYGONE)) res = 0; /* Don't waste bandwidth sending null frames */ else if (f->frametype == AST_FRAME_NULL) res = 0; - else if ((f->frametype == AST_FRAME_VOICE) && ast_test_flag(iaxs[callno], IAX_QUELCH)) + else if ((f->frametype == AST_FRAME_VOICE) && ast_test_flag64(iaxs[callno], IAX_QUELCH)) res = 0; else if (!ast_test_flag(&iaxs[callno]->state, IAX_STATE_STARTED)) res = 0; @@ -6347,15 +6429,15 @@ } /* If a max AUTHREQ restriction is in place, activate it */ if (user->maxauthreq > 0) - ast_set_flag(iaxs[callno], IAX_MAXAUTHREQ); + ast_set_flag64(iaxs[callno], IAX_MAXAUTHREQ); iaxs[callno]->prefs = user->prefs; - ast_copy_flags(iaxs[callno], user, IAX_CODEC_USER_FIRST | IAX_IMMEDIATE | IAX_CODEC_NOPREFS | IAX_CODEC_NOCAP | IAX_FORCE_ENCRYPT); + ast_copy_flags64(iaxs[callno], user, IAX_CODEC_USER_FIRST | IAX_IMMEDIATE | IAX_CODEC_NOPREFS | IAX_CODEC_NOCAP | IAX_FORCE_ENCRYPT); iaxs[callno]->encmethods = user->encmethods; /* Store the requested username if not specified */ if (ast_strlen_zero(iaxs[callno]->username)) ast_string_field_set(iaxs[callno], username, user->name); /* Store whether this is a trunked call, too, of course, and move if appropriate */ - ast_copy_flags(iaxs[callno], user, IAX_TRUNK); + ast_copy_flags64(iaxs[callno], user, IAX_TRUNK); iaxs[callno]->capability = user->capability; /* And use the default context */ if (ast_strlen_zero(iaxs[callno]->context)) { @@ -6370,7 +6452,7 @@ iaxs[callno]->authmethods = user->authmethods; iaxs[callno]->adsi = user->adsi; /* If the user has callerid, override the remote caller id. */ - if (ast_test_flag(user, IAX_HASCALLERID)) { + if (ast_test_flag64(user, IAX_HASCALLERID)) { iaxs[callno]->calling_tns = 0; iaxs[callno]->calling_ton = 0; ast_string_field_set(iaxs[callno], cid_num, user->cid_num); @@ -6392,7 +6474,7 @@ iaxs[callno]->amaflags = user->amaflags; if (!ast_strlen_zero(user->language)) ast_string_field_set(iaxs[callno], language, user->language); - ast_copy_flags(iaxs[callno], user, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF); + ast_copy_flags64(iaxs[callno], user, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE); /* Keep this check last */ if (!ast_strlen_zero(user->dbsecret)) { char *family, *key=NULL; @@ -6424,7 +6506,7 @@ res = 0; } } - ast_set2_flag(iaxs[callno], iax2_getpeertrunk(*sin), IAX_TRUNK); + ast_set2_flag64(iaxs[callno], iax2_getpeertrunk(*sin), IAX_TRUNK); return res; } @@ -6478,7 +6560,7 @@ memset(&ied, 0, sizeof(ied)); /* If an AUTHREQ restriction is in place, make sure we can send an AUTHREQ back */ - if (ast_test_flag(p, IAX_MAXAUTHREQ)) { + if (ast_test_flag64(p, IAX_MAXAUTHREQ)) { struct iax2_user *user, tmp_user = { .name = p->username, }; @@ -6516,7 +6598,7 @@ res = send_command(p, AST_FRAME_IAX, IAX_COMMAND_AUTHREQ, 0, ied.buf, ied.pos, -1); if (p->encmethods) - ast_set_flag(p, IAX_ENCRYPTED); + ast_set_flag64(p, IAX_ENCRYPTED); return res; } @@ -6538,14 +6620,14 @@ } user = ao2_find(users, &tmp_user, OBJ_POINTER); if (user) { - if (ast_test_flag(p, IAX_MAXAUTHREQ)) { + if (ast_test_flag64(p, IAX_MAXAUTHREQ)) { ast_atomic_fetchadd_int(&user->curauthreq, -1); - ast_clear_flag(p, IAX_MAXAUTHREQ); + ast_clear_flag64(p, IAX_MAXAUTHREQ); } ast_string_field_set(p, host, user->name); user = user_unref(user); } - if (ast_test_flag(p, IAX_FORCE_ENCRYPT) && !p->encmethods) { + if (ast_test_flag64(p, IAX_FORCE_ENCRYPT) && !p->encmethods) { ast_log(LOG_NOTICE, "Call Terminated, Incomming call is unencrypted while force encrypt is enabled."); return res; } @@ -6663,7 +6745,7 @@ goto return_unref; } - if (!ast_test_flag(p, IAX_DYNAMIC)) { + if (!ast_test_flag64(p, IAX_DYNAMIC)) { if (authdebug) ast_log(LOG_NOTICE, "Peer '%s' is not dynamic (from %s)\n", peer, ast_inet_ntoa(sin->sin_addr)); goto return_unref; @@ -6885,8 +6967,8 @@ } if (ies->encmethods) { - ast_set_flag(p, IAX_ENCRYPTED | IAX_KEYPOPULATED); - } else if (ast_test_flag(iaxs[callno], IAX_FORCE_ENCRYPT)) { + ast_set_flag64(p, IAX_ENCRYPTED | IAX_KEYPOPULATED); + } else if (ast_test_flag64(iaxs[callno], IAX_FORCE_ENCRYPT)) { ast_log(LOG_NOTICE, "Call initiated without encryption while forceencryption=yes option is set"); return -1; /* if force encryption is yes, and no encryption methods, then return -1 to hangup */ } @@ -7072,16 +7154,13 @@ pvt->lastsent = 0; pvt->nextpred = 0; pvt->pingtime = DEFAULT_RETRY_TIME; - AST_LIST_LOCK(&frame_queue); - AST_LIST_TRAVERSE(&frame_queue, cur, list) { + AST_LIST_TRAVERSE(&frame_queue[callno], cur, list) { /* We must cancel any packets that would have been transmitted because now we're talking to someone new. It's okay, they were transmitted to someone that didn't care anyway. */ - if (callno == cur->callno) - cur->retries = -1; + cur->retries = -1; } - AST_LIST_UNLOCK(&frame_queue); - return 0; + return 0; } /*! \brief Acknowledgment received for OUR registration */ @@ -7258,21 +7337,21 @@ peer->expire = -1; ast_debug(1, "Expiring registration for peer '%s'\n", peer->name); - if (ast_test_flag((&globalflags), IAX_RTUPDATE) && (ast_test_flag(peer, IAX_TEMPONLY|IAX_RTCACHEFRIENDS))) + if (ast_test_flag64((&globalflags), IAX_RTUPDATE) && (ast_test_flag64(peer, IAX_TEMPONLY|IAX_RTCACHEFRIENDS))) realtime_update_peer(peer->name, &peer->addr, 0); manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name); /* Reset the address */ memset(&peer->addr, 0, sizeof(peer->addr)); /* Reset expiry value */ peer->expiry = min_reg_expire; - if (!ast_test_flag(peer, IAX_TEMPONLY)) + if (!ast_test_flag64(peer, IAX_TEMPONLY)) ast_db_del("IAX/Registry", peer->name); register_peer_exten(peer, 0); ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "IAX2/%s", peer->name); /* Activate notification */ if (iax2_regfunk) iax2_regfunk(peer->name, 0); - if (ast_test_flag(peer, IAX_RTAUTOCLEAR)) + if (ast_test_flag64(peer, IAX_RTAUTOCLEAR)) unlink_peer(peer); peer_unref(peer); @@ -7294,7 +7373,7 @@ char data[80]; struct in_addr in; char *c, *d; - if (!ast_test_flag(p, IAX_TEMPONLY) && (!ast_db_get("IAX/Registry", p->name, data, sizeof(data)))) { + if (!ast_test_flag64(p, IAX_TEMPONLY) && (!ast_db_get("IAX/Registry", p->name, data, sizeof(data)))) { c = strchr(data, ':'); if (c) { *c = '\0'; @@ -7364,7 +7443,7 @@ if (!iaxs[callno]) goto return_unref; - if (ast_test_flag((&globalflags), IAX_RTUPDATE) && (ast_test_flag(p, IAX_TEMPONLY|IAX_RTCACHEFRIENDS))) { + if (ast_test_flag64((&globalflags), IAX_RTUPDATE) && (ast_test_flag64(p, IAX_TEMPONLY|IAX_RTCACHEFRIENDS))) { if (sin->sin_addr.s_addr) { time_t nowtime; time(&nowtime); @@ -7379,14 +7458,14 @@ /* Stash the IP address from which they registered */ memcpy(&p->addr, sin, sizeof(p->addr)); snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), p->expiry); - if (!ast_test_flag(p, IAX_TEMPONLY) && sin->sin_addr.s_addr) { + if (!ast_test_flag64(p, IAX_TEMPONLY) && sin->sin_addr.s_addr) { ast_db_put("IAX/Registry", p->name, data); ast_verb(3, "Registered IAX2 '%s' (%s) at %s:%d\n", p->name, ast_test_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED) ? "AUTHENTICATED" : "UNAUTHENTICATED", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Registered\r\n", p->name); register_peer_exten(p, 1); ast_devstate_changed(AST_DEVICE_UNKNOWN, "IAX2/%s", p->name); /* Activate notification */ - } else if (!ast_test_flag(p, IAX_TEMPONLY)) { + } else if (!ast_test_flag64(p, IAX_TEMPONLY)) { ast_verb(3, "Unregistered IAX2 '%s' (%s)\n", p->name, ast_test_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED) ? "AUTHENTICATED" : "UNAUTHENTICATED"); manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Unregistered\r\n", p->name); @@ -7470,7 +7549,7 @@ iax_ie_append_short(&ied, IAX_IE_MSGCOUNT, msgcount); } - if (ast_test_flag(p, IAX_HASCALLERID)) { + if (ast_test_flag64(p, IAX_HASCALLERID)) { iax_ie_append_str(&ied, IAX_IE_CALLING_NUMBER, p->cid_num); iax_ie_append_str(&ied, IAX_IE_CALLING_NAME, p->cid_name); } @@ -7687,16 +7766,13 @@ { struct iax_frame *f; - AST_LIST_LOCK(&frame_queue); - AST_LIST_TRAVERSE(&frame_queue, f, list) { + AST_LIST_TRAVERSE(&frame_queue[callno], f, list) { /* Send a copy immediately */ - if ((f->callno == callno) && iaxs[f->callno] && - ((unsigned char ) (f->oseqno - last) < 128) && - (f->retries >= 0)) { + if (((unsigned char) (f->oseqno - last) < 128) && + (f->retries >= 0)) { send_packet(f); } } - AST_LIST_UNLOCK(&frame_queue); } static void __iax2_poke_peer_s(const void *data) @@ -7734,7 +7810,7 @@ /* We're actually sending a frame, so fill the meta trunk header and meta header */ meta->zeros = 0; meta->metacmd = IAX_META_TRUNK; - if (ast_test_flag(&globalflags, IAX_TRUNKTIMESTAMPS)) + if (ast_test_flag64(&globalflags, IAX_TRUNKTIMESTAMPS)) meta->cmddata = IAX_META_TRUNK_MINI; else meta->cmddata = IAX_META_TRUNK_SUPERMINI; @@ -7972,9 +8048,6 @@ return -1; } - -static int iax2_provision(struct sockaddr_in *end, int sockfd, char *dest, const char *template, int force); - static int check_provisioning(struct sockaddr_in *sin, int sockfd, char *si, unsigned int ver) { unsigned int ourver; @@ -8052,7 +8125,7 @@ ast_mutex_lock(&iaxsl[callno]); if (iaxs[callno] && iaxs[callno]->owner && iaxs[callno]->owner->name) { - if(ast_test_flag(iaxs[callno], IAX_USEJITTERBUF)) { + if(ast_test_flag64(iaxs[callno], IAX_USEJITTERBUF)) { jb_getinfo(iaxs[callno]->jb, &jbinfo); localjitter = jbinfo.jitter; localdelay = jbinfo.current - jbinfo.min; @@ -8518,7 +8591,7 @@ /* Deal with POKE/PONG without allocating a callno */ if (f.frametype == AST_FRAME_IAX && f.subclass == IAX_COMMAND_POKE) { /* Reply back with a PONG, but don't care about the result. */ - send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_PONG, ntohs(fh->ts), fh->iseqno + 1); + send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_PONG, ntohl(fh->ts), fh->iseqno + 1); return 1; } else if (f.frametype == AST_FRAME_IAX && f.subclass == IAX_COMMAND_ACK && dcallno == 1) { /* Ignore */ @@ -8577,7 +8650,7 @@ ast_mutex_unlock(&iaxsl[fr->callno]); return 1; } - if (ast_test_flag(iaxs[fr->callno], IAX_ENCRYPTED)) { + if (ast_test_flag64(iaxs[fr->callno], IAX_ENCRYPTED)) { if (decrypt_frame(fr->callno, fh, &f, &res)) { ast_log(LOG_NOTICE, "Packet Decrypt Failed!\n"); ast_mutex_unlock(&iaxsl[fr->callno]); @@ -8713,17 +8786,15 @@ if (iaxdebug) ast_debug(1, "Cancelling transmission of packet %d\n", x); call_to_destroy = 0; - AST_LIST_LOCK(&frame_queue); - AST_LIST_TRAVERSE(&frame_queue, cur, list) { + AST_LIST_TRAVERSE(&frame_queue[fr->callno], cur, list) { /* If it's our call, and our timestamp, mark -1 retries */ - if ((fr->callno == cur->callno) && (x == cur->oseqno)) { + if (x == cur->oseqno) { cur->retries = -1; /* Destroy call if this is the end */ if (cur->final) call_to_destroy = fr->callno; } } - AST_LIST_UNLOCK(&frame_queue); if (call_to_destroy) { if (iaxdebug) ast_debug(1, "Really destroying %d, having been acked on final message\n", call_to_destroy); @@ -8780,8 +8851,8 @@ if ((f.frametype == AST_FRAME_VOICE) || (f.frametype == AST_FRAME_VIDEO) || (f.frametype == AST_FRAME_IAX)) { - if (ast_test_flag(iaxs[fr->callno], IAX_DELAYPBXSTART)) { - ast_clear_flag(iaxs[fr->callno], IAX_DELAYPBXSTART); + if (ast_test_flag64(iaxs[fr->callno], IAX_DELAYPBXSTART)) { + ast_clear_flag64(iaxs[fr->callno], IAX_DELAYPBXSTART); if (!ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->chosenformat)) { ast_mutex_unlock(&iaxsl[fr->callno]); return 1; @@ -8928,7 +8999,7 @@ iaxs[fr->callno]->owner->uniqueid); } - ast_set_flag(iaxs[fr->callno], IAX_QUELCH); + ast_set_flag64(iaxs[fr->callno], IAX_QUELCH); if (ies.musiconhold) { if (iaxs[fr->callno]->owner && ast_bridged_channel(iaxs[fr->callno]->owner)) { const char *moh_suggest = iaxs[fr->callno]->mohsuggest; @@ -8946,7 +9017,7 @@ case IAX_COMMAND_UNQUELCH: if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED)) { /* Generate Manager Unhold event, if necessary*/ - if (iaxs[fr->callno]->owner && ast_test_flag(iaxs[fr->callno], IAX_QUELCH)) { + if (iaxs[fr->callno]->owner && ast_test_flag64(iaxs[fr->callno], IAX_QUELCH)) { manager_event(EVENT_FLAG_CALL, "Hold", "Status: Off\r\n" "Channel: %s\r\n" @@ -8955,7 +9026,7 @@ iaxs[fr->callno]->owner->uniqueid); } - ast_clear_flag(iaxs[fr->callno], IAX_QUELCH); + ast_clear_flag64(iaxs[fr->callno], IAX_QUELCH); if (iaxs[fr->callno]->owner && ast_bridged_channel(iaxs[fr->callno]->owner)) { iax2_queue_control_data(fr->callno, AST_CONTROL_UNHOLD, NULL, 0); if (!iaxs[fr->callno]) { @@ -8968,13 +9039,12 @@ case IAX_COMMAND_TXACC: if (iaxs[fr->callno]->transferring == TRANSFER_BEGIN) { /* Ack the packet with the given timestamp */ - AST_LIST_LOCK(&frame_queue); - AST_LIST_TRAVERSE(&frame_queue, cur, list) { + AST_LIST_TRAVERSE(&frame_queue[fr->callno], cur, list) { /* Cancel any outstanding txcnt's */ - if ((fr->callno == cur->callno) && (cur->transfer)) + if (cur->transfer) { cur->retries = -1; + } } - AST_LIST_UNLOCK(&frame_queue); memset(&ied1, 0, sizeof(ied1)); iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[fr->callno]->callno); send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_TXREADY, 0, ied1.buf, ied1.pos, -1); @@ -8995,7 +9065,7 @@ } } /* If we're in trunk mode, do it now, and update the trunk number in our frame before continuing */ - if (ast_test_flag(iaxs[fr->callno], IAX_TRUNK)) { + if (ast_test_flag64(iaxs[fr->callno], IAX_TRUNK)) { int new_callno; if ((new_callno = make_trunk(fr->callno, 1)) != -1) fr->callno = new_callno; @@ -9010,7 +9080,7 @@ ast_log(LOG_NOTICE, "Rejected connect attempt from %s, who was trying to reach '%s@%s'\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->exten, iaxs[fr->callno]->context); break; } - if (ast_strlen_zero(iaxs[fr->callno]->secret) && ast_test_flag(iaxs[fr->callno], IAX_FORCE_ENCRYPT)) { + if (ast_strlen_zero(iaxs[fr->callno]->secret) && ast_test_flag64(iaxs[fr->callno], IAX_FORCE_ENCRYPT)) { auth_fail(fr->callno, IAX_COMMAND_REJECT); ast_log(LOG_WARNING, "Rejected connect attempt. No secret present while force encrypt enabled.\n"); break; @@ -9050,8 +9120,8 @@ } else { /* Select an appropriate format */ - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOPREFS)) { - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) { + if(ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOPREFS)) { + if(ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP)) { using_prefs = "reqonly"; } else { using_prefs = "disabled"; @@ -9067,7 +9137,7 @@ ast_codec_pref_convert(&iaxs[fr->callno]->rprefs, ies.codec_prefs, 32, 0); if (ast_codec_pref_index(&iaxs[fr->callno]->rprefs, 0)) { /* If we are codec_first_choice we let the caller have the 1st shot at picking the codec.*/ - if (ast_test_flag(iaxs[fr->callno], IAX_CODEC_USER_FIRST)) { + if (ast_test_flag64(iaxs[fr->callno], IAX_CODEC_USER_FIRST)) { pref = iaxs[fr->callno]->rprefs; using_prefs = "caller"; } else { @@ -9075,13 +9145,13 @@ } } else pref = iaxs[fr->callno]->prefs; - + format = ast_codec_choose(&pref, iaxs[fr->callno]->capability & iaxs[fr->callno]->peercapability, 0); ast_codec_pref_string(&iaxs[fr->callno]->rprefs, caller_pref_buf, sizeof(caller_pref_buf) - 1); ast_codec_pref_string(&iaxs[fr->callno]->prefs, host_pref_buf, sizeof(host_pref_buf) - 1); } if (!format) { - if(!ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) + if(!ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP)) format = iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability; if (!format) { memset(&ied0, 0, sizeof(ied0)); @@ -9093,19 +9163,19 @@ return 1; } if (authdebug) { - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) + if(ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP)) ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->capability); else ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->peercapability, iaxs[fr->callno]->capability); } } else { /* Pick one... */ - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) { + if(ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP)) { if(!(iaxs[fr->callno]->peerformat & iaxs[fr->callno]->capability)) format = 0; } else { - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOPREFS)) { - using_prefs = ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP) ? "reqonly" : "disabled"; + if(ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOPREFS)) { + using_prefs = ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP) ? "reqonly" : "disabled"; memset(&pref, 0, sizeof(pref)); format = ast_best_codec(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability); strcpy(caller_pref_buf,"disabled"); @@ -9114,16 +9184,15 @@ using_prefs = "mine"; if (ast_codec_pref_index(&iaxs[fr->callno]->rprefs, 0)) { /* Do the opposite of what we tried above. */ - if (ast_test_flag(iaxs[fr->callno], IAX_CODEC_USER_FIRST)) { - pref = iaxs[fr->callno]->prefs; + if (ast_test_flag64(iaxs[fr->callno], IAX_CODEC_USER_FIRST)) { + pref = iaxs[fr->callno]->prefs; } else { pref = iaxs[fr->callno]->rprefs; using_prefs = "caller"; } format = ast_codec_choose(&pref, iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability, 1); - } else /* if no codec_prefs IE do it the old way */ - format = ast_best_codec(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability); + format = ast_best_codec(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability); } } @@ -9139,7 +9208,7 @@ } if (authdebug) ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->peercapability, iaxs[fr->callno]->capability); - ast_set_flag(iaxs[fr->callno], IAX_ALREADYGONE); + ast_set_flag64(iaxs[fr->callno], IAX_ALREADYGONE); break; } } @@ -9168,9 +9237,9 @@ host_pref_buf, VERBOSE_PREFIX_4, using_prefs); - + iaxs[fr->callno]->chosenformat = format; - ast_set_flag(iaxs[fr->callno], IAX_DELAYPBXSTART); + ast_set_flag64(iaxs[fr->callno], IAX_DELAYPBXSTART); } else { ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD); /* If this is a TBD call, we're ready but now what... */ @@ -9205,7 +9274,7 @@ } break; case IAX_COMMAND_HANGUP: - ast_set_flag(iaxs[fr->callno], IAX_ALREADYGONE); + ast_set_flag64(iaxs[fr->callno], IAX_ALREADYGONE); ast_debug(1, "Immediately destroying %d, having received hangup\n", fr->callno); /* Set hangup cause according to remote */ if (ies.causecode && iaxs[fr->callno]->owner) @@ -9219,7 +9288,7 @@ if (ies.causecode && iaxs[fr->callno]->owner) iaxs[fr->callno]->owner->hangupcause = ies.causecode; - if (!ast_test_flag(iaxs[fr->callno], IAX_PROVISION)) { + if (!ast_test_flag64(iaxs[fr->callno], IAX_PROVISION)) { if (iaxs[fr->callno]->owner && authdebug) ast_log(LOG_WARNING, "Call rejected by %s: %s\n", ast_inet_ntoa(iaxs[fr->callno]->addr.sin_addr), @@ -9230,7 +9299,7 @@ /* Send ack immediately, before we destroy */ send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0, fr->iseqno); - if (!ast_test_flag(iaxs[fr->callno], IAX_PROVISION)) + if (!ast_test_flag64(iaxs[fr->callno], IAX_PROVISION)) iaxs[fr->callno]->error = EPERM; iax2_destroy(fr->callno); break; @@ -9278,7 +9347,7 @@ /* Ignore if call is already up or needs authentication or is a TBD */ if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED | IAX_STATE_TBD | IAX_STATE_AUTHENTICATED)) break; - if (ast_test_flag(iaxs[fr->callno], IAX_PROVISION)) { + if (ast_test_flag64(iaxs[fr->callno], IAX_PROVISION)) { /* Send ack immediately, before we destroy */ send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno); iax2_destroy(fr->callno); @@ -9479,8 +9548,8 @@ } } else { /* Select an appropriate format */ - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOPREFS)) { - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) { + if(ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOPREFS)) { + if(ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP)) { using_prefs = "reqonly"; } else { using_prefs = "disabled"; @@ -9494,7 +9563,7 @@ if (ies.codec_prefs) ast_codec_pref_convert(&iaxs[fr->callno]->rprefs, ies.codec_prefs, 32, 0); if (ast_codec_pref_index(&iaxs[fr->callno]->rprefs, 0)) { - if (ast_test_flag(iaxs[fr->callno], IAX_CODEC_USER_FIRST)) { + if (ast_test_flag64(iaxs[fr->callno], IAX_CODEC_USER_FIRST)) { pref = iaxs[fr->callno]->rprefs; using_prefs = "caller"; } else { @@ -9502,19 +9571,18 @@ } } else /* if no codec_prefs IE do it the old way */ pref = iaxs[fr->callno]->prefs; - format = ast_codec_choose(&pref, iaxs[fr->callno]->capability & iaxs[fr->callno]->peercapability, 0); ast_codec_pref_string(&iaxs[fr->callno]->rprefs, caller_pref_buf, sizeof(caller_pref_buf) - 1); ast_codec_pref_string(&iaxs[fr->callno]->prefs, host_pref_buf, sizeof(host_pref_buf) - 1); } if (!format) { - if(!ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) { + if(!ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP)) { ast_debug(1, "We don't do requested format %s, falling back to peer capability %d\n", ast_getformatname(iaxs[fr->callno]->peerformat), iaxs[fr->callno]->peercapability); format = iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability; } if (!format) { if (authdebug) { - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) + if(ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP)) ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->capability); else ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->peercapability, iaxs[fr->callno]->capability); @@ -9529,14 +9597,14 @@ } } else { /* Pick one... */ - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) { + if(ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP)) { if(!(iaxs[fr->callno]->peerformat & iaxs[fr->callno]->capability)) format = 0; } else { - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOPREFS)) { - using_prefs = ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP) ? "reqonly" : "disabled"; + if(ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOPREFS)) { + using_prefs = ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP) ? "reqonly" : "disabled"; memset(&pref, 0, sizeof(pref)); - format = ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP) ? + format = ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP) ? iaxs[fr->callno]->peerformat : ast_best_codec(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability); strcpy(caller_pref_buf,"disabled"); strcpy(host_pref_buf,"disabled"); @@ -9544,8 +9612,8 @@ using_prefs = "mine"; if (ast_codec_pref_index(&iaxs[fr->callno]->rprefs, 0)) { /* Do the opposite of what we tried above. */ - if (ast_test_flag(iaxs[fr->callno], IAX_CODEC_USER_FIRST)) { - pref = iaxs[fr->callno]->prefs; + if (ast_test_flag64(iaxs[fr->callno], IAX_CODEC_USER_FIRST)) { + pref = iaxs[fr->callno]->prefs; } else { pref = iaxs[fr->callno]->rprefs; using_prefs = "caller"; @@ -9558,7 +9626,7 @@ if (!format) { ast_log(LOG_ERROR, "No best format in 0x%x???\n", iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability); if (authdebug) { - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) + if(ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP)) ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->capability); else ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->peercapability, iaxs[fr->callno]->capability); @@ -9641,7 +9709,7 @@ ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD); /* If this is a TBD call, we're ready but now what... */ ast_verb(3, "Accepted AUTHENTICATED TBD call from %s\n", ast_inet_ntoa(sin.sin_addr)); - if (ast_test_flag(iaxs[fr->callno], IAX_IMMEDIATE)) { + if (ast_test_flag64(iaxs[fr->callno], IAX_IMMEDIATE)) { goto immediatedial; } } @@ -9840,8 +9908,8 @@ iaxs[iaxs[fr->callno]->bridgecallno]->transferring = TRANSFER_RELEASED; iaxs[fr->callno]->transferring = TRANSFER_RELEASED; - ast_set_flag(iaxs[iaxs[fr->callno]->bridgecallno], IAX_ALREADYGONE); - ast_set_flag(iaxs[fr->callno], IAX_ALREADYGONE); + ast_set_flag64(iaxs[iaxs[fr->callno]->bridgecallno], IAX_ALREADYGONE); + ast_set_flag64(iaxs[fr->callno], IAX_ALREADYGONE); /* Stop doing lag & ping requests */ stop_stuff(fr->callno); @@ -9874,13 +9942,12 @@ break; case IAX_COMMAND_TXMEDIA: if (iaxs[fr->callno]->transferring == TRANSFER_READY) { - AST_LIST_LOCK(&frame_queue); - AST_LIST_TRAVERSE(&frame_queue, cur, list) { + AST_LIST_TRAVERSE(&frame_queue[fr->callno], cur, list) { /* Cancel any outstanding frames and start anew */ - if ((fr->callno == cur->callno) && (cur->transfer)) + if (cur->transfer) { cur->retries = -1; + } } - AST_LIST_UNLOCK(&frame_queue); /* Start sending our media to the transfer address, but otherwise leave the call as-is */ iaxs[fr->callno]->transferring = TRANSFER_MEDIAPASS; } @@ -9906,7 +9973,7 @@ break; case IAX_COMMAND_FWDOWNL: /* Firmware download */ - if (!ast_test_flag(&globalflags, IAX_ALLOWFWDOWNLOAD)) { + if (!ast_test_flag64(&globalflags, IAX_ALLOWFWDOWNLOAD)) { send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_UNSUPPORT, 0, NULL, 0, -1); break; } @@ -10006,6 +10073,31 @@ ast_mutex_unlock(&iaxsl[fr->callno]); return 1; } + /* Don't allow connected line updates unless we are configured to */ + if (f.frametype == AST_FRAME_CONTROL && f.subclass == AST_CONTROL_CONNECTED_LINE) { + struct ast_party_connected_line connected; + + if (!ast_test_flag64(iaxs[fr->callno], IAX_RECVCONNECTEDLINE)) { + ast_mutex_unlock(&iaxsl[fr->callno]); + return 1; + } + + /* Initialize defaults */ + ast_party_connected_line_init(&connected); + connected.id.number_presentation = iaxs[fr->callno]->calling_pres; + + if (!ast_connected_line_parse_data(f.data.ptr, f.datalen, &connected)) { + ast_string_field_set(iaxs[fr->callno], cid_num, connected.id.number); + ast_string_field_set(iaxs[fr->callno], cid_name, connected.id.name); + iaxs[fr->callno]->calling_pres = connected.id.number_presentation; + + if (iaxs[fr->callno]->owner) { + ast_set_callerid(iaxs[fr->callno]->owner, S_OR(connected.id.number, ""), S_OR(connected.id.name, ""), NULL); + iaxs[fr->callno]->owner->cid.cid_pres = connected.id.number_presentation; + } + } + ast_party_connected_line_free(&connected); + } /* Common things */ f.src = "IAX2"; f.mallocd = 0; @@ -10243,7 +10335,7 @@ return 0; } -static int iax2_provision(struct sockaddr_in *end, int sockfd, char *dest, const char *template, int force) +static int iax2_provision(struct sockaddr_in *end, int sockfd, const char *dest, const char *template, int force) { /* Returns 1 if provisioned, -1 if not able to find destination, or 0 if no provisioning is found for template */ @@ -10281,7 +10373,7 @@ /* Schedule autodestruct in case they don't ever give us anything back */ iaxs[callno]->autoid = iax2_sched_replace(iaxs[callno]->autoid, sched, 15000, auto_hangup, (void *)(long)callno); - ast_set_flag(iaxs[callno], IAX_PROVISION); + ast_set_flag64(iaxs[callno], IAX_PROVISION); /* Got a call number now, so go ahead and send the provisioning information */ send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_PROVISION, 0, ied.buf, ied.pos, -1); } @@ -10295,7 +10387,7 @@ /*! iax2provision \ingroup applications */ -static int iax2_prov_app(struct ast_channel *chan, void *data) +static int iax2_prov_app(struct ast_channel *chan, const char *data) { int res; char *sdata; @@ -10501,7 +10593,7 @@ memset(&cai, 0, sizeof(cai)); cai.capability = iax2_capability; - ast_copy_flags(&cai, &globalflags, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF); + ast_copy_flags64(&cai, &globalflags, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE); /* Populate our address from the given */ if (create_addr(pds.peer, NULL, &sin, &cai)) { @@ -10520,8 +10612,8 @@ } /* If this is a trunk, update it now */ - ast_copy_flags(iaxs[callno], &cai, IAX_TRUNK | IAX_SENDANI | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF); - if (ast_test_flag(&cai, IAX_TRUNK)) { + ast_copy_flags64(iaxs[callno], &cai, IAX_TRUNK | IAX_SENDANI | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE); + if (ast_test_flag64(&cai, IAX_TRUNK)) { int new_callno; if ((new_callno = make_trunk(callno, 1)) != -1) callno = new_callno; @@ -10559,66 +10651,18 @@ static void *network_thread(void *ignore) { - /* Our job is simple: Send queued messages, retrying if necessary. Read frames - from the network, and queue them for delivery to the channels */ - int res, count, wakeup; - struct iax_frame *f; - - if (timer) + if (timer) { ast_io_add(io, ast_timer_fd(timer), timing_read, AST_IO_IN | AST_IO_PRI, NULL); - - for(;;) { - pthread_testcancel(); + } - /* Go through the queue, sending messages which have not yet been - sent, and scheduling retransmissions if appropriate */ - AST_LIST_LOCK(&frame_queue); - count = 0; - wakeup = -1; - AST_LIST_TRAVERSE_SAFE_BEGIN(&frame_queue, f, list) { - if (f->sentyet) - continue; - - /* Try to lock the pvt, if we can't... don't fret - defer it till later */ - if (ast_mutex_trylock(&iaxsl[f->callno])) { - wakeup = 1; - continue; - } - - f->sentyet = 1; - - if (iaxs[f->callno]) { - send_packet(f); - count++; - } - - ast_mutex_unlock(&iaxsl[f->callno]); - - if (f->retries < 0) { - /* This is not supposed to be retransmitted */ - AST_LIST_REMOVE_CURRENT(list); - /* Free the iax frame */ - iax_frame_free(f); - } else { - /* We need reliable delivery. Schedule a retransmission */ - f->retries++; - f->retrans = iax2_sched_add(sched, f->retrytime, attempt_transmit, f); - } - } - AST_LIST_TRAVERSE_SAFE_END; - AST_LIST_UNLOCK(&frame_queue); - + for (;;) { pthread_testcancel(); - if (count >= 20) - ast_debug(1, "chan_iax2: Sent %d queued outbound frames all at once\n", count); + /* Wake up once a second just in case SIGURG was sent while + * we weren't in poll(), to make sure we don't hang when trying + * to unload. */ + ast_io_wait(io, 1000); + } - /* Now do the IO, and run scheduled tasks */ - res = ast_io_wait(io, wakeup); - if (res >= 0) { - if (res >= 20) - ast_debug(1, "chan_iax2: ast_io_wait ran %d I/Os all at once\n", res); - } - } return NULL; } @@ -10810,7 +10854,7 @@ if (!temponly) { peer = ao2_find(peers, &tmp_peer, OBJ_POINTER); - if (peer && !ast_test_flag(peer, IAX_DELME)) + if (peer && !ast_test_flag64(peer, IAX_DELME)) firstpass = 0; } @@ -10831,7 +10875,7 @@ if (peer) { if (firstpass) { - ast_copy_flags(peer, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_FORCE_ENCRYPT); + ast_copy_flags64(peer, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE | IAX_FORCE_ENCRYPT); peer->encmethods = iax2_encryption; peer->adsi = adsi; ast_string_field_set(peer,secret,""); @@ -10847,7 +10891,7 @@ peer->pokefreqnotok = DEFAULT_FREQ_NOTOK; ast_string_field_set(peer,context,""); ast_string_field_set(peer,peercontext,""); - ast_clear_flag(peer, IAX_HASCALLERID); + ast_clear_flag64(peer, IAX_HASCALLERID); ast_string_field_set(peer, cid_name, ""); ast_string_field_set(peer, cid_num, ""); ast_string_field_set(peer, mohinterpret, mohinterpret); @@ -10874,42 +10918,42 @@ } else if (!strcasecmp(v->name, "dbsecret")) { ast_string_field_set(peer, dbsecret, v->value); } else if (!strcasecmp(v->name, "trunk")) { - ast_set2_flag(peer, ast_true(v->value), IAX_TRUNK); - if (ast_test_flag(peer, IAX_TRUNK) && !timer) { + ast_set2_flag64(peer, ast_true(v->value), IAX_TRUNK); + if (ast_test_flag64(peer, IAX_TRUNK) && !timer) { ast_log(LOG_WARNING, "Unable to support trunking on peer '%s' without a timing interface\n", peer->name); - ast_clear_flag(peer, IAX_TRUNK); + ast_clear_flag64(peer, IAX_TRUNK); } } else if (!strcasecmp(v->name, "auth")) { peer->authmethods = get_auth_methods(v->value); } else if (!strcasecmp(v->name, "encryption")) { peer->encmethods |= get_encrypt_methods(v->value); if (!peer->encmethods) { - ast_clear_flag(peer, IAX_FORCE_ENCRYPT); + ast_clear_flag64(peer, IAX_FORCE_ENCRYPT); } } else if (!strcasecmp(v->name, "forceencryption")) { if (ast_false(v->value)) { - ast_clear_flag(peer, IAX_FORCE_ENCRYPT); + ast_clear_flag64(peer, IAX_FORCE_ENCRYPT); } else { peer->encmethods |= get_encrypt_methods(v->value); if (peer->encmethods) { - ast_set_flag(peer, IAX_FORCE_ENCRYPT); + ast_set_flag64(peer, IAX_FORCE_ENCRYPT); } } } else if (!strcasecmp(v->name, "transfer")) { if (!strcasecmp(v->value, "mediaonly")) { - ast_set_flags_to(peer, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_TRANSFERMEDIA); + ast_set_flags_to64(peer, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_TRANSFERMEDIA); } else if (ast_true(v->value)) { - ast_set_flags_to(peer, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, 0); - } else - ast_set_flags_to(peer, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_NOTRANSFER); + ast_set_flags_to64(peer, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, 0); + } else + ast_set_flags_to64(peer, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_NOTRANSFER); } else if (!strcasecmp(v->name, "jitterbuffer")) { - ast_set2_flag(peer, ast_true(v->value), IAX_USEJITTERBUF); + ast_set2_flag64(peer, ast_true(v->value), IAX_USEJITTERBUF); } else if (!strcasecmp(v->name, "forcejitterbuffer")) { - ast_set2_flag(peer, ast_true(v->value), IAX_FORCEJITTERBUF); + ast_set2_flag64(peer, ast_true(v->value), IAX_FORCEJITTERBUF); } else if (!strcasecmp(v->name, "host")) { if (!strcasecmp(v->value, "dynamic")) { /* They'll register with us */ - ast_set_flag(peer, IAX_DYNAMIC); + ast_set_flag64(peer, IAX_DYNAMIC); if (!found) { /* Initialize stuff iff we're not found, otherwise we keep going with what we had */ @@ -10923,7 +10967,7 @@ } else { /* Non-dynamic. Make sure we become that way if we're not */ ast_sched_thread_del(sched, peer->expire); - ast_clear_flag(peer, IAX_DYNAMIC); + ast_clear_flag64(peer, IAX_DYNAMIC); if (ast_dnsmgr_lookup(v->value, &peer->addr, &peer->dnsmgr, srvlookup ? "_iax._udp" : NULL)) return peer_unref(peer); if (!peer->addr.sin_port) @@ -10949,7 +10993,7 @@ } else if (!strcasecmp(v->name, "peercontext")) { ast_string_field_set(peer, peercontext, v->value); } else if (!strcasecmp(v->name, "port")) { - if (ast_test_flag(peer, IAX_DYNAMIC)) + if (ast_test_flag64(peer, IAX_DYNAMIC)) peer->defaddr.sin_port = htons(atoi(v->value)); else peer->addr.sin_port = htons(atoi(v->value)); @@ -10970,15 +11014,15 @@ ast_string_field_set(peer, cid_name, ""); ast_string_field_set(peer, cid_num, ""); } - ast_set_flag(peer, IAX_HASCALLERID); + ast_set_flag64(peer, IAX_HASCALLERID); } else if (!strcasecmp(v->name, "fullname")) { ast_string_field_set(peer, cid_name, S_OR(v->value, "")); - ast_set_flag(peer, IAX_HASCALLERID); + ast_set_flag64(peer, IAX_HASCALLERID); } else if (!strcasecmp(v->name, "cid_number")) { ast_string_field_set(peer, cid_num, S_OR(v->value, "")); - ast_set_flag(peer, IAX_HASCALLERID); + ast_set_flag64(peer, IAX_HASCALLERID); } else if (!strcasecmp(v->name, "sendani")) { - ast_set2_flag(peer, ast_true(v->value), IAX_SENDANI); + ast_set2_flag64(peer, ast_true(v->value), IAX_SENDANI); } else if (!strcasecmp(v->name, "inkeys")) { ast_string_field_set(peer, inkeys, v->value); } else if (!strcasecmp(v->name, "outkey")) { @@ -11006,6 +11050,18 @@ ast_string_field_set(peer, zonetag, v->value); } else if (!strcasecmp(v->name, "adsi")) { peer->adsi = ast_true(v->value); + } else if (!strcasecmp(v->name, "connectedline")) { + if (ast_true(v->value)) { + ast_set_flag64(peer, IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE); + } else if (!strcasecmp(v->value, "send")) { + ast_clear_flag64(peer, IAX_RECVCONNECTEDLINE); + ast_set_flag64(peer, IAX_SENDCONNECTEDLINE); + } else if (!strcasecmp(v->value, "receive")) { + ast_clear_flag64(peer, IAX_SENDCONNECTEDLINE); + ast_set_flag64(peer, IAX_RECVCONNECTEDLINE); + } else { + ast_clear_flag64(peer, IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE); + } }/* else if (strcasecmp(v->name,"type")) */ /* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */ v = v->next; @@ -11016,7 +11072,7 @@ } if (!peer->authmethods) peer->authmethods = IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT; - ast_clear_flag(peer, IAX_DELME); + ast_clear_flag64(peer, IAX_DELME); /* Make sure these are IPv4 addresses */ peer->addr.sin_family = AF_INET; } @@ -11070,7 +11126,7 @@ if (!temponly) { user = ao2_find(users, &tmp_user, OBJ_POINTER); - if (user && !ast_test_flag(user, IAX_DELME)) + if (user && !ast_test_flag64(user, IAX_DELME)) firstpass = 0; } @@ -11104,8 +11160,8 @@ user->adsi = adsi; ast_string_field_set(user, name, name); ast_string_field_set(user, language, language); - ast_copy_flags(user, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_CODEC_USER_FIRST | IAX_CODEC_NOPREFS | IAX_CODEC_NOCAP | IAX_FORCE_ENCRYPT); - ast_clear_flag(user, IAX_HASCALLERID); + ast_copy_flags64(user, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_CODEC_USER_FIRST | IAX_CODEC_NOPREFS | IAX_CODEC_NOCAP | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE | IAX_FORCE_ENCRYPT); + ast_clear_flag64(user, IAX_HASCALLERID); ast_string_field_set(user, cid_name, ""); ast_string_field_set(user, cid_num, ""); ast_string_field_set(user, accountcode, accountcode); @@ -11144,49 +11200,49 @@ } else if (!strcasecmp(v->name, "disallow")) { ast_parse_allow_disallow(&user->prefs, &user->capability,v->value, 0); } else if (!strcasecmp(v->name, "trunk")) { - ast_set2_flag(user, ast_true(v->value), IAX_TRUNK); - if (ast_test_flag(user, IAX_TRUNK) && !timer) { + ast_set2_flag64(user, ast_true(v->value), IAX_TRUNK); + if (ast_test_flag64(user, IAX_TRUNK) && !timer) { ast_log(LOG_WARNING, "Unable to support trunking on user '%s' without a timing interface\n", user->name); - ast_clear_flag(user, IAX_TRUNK); + ast_clear_flag64(user, IAX_TRUNK); } } else if (!strcasecmp(v->name, "auth")) { user->authmethods = get_auth_methods(v->value); } else if (!strcasecmp(v->name, "encryption")) { user->encmethods |= get_encrypt_methods(v->value); if (!user->encmethods) { - ast_clear_flag(user, IAX_FORCE_ENCRYPT); + ast_clear_flag64(user, IAX_FORCE_ENCRYPT); } } else if (!strcasecmp(v->name, "forceencryption")) { if (ast_false(v->value)) { - ast_clear_flag(user, IAX_FORCE_ENCRYPT); + ast_clear_flag64(user, IAX_FORCE_ENCRYPT); } else { user->encmethods |= get_encrypt_methods(v->value); if (user->encmethods) { - ast_set_flag(user, IAX_FORCE_ENCRYPT); + ast_set_flag64(user, IAX_FORCE_ENCRYPT); } } } else if (!strcasecmp(v->name, "transfer")) { if (!strcasecmp(v->value, "mediaonly")) { - ast_set_flags_to(user, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_TRANSFERMEDIA); + ast_set_flags_to64(user, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_TRANSFERMEDIA); } else if (ast_true(v->value)) { - ast_set_flags_to(user, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, 0); - } else - ast_set_flags_to(user, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_NOTRANSFER); + ast_set_flags_to64(user, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, 0); + } else + ast_set_flags_to64(user, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_NOTRANSFER); } else if (!strcasecmp(v->name, "codecpriority")) { if(!strcasecmp(v->value, "caller")) - ast_set_flag(user, IAX_CODEC_USER_FIRST); + ast_set_flag64(user, IAX_CODEC_USER_FIRST); else if(!strcasecmp(v->value, "disabled")) - ast_set_flag(user, IAX_CODEC_NOPREFS); + ast_set_flag64(user, IAX_CODEC_NOPREFS); else if(!strcasecmp(v->value, "reqonly")) { - ast_set_flag(user, IAX_CODEC_NOCAP); - ast_set_flag(user, IAX_CODEC_NOPREFS); + ast_set_flag64(user, IAX_CODEC_NOCAP); + ast_set_flag64(user, IAX_CODEC_NOPREFS); } } else if (!strcasecmp(v->name, "immediate")) { - ast_set2_flag(user, ast_true(v->value), IAX_IMMEDIATE); + ast_set2_flag64(user, ast_true(v->value), IAX_IMMEDIATE); } else if (!strcasecmp(v->name, "jitterbuffer")) { - ast_set2_flag(user, ast_true(v->value), IAX_USEJITTERBUF); + ast_set2_flag64(user, ast_true(v->value), IAX_USEJITTERBUF); } else if (!strcasecmp(v->name, "forcejitterbuffer")) { - ast_set2_flag(user, ast_true(v->value), IAX_FORCEJITTERBUF); + ast_set2_flag64(user, ast_true(v->value), IAX_FORCEJITTERBUF); } else if (!strcasecmp(v->name, "dbsecret")) { ast_string_field_set(user, dbsecret, v->value); } else if (!strcasecmp(v->name, "secret")) { @@ -11203,29 +11259,29 @@ ast_callerid_split(v->value, name2, sizeof(name2), num2, sizeof(num2)); ast_string_field_set(user, cid_name, name2); ast_string_field_set(user, cid_num, num2); - ast_set_flag(user, IAX_HASCALLERID); + ast_set_flag64(user, IAX_HASCALLERID); } else { - ast_clear_flag(user, IAX_HASCALLERID); + ast_clear_flag64(user, IAX_HASCALLERID); ast_string_field_set(user, cid_name, ""); ast_string_field_set(user, cid_num, ""); } } else if (!strcasecmp(v->name, "fullname")) { if (!ast_strlen_zero(v->value)) { ast_string_field_set(user, cid_name, v->value); - ast_set_flag(user, IAX_HASCALLERID); + ast_set_flag64(user, IAX_HASCALLERID); } else { ast_string_field_set(user, cid_name, ""); if (ast_strlen_zero(user->cid_num)) - ast_clear_flag(user, IAX_HASCALLERID); + ast_clear_flag64(user, IAX_HASCALLERID); } } else if (!strcasecmp(v->name, "cid_number")) { if (!ast_strlen_zero(v->value)) { ast_string_field_set(user, cid_num, v->value); - ast_set_flag(user, IAX_HASCALLERID); + ast_set_flag64(user, IAX_HASCALLERID); } else { ast_string_field_set(user, cid_num, ""); if (ast_strlen_zero(user->cid_name)) - ast_clear_flag(user, IAX_HASCALLERID); + ast_clear_flag64(user, IAX_HASCALLERID); } } else if (!strcasecmp(v->name, "accountcode")) { ast_string_field_set(user, accountcode, v->value); @@ -11252,6 +11308,18 @@ user->maxauthreq = 0; } else if (!strcasecmp(v->name, "adsi")) { user->adsi = ast_true(v->value); + } else if (!strcasecmp(v->name, "connectedline")) { + if (ast_true(v->value)) { + ast_set_flag64(user, IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE); + } else if (!strcasecmp(v->value, "send")) { + ast_clear_flag64(user, IAX_RECVCONNECTEDLINE); + ast_set_flag64(user, IAX_SENDCONNECTEDLINE); + } else if (!strcasecmp(v->value, "receive")) { + ast_clear_flag64(user, IAX_SENDCONNECTEDLINE); + ast_set_flag64(user, IAX_RECVCONNECTEDLINE); + } else { + ast_clear_flag64(user, IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE); + } }/* else if (strcasecmp(v->name,"type")) */ /* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */ v = v->next; @@ -11271,7 +11339,7 @@ user->authmethods = IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT; } } - ast_clear_flag(user, IAX_DELME); + ast_clear_flag64(user, IAX_DELME); } cleanup: if (oldha) @@ -11285,7 +11353,7 @@ { struct iax2_peer *peer = obj; - ast_set_flag(peer, IAX_DELME); + ast_set_flag64(peer, IAX_DELME); return 0; } @@ -11294,7 +11362,7 @@ { struct iax2_user *user = obj; - ast_set_flag(user, IAX_DELME); + ast_set_flag64(user, IAX_DELME); return 0; } @@ -11335,7 +11403,7 @@ i = ao2_iterator_init(users, 0); while ((user = ao2_iterator_next(&i))) { - if (ast_test_flag(user, IAX_DELME) || ast_test_flag(user, IAX_RTCACHEFRIENDS)) { + if (ast_test_flag64(user, IAX_DELME) || ast_test_flag64(user, IAX_RTCACHEFRIENDS)) { ao2_unlink(users, user); } user_unref(user); @@ -11350,7 +11418,7 @@ i = ao2_iterator_init(peers, 0); while ((peer = ao2_iterator_next(&i))) { - if (ast_test_flag(peer, IAX_DELME) || ast_test_flag(peer, IAX_RTCACHEFRIENDS)) { + if (ast_test_flag64(peer, IAX_DELME) || ast_test_flag64(peer, IAX_RTCACHEFRIENDS)) { unlink_peer(peer); } peer_unref(peer); @@ -11366,10 +11434,8 @@ trunkmaxsize = MAX_TRUNKDATA; amaflags = 0; delayreject = 0; - ast_clear_flag((&globalflags), IAX_NOTRANSFER); - ast_clear_flag((&globalflags), IAX_TRANSFERMEDIA); - ast_clear_flag((&globalflags), IAX_USEJITTERBUF); - ast_clear_flag((&globalflags), IAX_FORCEJITTERBUF); + ast_clear_flag64((&globalflags), IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | + IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE); delete_users(); } @@ -11426,13 +11492,13 @@ set_config_destroy(); } - /* Reset global codec prefs */ + /* Reset global codec prefs */ memset(&prefs, 0 , sizeof(struct ast_codec_pref)); - + /* Reset Global Flags */ memset(&globalflags, 0, sizeof(globalflags)); - ast_set_flag(&globalflags, IAX_RTUPDATE); - + ast_set_flag64(&globalflags, IAX_RTUPDATE); + #ifdef SO_NO_CHECK nochecksums = 0; #endif @@ -11544,67 +11610,69 @@ } else if (!strcasecmp(v->name, "encryption")) { iax2_encryption |= get_encrypt_methods(v->value); if (!iax2_encryption) { - ast_clear_flag((&globalflags), IAX_FORCE_ENCRYPT); + ast_clear_flag64((&globalflags), IAX_FORCE_ENCRYPT); } } else if (!strcasecmp(v->name, "forceencryption")) { if (ast_false(v->value)) { - ast_clear_flag((&globalflags), IAX_FORCE_ENCRYPT); + ast_clear_flag64((&globalflags), IAX_FORCE_ENCRYPT); } else { iax2_encryption |= get_encrypt_methods(v->value); if (iax2_encryption) { - ast_set_flag((&globalflags), IAX_FORCE_ENCRYPT); + ast_set_flag64((&globalflags), IAX_FORCE_ENCRYPT); } } } else if (!strcasecmp(v->name, "transfer")) { if (!strcasecmp(v->value, "mediaonly")) { - ast_set_flags_to((&globalflags), IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_TRANSFERMEDIA); + ast_set_flags_to64((&globalflags), IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_TRANSFERMEDIA); } else if (ast_true(v->value)) { - ast_set_flags_to((&globalflags), IAX_NOTRANSFER|IAX_TRANSFERMEDIA, 0); - } else - ast_set_flags_to((&globalflags), IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_NOTRANSFER); + ast_set_flags_to64((&globalflags), IAX_NOTRANSFER|IAX_TRANSFERMEDIA, 0); + } else + ast_set_flags_to64((&globalflags), IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_NOTRANSFER); } else if (!strcasecmp(v->name, "codecpriority")) { if(!strcasecmp(v->value, "caller")) - ast_set_flag((&globalflags), IAX_CODEC_USER_FIRST); + ast_set_flag64((&globalflags), IAX_CODEC_USER_FIRST); else if(!strcasecmp(v->value, "disabled")) - ast_set_flag((&globalflags), IAX_CODEC_NOPREFS); + ast_set_flag64((&globalflags), IAX_CODEC_NOPREFS); else if(!strcasecmp(v->value, "reqonly")) { - ast_set_flag((&globalflags), IAX_CODEC_NOCAP); - ast_set_flag((&globalflags), IAX_CODEC_NOPREFS); + ast_set_flag64((&globalflags), IAX_CODEC_NOCAP); + ast_set_flag64((&globalflags), IAX_CODEC_NOPREFS); } } else if (!strcasecmp(v->name, "jitterbuffer")) - ast_set2_flag((&globalflags), ast_true(v->value), IAX_USEJITTERBUF); + ast_set2_flag64((&globalflags), ast_true(v->value), IAX_USEJITTERBUF); else if (!strcasecmp(v->name, "forcejitterbuffer")) - ast_set2_flag((&globalflags), ast_true(v->value), IAX_FORCEJITTERBUF); + ast_set2_flag64((&globalflags), ast_true(v->value), IAX_FORCEJITTERBUF); else if (!strcasecmp(v->name, "delayreject")) delayreject = ast_true(v->value); else if (!strcasecmp(v->name, "allowfwdownload")) - ast_set2_flag((&globalflags), ast_true(v->value), IAX_ALLOWFWDOWNLOAD); + ast_set2_flag64((&globalflags), ast_true(v->value), IAX_ALLOWFWDOWNLOAD); else if (!strcasecmp(v->name, "rtcachefriends")) - ast_set2_flag((&globalflags), ast_true(v->value), IAX_RTCACHEFRIENDS); + ast_set2_flag64((&globalflags), ast_true(v->value), IAX_RTCACHEFRIENDS); else if (!strcasecmp(v->name, "rtignoreregexpire")) - ast_set2_flag((&globalflags), ast_true(v->value), IAX_RTIGNOREREGEXPIRE); + ast_set2_flag64((&globalflags), ast_true(v->value), IAX_RTIGNOREREGEXPIRE); else if (!strcasecmp(v->name, "rtupdate")) - ast_set2_flag((&globalflags), ast_true(v->value), IAX_RTUPDATE); + ast_set2_flag64((&globalflags), ast_true(v->value), IAX_RTUPDATE); + else if (!strcasecmp(v->name, "rtsavesysname")) + ast_set2_flag64((&globalflags), ast_true(v->value), IAX_RTSAVE_SYSNAME); else if (!strcasecmp(v->name, "trunktimestamps")) - ast_set2_flag(&globalflags, ast_true(v->value), IAX_TRUNKTIMESTAMPS); + ast_set2_flag64(&globalflags, ast_true(v->value), IAX_TRUNKTIMESTAMPS); else if (!strcasecmp(v->name, "rtautoclear")) { int i = atoi(v->value); if(i > 0) global_rtautoclear = i; else i = 0; - ast_set2_flag((&globalflags), i || ast_true(v->value), IAX_RTAUTOCLEAR); + ast_set2_flag64((&globalflags), i || ast_true(v->value), IAX_RTAUTOCLEAR); } else if (!strcasecmp(v->name, "trunkfreq")) { trunkfreq = atoi(v->value); if (trunkfreq < 10) trunkfreq = 10; } else if (!strcasecmp(v->name, "trunkmtu")) { mtuv = atoi(v->value); - if (mtuv == 0 ) - global_max_trunk_mtu = 0; - else if (mtuv >= 172 && mtuv < 4000) - global_max_trunk_mtu = mtuv; - else + if (mtuv == 0 ) + global_max_trunk_mtu = 0; + else if (mtuv >= 172 && mtuv < 4000) + global_max_trunk_mtu = mtuv; + else ast_log(LOG_NOTICE, "trunkmtu value out of bounds (%d) at line %d\n", mtuv, v->lineno); } else if (!strcasecmp(v->name, "trunkmaxsize")) { @@ -11674,6 +11742,18 @@ adsi = ast_true(v->value); } else if (!strcasecmp(v->name, "srvlookup")) { srvlookup = ast_true(v->value); + } else if (!strcasecmp(v->name, "connectedline")) { + if (ast_true(v->value)) { + ast_set_flag64((&globalflags), IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE); + } else if (!strcasecmp(v->value, "send")) { + ast_clear_flag64((&globalflags), IAX_RECVCONNECTEDLINE); + ast_set_flag64((&globalflags), IAX_SENDCONNECTEDLINE); + } else if (!strcasecmp(v->value, "receive")) { + ast_clear_flag64((&globalflags), IAX_SENDCONNECTEDLINE); + ast_set_flag64((&globalflags), IAX_RECVCONNECTEDLINE); + } else { + ast_clear_flag64((&globalflags), IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE); + } } /*else if (strcasecmp(v->name,"type")) */ /* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */ v = v->next; @@ -11728,7 +11808,7 @@ } peer = build_peer(cat, gen, ast_variable_browse(ucfg, cat), 0); if (peer) { - if (ast_test_flag(peer, IAX_DYNAMIC)) + if (ast_test_flag64(peer, IAX_DYNAMIC)) reg_source_db(peer); ao2_link(peers, peer); peer = peer_unref(peer); @@ -11774,7 +11854,7 @@ if (!strcasecmp(utype, "peer") || !strcasecmp(utype, "friend")) { peer = build_peer(cat, ast_variable_browse(cfg, cat), NULL, 0); if (peer) { - if (ast_test_flag(peer, IAX_DYNAMIC)) + if (ast_test_flag64(peer, IAX_DYNAMIC)) reg_source_db(peer); ao2_link(peers, peer); peer = peer_unref(peer); @@ -12211,7 +12291,7 @@ } else if (!strcasecmp(colname, "expire")) { snprintf(buf, len, "%d", peer->expire); } else if (!strcasecmp(colname, "dynamic")) { - ast_copy_string(buf, (ast_test_flag(peer, IAX_DYNAMIC) ? "yes" : "no"), len); + ast_copy_string(buf, (ast_test_flag64(peer, IAX_DYNAMIC) ? "yes" : "no"), len); } else if (!strcasecmp(colname, "callerid_name")) { ast_copy_string(buf, peer->cid_name, len); } else if (!strcasecmp(colname, "callerid_num")) { @@ -12242,7 +12322,7 @@ return 0; } -struct ast_custom_function iaxpeer_function = { +static struct ast_custom_function iaxpeer_function = { .name = "IAXPEER", .read = function_iaxpeer, }; @@ -12495,19 +12575,14 @@ struct ast_context *con; int x; - /* Make sure threads do not hold shared resources when they are canceled */ - - /* Grab the sched lock resource to keep it away from threads about to die */ - /* Cancel the network thread, close the net socket */ if (netthreadid != AST_PTHREADT_NULL) { - AST_LIST_LOCK(&frame_queue); pthread_cancel(netthreadid); - AST_LIST_UNLOCK(&frame_queue); + pthread_kill(netthreadid, SIGURG); pthread_join(netthreadid, NULL); } sched = ast_sched_thread_destroy(sched); - + /* Call for all threads to halt */ AST_LIST_LOCK(&idle_list); while ((thread = AST_LIST_REMOVE_HEAD(&idle_list, list))) @@ -12558,6 +12633,7 @@ if (timer) { ast_timer_close(timer); } + transmit_processor = ast_taskprocessor_unreference(transmit_processor); con = ast_context_find(regcontext); if (con) @@ -12626,19 +12702,23 @@ struct iax2_registry *reg = NULL; peers = ao2_container_alloc(MAX_PEER_BUCKETS, peer_hash_cb, peer_cmp_cb); - if (!peers) + if (!peers) { return AST_MODULE_LOAD_FAILURE; + } + users = ao2_container_alloc(MAX_USER_BUCKETS, user_hash_cb, user_cmp_cb); if (!users) { ao2_ref(peers, -1); return AST_MODULE_LOAD_FAILURE; } + iax_peercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, pvt_hash_cb, pvt_cmp_cb); if (!iax_peercallno_pvts) { ao2_ref(peers, -1); ao2_ref(users, -1); return AST_MODULE_LOAD_FAILURE; } + iax_transfercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, transfercallno_pvt_hash_cb, transfercallno_pvt_cmp_cb); if (!iax_transfercallno_pvts) { ao2_ref(peers, -1); @@ -12646,6 +12726,16 @@ ao2_ref(iax_peercallno_pvts, -1); return AST_MODULE_LOAD_FAILURE; } + + transmit_processor = ast_taskprocessor_get("iax2_transmit", TPS_REF_DEFAULT); + if (!transmit_processor) { + ao2_ref(peers, -1); + ao2_ref(users, -1); + ao2_ref(iax_peercallno_pvts, -1); + ao2_ref(iax_transfercallno_pvts, -1); + return AST_MODULE_LOAD_FAILURE; + } + ast_custom_function_register(&iaxpeer_function); ast_custom_function_register(&iaxvar_function); @@ -12691,10 +12781,10 @@ ast_register_application_xml(papp, iax2_prov_app); - ast_manager_register( "IAXpeers", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_iax2_show_peers, "List IAX Peers" ); - ast_manager_register( "IAXpeerlist", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_iax2_show_peer_list, "List IAX Peers" ); - ast_manager_register( "IAXnetstats", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_iax2_show_netstats, "Show IAX Netstats" ); - ast_manager_register( "IAXregistry", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_iax2_show_registry, "Show IAX registrations"); + ast_manager_register_xml("IAXpeers", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_iax2_show_peers); + ast_manager_register_xml("IAXpeerlist", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_iax2_show_peer_list); + ast_manager_register_xml("IAXnetstats", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_iax2_show_netstats); + ast_manager_register_xml("IAXregistry", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_iax2_show_registry); if ((timer = ast_timer_open())) { ast_timer_set_rate(timer, trunkfreq); Index: channels/chan_oss.c =================================================================== --- a/channels/chan_oss.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/chan_oss.c (.../trunk) (revision 202568) @@ -305,7 +305,7 @@ }; /*! forward declaration */ -static struct chan_oss_pvt *find_desc(char *dev); +static struct chan_oss_pvt *find_desc(const char *dev); static char *oss_active; /*!< the active device */ @@ -367,7 +367,7 @@ /*! * \brief returns a pointer to the descriptor with the given name */ -static struct chan_oss_pvt *find_desc(char *dev) +static struct chan_oss_pvt *find_desc(const char *dev) { struct chan_oss_pvt *o = NULL; @@ -1075,7 +1075,8 @@ static char *console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - char *s = NULL, *mye = NULL, *myc = NULL; + char *s = NULL; + char *mye = NULL, *myc = NULL; struct chan_oss_pvt *o = find_desc(oss_active); if (cmd == CLI_INIT) { @@ -1092,6 +1093,7 @@ if (o->owner) { /* already in a call */ int i; struct ast_frame f = { AST_FRAME_DTMF, 0 }; + const char *s; if (a->argc == e->args) { /* argument is mandatory here */ ast_cli(a->fd, "Already in a call. You can only dial digits until you hangup.\n"); @@ -1126,7 +1128,7 @@ static char *console_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct chan_oss_pvt *o = find_desc(oss_active); - char *s; + const char *s; int toggle = 0; if (cmd == CLI_INIT) { Index: channels/chan_misdn.c =================================================================== --- a/channels/chan_misdn.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/chan_misdn.c (.../trunk) (revision 202568) @@ -1,6 +1,6 @@ /* * Asterisk -- An open source telephony toolkit. - * + * * Copyright (C) 2004 - 2006, Christian Richter * * Christian Richter @@ -29,6 +29,26 @@ * \ingroup channel_drivers */ +/*! + * \note + * To use the CCBS/CCNR supplementary service feature and other + * supplementary services using FACILITY messages requires a + * modified version of mISDN. + * + * \note + * The latest modified mISDN v1.1.x based version is available at: + * http://svn.digium.com/svn/thirdparty/mISDN/trunk + * http://svn.digium.com/svn/thirdparty/mISDNuser/trunk + * + * \note + * Taged versions of the modified mISDN code are available under: + * http://svn.digium.com/svn/thirdparty/mISDN/tags + * http://svn.digium.com/svn/thirdparty/mISDNuser/tags + */ + +/* Define to enable cli commands to generate canned CCBS messages. */ +// #define CCBS_TEST_MESSAGES 1 + /*** MODULEINFO isdnnet misdn @@ -47,6 +67,8 @@ #include #include #include +#include +#include #include "asterisk/channel.h" #include "asterisk/config.h" @@ -72,7 +94,7 @@ #include "chan_misdn_config.h" #include "isdn_lib.h" -char global_tracefile[BUFFERSIZE + 1]; +static char global_tracefile[BUFFERSIZE + 1]; static int g_config_initialized = 0; @@ -88,8 +110,6 @@ ast_mutex_t mutexjb; }; - - /*! \brief allocates the jb-structure and initialize the elements */ struct misdn_jb *misdn_jb_init(int size, int upper_threshold); @@ -111,8 +131,167 @@ /* BEGIN: chan_misdn.h */ -ast_mutex_t release_lock; +#if defined(AST_MISDN_ENHANCEMENTS) +/* + * This timeout duration is to clean up any call completion records that + * are forgotten about by the switch. + */ +#define MISDN_CC_RECORD_AGE_MAX (6UL * 60 * 60) /* seconds */ +#define MISDN_CC_REQUEST_WAIT_MAX 5 /* seconds */ + +/*! + * \brief Caller that initialized call completion services + * + * \details + * This data is the payload for a datastore that is put on the channel that + * initializes call completion services. This datastore is set to be inherited + * by the outbound mISDN channel. When one of these channels hangs up, the + * channel pointer will be set to NULL. That way, we can ensure that we do not + * touch this channel after it gets destroyed. + */ +struct misdn_cc_caller { + /*! \brief The channel that initialized call completion services */ + struct ast_channel *chan; +}; + +struct misdn_cc_notify { + /*! \brief Dialplan: Notify extension priority */ + int priority; + + /*! \brief Dialplan: Notify extension context */ + char context[AST_MAX_CONTEXT]; + + /*! \brief Dialplan: Notify extension number (User-A) */ + char exten[AST_MAX_EXTENSION]; +}; + +/*! \brief mISDN call completion record */ +struct misdn_cc_record { + /*! \brief Call completion record linked list */ + AST_LIST_ENTRY(misdn_cc_record) list; + + /*! \brief Time the record was created. */ + time_t time_created; + + /*! \brief MISDN_CC_RECORD_ID value */ + long record_id; + + /*! + * \brief Logical Layer 1 port associated with this + * call completion record + */ + int port; + + /*! \brief TRUE if point-to-point mode (CCBS-T/CCNR-T mode) */ + int ptp; + + /*! \brief Mode specific parameters */ + union { + /*! \brief point-to-point specific parameters. */ + struct { + /*! + * \brief Call-completion signaling link. + * NULL if signaling link not established. + */ + struct misdn_bchannel *bc; + + /*! + * \brief TRUE if we requested the request retention option + * to be enabled. + */ + int requested_retention; + + /*! + * \brief TRUE if the request retention option is enabled. + */ + int retention_enabled; + } ptp; + + /*! \brief point-to-multi-point specific parameters. */ + struct { + /*! \brief CallLinkageID (valid when port determined) */ + int linkage_id; + + /*! \breif CCBSReference (valid when activated is TRUE) */ + int reference_id; + + /*! \brief globalRecall(0), specificRecall(1) */ + int recall_mode; + } ptmp; + } mode; + + /*! \brief TRUE if call completion activated */ + int activated; + + /*! \brief Outstanding message ID (valid when outstanding_message) */ + int invoke_id; + + /*! \brief TRUE if waiting for a response from a message (invoke_id is valid) */ + int outstanding_message; + + /*! \brief TRUE if activation has been requested */ + int activation_requested; + + /*! + * \brief TRUE if User-A is free + * \note PTMP - Used to answer CCBSStatusRequest. + * PTP - Determines how to respond to CCBS_T_RemoteUserFree. + */ + int party_a_free; + + /*! \brief Error code received from last outstanding message. */ + enum FacErrorCode error_code; + + /*! \brief Reject code received from last outstanding message. */ + enum FacRejectCode reject_code; + + /*! + * \brief Saved struct misdn_bchannel call information when + * attempted to call User-B + */ + struct { + /*! \brief User-A caller id information */ + struct misdn_party_id caller; + + /*! \brief User-B number information */ + struct misdn_party_dialing dialed; + + /*! \brief The BC, HLC (optional) and LLC (optional) contents from the SETUP message. */ + struct Q931_Bc_Hlc_Llc setup_bc_hlc_llc; + + /*! \brief SETUP message bearer capability field code value */ + int capability; + + /*! \brief TRUE if call made in digital HDLC mode */ + int hdlc; + } redial; + + /*! \brief Dialplan location to indicate User-B free and User-A is free */ + struct misdn_cc_notify remote_user_free; + + /*! \brief Dialplan location to indicate User-B free and User-A is busy */ + struct misdn_cc_notify b_free; +}; + +/*! \brief mISDN call completion record database */ +static AST_LIST_HEAD_STATIC(misdn_cc_records_db, misdn_cc_record); +/*! \brief Next call completion record ID to use */ +static __u16 misdn_cc_record_id; +/*! \brief Next invoke ID to use */ +static __s16 misdn_invoke_id; + +static const char misdn_no_response_from_network[] = "No response from network"; +static const char misdn_cc_record_not_found[] = "Call completion record not found"; + +/* mISDN channel variable names */ +#define MISDN_CC_RECORD_ID "MISDN_CC_RECORD_ID" +#define MISDN_CC_STATUS "MISDN_CC_STATUS" +#define MISDN_ERROR_MSG "MISDN_ERROR_MSG" +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +static ast_mutex_t release_lock; + enum misdn_chan_state { MISDN_NOTHING = 0, /*!< at beginning */ MISDN_WAITING4DIGS, /*!< when waiting for info */ @@ -126,7 +305,7 @@ MISDN_ALERTING, /*!< when Alerting */ MISDN_BUSY, /*!< when BUSY */ MISDN_CONNECTED, /*!< when connected */ - MISDN_PRECONNECTED, /*!< when connected */ + MISDN_PRECONNECTED, /*!< when connected (Noone sets this state) */ MISDN_DISCONNECTED, /*!< when connected */ MISDN_RELEASED, /*!< when connected */ MISDN_BRIDGED, /*!< when bridged */ @@ -135,21 +314,22 @@ MISDN_HUNGUP_FROM_AST, /*!< when DISCONNECT/RELEASE/REL_COMP came out of misdn_hangup */ MISDN_HOLDED, /*!< when on hold */ MISDN_HOLD_DISCONNECT, /*!< when on hold */ - }; +/*! Asterisk created the channel (outgoing call) */ #define ORG_AST 1 +/*! mISDN created the channel (incoming call) */ #define ORG_MISDN 2 struct hold_info { /*! - * \brief Logical port the channel call record is HOLDED on - * because the B channel is no longer associated. + * \brief Logical port the channel call record is HOLDED on + * because the B channel is no longer associated. */ int port; /*! - * \brief Original B channel number the HOLDED call was using. + * \brief Original B channel number the HOLDED call was using. * \note Used only for debug display messages. */ int channel; @@ -159,18 +339,18 @@ * \brief Channel call record structure */ struct chan_list { - /*! + /*! * \brief The "allowed_bearers" string read in from /etc/asterisk/misdn.conf */ char allowed_bearers[BUFFERSIZE + 1]; - - /*! + + /*! * \brief State of the channel */ enum misdn_chan_state state; - /*! - * \brief TRUE if a hangup needs to be queued + /*! + * \brief TRUE if a hangup needs to be queued * \note This is a debug flag only used to catch calls to hangup_chan() that are already hungup. */ int need_queue_hangup; @@ -184,30 +364,30 @@ * \brief TRUE if we could send an AST_CONTROL_BUSY if needed. */ int need_busy; - + /*! * \brief Who originally created this channel. ORG_AST or ORG_MISDN */ int originator; - /*! + /*! * \brief TRUE of we are not to respond immediately to a SETUP message. Check the dialplan first. * \note The "noautorespond_on_setup" boolean read in from /etc/asterisk/misdn.conf */ int noautorespond_on_setup; - + int norxtone; /*!< Boolean assigned values but the value is not used. */ /*! * \brief TRUE if we are not to generate tones (Playtones) */ - int notxtone; + int notxtone; /*! * \brief TRUE if echo canceller is enabled. Value is toggled. */ int toggle_ec; - + /*! * \brief TRUE if you want to send Tone Indications to an incoming * ISDN channel on a TE Port. @@ -222,8 +402,8 @@ int ignore_dtmf; /*! - * \brief Pipe file descriptor handles array. - * Read from pipe[0], write to pipe[1] + * \brief Pipe file descriptor handles array. + * Read from pipe[0], write to pipe[1] */ int pipe[2]; @@ -282,48 +462,56 @@ /*! * \brief Allocated jitterbuffer controller * \note misdn_jb_init() creates the jitterbuffer. - * \note Must use misdn_jb_destroy() to clean up. + * \note Must use misdn_jb_destroy() to clean up. */ struct misdn_jb *jb; - + /*! * \brief Allocated DSP controller * \note ast_dsp_new() creates the DSP controller. - * \note Must use ast_dsp_free() to clean up. + * \note Must use ast_dsp_free() to clean up. */ struct ast_dsp *dsp; /*! * \brief Allocated audio frame sample translator * \note ast_translator_build_path() creates the translator path. - * \note Must use ast_translator_free_path() to clean up. + * \note Must use ast_translator_free_path() to clean up. */ struct ast_trans_pvt *trans; - + /*! * \brief Associated Asterisk channel structure. */ struct ast_channel * ast; - //int dummy; /* Not used */ - /*! * \brief Associated B channel structure. */ struct misdn_bchannel *bc; +#if defined(AST_MISDN_ENHANCEMENTS) /*! + * \brief Peer channel for which call completion was initialized. + */ + struct misdn_cc_caller *peer; + + /*! \brief Associated call completion record ID (-1 if not associated) */ + long record_id; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + + /*! * \brief HOLDED channel information */ struct hold_info hold_info; - /*! - * \brief From associated B channel: Layer 3 process ID - * \note Used to find the HOLDED channel call record when retrieving a call. + /*! + * \brief From associated B channel: Layer 3 process ID + * \note Used to find the HOLDED channel call record when retrieving a call. */ unsigned int l3id; - /*! + /*! * \brief From associated B channel: B Channel mISDN driver layer ID from mISDN_get_layerid() * \note Used only for debug display messages. */ @@ -341,10 +529,6 @@ */ char mohinterpret[MAX_MUSICCLASS]; -#if 0 - int zero_read_cnt; /* Not used */ -#endif - /*! * \brief Number of outgoing audio frames dropped since last debug gripe message. */ @@ -363,24 +547,24 @@ int nttimeout; /*! - * \brief Other channel call record PID - * \note Value imported from Asterisk environment variable MISDN_PID + * \brief Other channel call record PID + * \note Value imported from Asterisk environment variable MISDN_PID */ int other_pid; /*! * \brief Bridged other channel call record - * \note Pointer set when other_pid imported from Asterisk environment + * \note Pointer set when other_pid imported from Asterisk environment * variable MISDN_PID by either side. */ struct chan_list *other_ch; /*! * \brief Tone zone sound used for dialtone generation. - * \note Used as a boolean. Non-NULL to prod generation if enabled. + * \note Used as a boolean. Non-NULL to prod generation if enabled. */ struct ast_tone_zone_sound *ts; - + /*! * \brief Enables overlap dialing for the set amount of seconds. (0 = Disabled) * \note The "overlapdial" value read in from /etc/asterisk/misdn.conf @@ -402,18 +586,10 @@ */ struct timeval overlap_tv; -#if 0 - struct chan_list *peer; /* Not used */ -#endif - /*! * \brief Next channel call record in the list. */ struct chan_list *next; -#if 0 - struct chan_list *prev; /* Not used */ - struct chan_list *first; /* Not used */ -#endif }; @@ -424,13 +600,14 @@ void import_ch(struct ast_channel *chan, struct misdn_bchannel *bc, struct chan_list *ch); static struct ast_frame *process_ast_dsp(struct chan_list *tmp, struct ast_frame *frame); -static struct robin_list { +struct robin_list { char *group; int port; int channel; struct robin_list *next; struct robin_list *prev; -} *robin = NULL; +}; +static struct robin_list *robin = NULL; static inline void free_robin_list_r(struct robin_list *r) @@ -450,7 +627,7 @@ robin = NULL; } -static struct robin_list* get_robin_position(char *group) +static struct robin_list *get_robin_position(char *group) { struct robin_list *new; struct robin_list *iter = robin; @@ -481,13 +658,12 @@ __attribute__((format(printf, 3, 4))); static struct ast_channel *misdn_new(struct chan_list *cl, int state, char *exten, char *callerid, int format, int port, int c); -static void send_digit_to_chan(struct chan_list *cl, char digit ); +static void send_digit_to_chan(struct chan_list *cl, char digit); static void hangup_chan(struct chan_list *ch); static int pbx_start_chan(struct chan_list *ch); #define MISDN_ASTERISK_TECH_PVT(ast) ast->tech_pvt -#define MISDN_ASTERISK_PVT(ast) 1 #include "asterisk/strings.h" @@ -507,13 +683,11 @@ static int *misdn_in_calls; static int *misdn_out_calls; -struct chan_list dummy_cl; - /*! * \brief Global channel call record list head. */ -struct chan_list *cl_te=NULL; -ast_mutex_t cl_te_lock; +static struct chan_list *cl_te=NULL; +static ast_mutex_t cl_te_lock; static enum event_response_e cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data); @@ -533,13 +707,17 @@ static int stop_bc_tones(struct chan_list *cl); static void release_chan(struct misdn_bchannel *bc); -static int misdn_check_l2l1(struct ast_channel *chan, void *data); -static int misdn_set_opt_exec(struct ast_channel *chan, void *data); -static int misdn_facility_exec(struct ast_channel *chan, void *data); +#if defined(AST_MISDN_ENHANCEMENTS) +static const char misdn_command_name[] = "misdn_command"; +static int misdn_command_exec(struct ast_channel *chan, const char *data); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ +static int misdn_check_l2l1(struct ast_channel *chan, const char *data); +static int misdn_set_opt_exec(struct ast_channel *chan, const char *data); +static int misdn_facility_exec(struct ast_channel *chan, const char *data); int chan_misdn_jb_empty(struct misdn_bchannel *bc, char *buf, int len); -void debug_numplan(int port, int numplan, char *type); +void debug_numtype(int port, int numtype, char *type); int add_out_calls(int port); int add_in_calls(int port); @@ -555,34 +733,1583 @@ /*************** Helpers *****************/ -static struct chan_list * get_chan_by_ast(struct ast_channel *ast) +static struct chan_list *get_chan_by_ast(struct ast_channel *ast) { struct chan_list *tmp; - + for (tmp = cl_te; tmp; tmp = tmp->next) { if (tmp->ast == ast) { return tmp; } } - + return NULL; } -static struct chan_list * get_chan_by_ast_name(char *name) +static struct chan_list *get_chan_by_ast_name(const char *name) { struct chan_list *tmp; - + for (tmp = cl_te; tmp; tmp = tmp->next) { if (tmp->ast && strcmp(tmp->ast->name, name) == 0) { return tmp; } } - + return NULL; } +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Destroy the misdn_cc_ds_info datastore payload + * + * \param[in] data the datastore payload, a reference to an misdn_cc_caller + * + * \details + * Since the payload is a reference to an astobj2 object, we just decrement its + * reference count. Before doing so, we NULL out the channel pointer inside of + * the misdn_cc_caller instance. This function will be called in one of two + * cases. In both cases, we no longer need the channel pointer: + * + * - The original channel that initialized call completion services, the same + * channel that is stored here, has been destroyed early. This could happen + * if it transferred the mISDN channel, for example. + * + * - The mISDN channel that had this datastore inherited on to it is now being + * destroyed. If this is the case, then the call completion events have + * already occurred and the appropriate channel variables have already been + * set on the original channel that requested call completion services. + * + * \return Nothing + */ +static void misdn_cc_ds_destroy(void *data) +{ + struct misdn_cc_caller *cc_caller = data; + ao2_lock(cc_caller); + cc_caller->chan = NULL; + ao2_unlock(cc_caller); + ao2_ref(cc_caller, -1); +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Duplicate the misdn_cc_ds_info datastore payload + * + * \param[in] data the datastore payload, a reference to an misdn_cc_caller + * + * \details + * All we need to do is bump the reference count and return the same instance. + * + * \return A reference to an instance of a misdn_cc_caller + */ +static void *misdn_cc_ds_duplicate(void *data) +{ + struct misdn_cc_caller *cc_caller = data; + + ao2_ref(cc_caller, +1); + + return cc_caller; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static const struct ast_datastore_info misdn_cc_ds_info = { + .type = "misdn_cc", + .destroy = misdn_cc_ds_destroy, + .duplicate = misdn_cc_ds_duplicate, +}; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Set a channel var on the peer channel for call completion services + * + * \param[in] peer The peer that initialized call completion services + * \param[in] var The variable name to set + * \param[in] value The variable value to set + * + * This function may be called from outside of the channel thread. It handles + * the fact that the peer channel may be hung up and destroyed at any time. + * + * \return nothing + */ +static void misdn_cc_set_peer_var(struct misdn_cc_caller *peer, const char *var, + const char *value) +{ + ao2_lock(peer); + + /*! \todo XXX This nastiness can go away once ast_channel is ref counted! */ + while (peer->chan && ast_channel_trylock(peer->chan)) { + ao2_unlock(peer); + sched_yield(); + ao2_lock(peer); + } + + if (peer->chan) { + pbx_builtin_setvar_helper(peer->chan, var, value); + ast_channel_unlock(peer->chan); + } + + ao2_unlock(peer); +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Get a reference to the CC caller if it exists + */ +static struct misdn_cc_caller *misdn_cc_caller_get(struct ast_channel *chan) +{ + struct ast_datastore *datastore; + struct misdn_cc_caller *cc_caller; + + ast_channel_lock(chan); + + if (!(datastore = ast_channel_datastore_find(chan, &misdn_cc_ds_info, NULL))) { + ast_channel_unlock(chan); + return NULL; + } + + ao2_ref(datastore->data, +1); + cc_caller = datastore->data; + + ast_channel_unlock(chan); + + return cc_caller; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Find the call completion record given the record id. + * + * \param record_id + * + * \retval pointer to found call completion record + * \retval NULL if not found + * + * \note Assumes the misdn_cc_records_db lock is already obtained. + */ +static struct misdn_cc_record *misdn_cc_find_by_id(long record_id) +{ + struct misdn_cc_record *current; + + AST_LIST_TRAVERSE(&misdn_cc_records_db, current, list) { + if (current->record_id == record_id) { + /* Found the record */ + break; + } + } + + return current; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Find the call completion record given the port and call linkage id. + * + * \param port Logical port number + * \param linkage_id Call linkage ID number from switch. + * + * \retval pointer to found call completion record + * \retval NULL if not found + * + * \note Assumes the misdn_cc_records_db lock is already obtained. + */ +static struct misdn_cc_record *misdn_cc_find_by_linkage(int port, int linkage_id) +{ + struct misdn_cc_record *current; + + AST_LIST_TRAVERSE(&misdn_cc_records_db, current, list) { + if (current->port == port + && !current->ptp + && current->mode.ptmp.linkage_id == linkage_id) { + /* Found the record */ + break; + } + } + + return current; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Find the call completion record given the port and outstanding invocation id. + * + * \param port Logical port number + * \param invoke_id Outstanding message invocation ID number. + * + * \retval pointer to found call completion record + * \retval NULL if not found + * + * \note Assumes the misdn_cc_records_db lock is already obtained. + */ +static struct misdn_cc_record *misdn_cc_find_by_invoke(int port, int invoke_id) +{ + struct misdn_cc_record *current; + + AST_LIST_TRAVERSE(&misdn_cc_records_db, current, list) { + if (current->outstanding_message + && current->invoke_id == invoke_id + && current->port == port) { + /* Found the record */ + break; + } + } + + return current; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Find the call completion record given the port and CCBS reference id. + * + * \param port Logical port number + * \param reference_id CCBS reference ID number from switch. + * + * \retval pointer to found call completion record + * \retval NULL if not found + * + * \note Assumes the misdn_cc_records_db lock is already obtained. + */ +static struct misdn_cc_record *misdn_cc_find_by_reference(int port, int reference_id) +{ + struct misdn_cc_record *current; + + AST_LIST_TRAVERSE(&misdn_cc_records_db, current, list) { + if (current->activated + && current->port == port + && !current->ptp + && current->mode.ptmp.reference_id == reference_id) { + /* Found the record */ + break; + } + } + + return current; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Find the call completion record given the B channel pointer + * + * \param bc B channel control structure pointer. + * + * \retval pointer to found call completion record + * \retval NULL if not found + * + * \note Assumes the misdn_cc_records_db lock is already obtained. + */ +static struct misdn_cc_record *misdn_cc_find_by_bc(const struct misdn_bchannel *bc) +{ + struct misdn_cc_record *current; + + if (bc) { + AST_LIST_TRAVERSE(&misdn_cc_records_db, current, list) { + if (current->ptp + && current->mode.ptp.bc == bc) { + /* Found the record */ + break; + } + } + } else { + current = NULL; + } + + return current; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Delete the given call completion record + * + * \param doomed Call completion record to destroy + * + * \return Nothing + * + * \note Assumes the misdn_cc_records_db lock is already obtained. + */ +static void misdn_cc_delete(struct misdn_cc_record *doomed) +{ + struct misdn_cc_record *current; + + AST_LIST_TRAVERSE_SAFE_BEGIN(&misdn_cc_records_db, current, list) { + if (current == doomed) { + AST_LIST_REMOVE_CURRENT(list); + ast_free(current); + return; + } + } + AST_LIST_TRAVERSE_SAFE_END; + + /* The doomed node is not in the call completion database */ +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Delete all old call completion records + * + * \return Nothing + * + * \note Assumes the misdn_cc_records_db lock is already obtained. + */ +static void misdn_cc_remove_old(void) +{ + struct misdn_cc_record *current; + time_t now; + + now = time(NULL); + AST_LIST_TRAVERSE_SAFE_BEGIN(&misdn_cc_records_db, current, list) { + if (MISDN_CC_RECORD_AGE_MAX < now - current->time_created) { + if (current->ptp && current->mode.ptp.bc) { + /* Close the old call-completion signaling link */ + current->mode.ptp.bc->fac_out.Function = Fac_None; + current->mode.ptp.bc->out_cause = AST_CAUSE_NORMAL_CLEARING; + misdn_lib_send_event(current->mode.ptp.bc, EVENT_RELEASE_COMPLETE); + } + + /* Remove the old call completion record */ + AST_LIST_REMOVE_CURRENT(list); + ast_free(current); + } + } + AST_LIST_TRAVERSE_SAFE_END; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Allocate the next record id. + * + * \retval New record id on success. + * \retval -1 on error. + * + * \note Assumes the misdn_cc_records_db lock is already obtained. + */ +static long misdn_cc_record_id_new(void) +{ + long record_id; + long first_id; + + record_id = ++misdn_cc_record_id; + first_id = record_id; + while (misdn_cc_find_by_id(record_id)) { + record_id = ++misdn_cc_record_id; + if (record_id == first_id) { + /* + * We have a resource leak. + * We should never need to allocate 64k records. + */ + chan_misdn_log(0, 0, " --> ERROR Too many call completion records!\n"); + record_id = -1; + break; + } + } + + return record_id; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Create a new call completion record + * + * \retval pointer to new call completion record + * \retval NULL if failed + * + * \note Assumes the misdn_cc_records_db lock is already obtained. + */ +static struct misdn_cc_record *misdn_cc_new(void) +{ + struct misdn_cc_record *cc_record; + long record_id; + + misdn_cc_remove_old(); + + cc_record = ast_calloc(1, sizeof(*cc_record)); + if (cc_record) { + record_id = misdn_cc_record_id_new(); + if (record_id < 0) { + ast_free(cc_record); + return NULL; + } + + /* Initialize the new record */ + cc_record->record_id = record_id; + cc_record->port = -1;/* Invalid port so it will never be found this way */ + cc_record->invoke_id = ++misdn_invoke_id; + cc_record->party_a_free = 1;/* Default User-A as free */ + cc_record->error_code = FacError_None; + cc_record->reject_code = FacReject_None; + cc_record->time_created = time(NULL); + + /* Insert the new record into the database */ + AST_LIST_INSERT_HEAD(&misdn_cc_records_db, cc_record, list); + } + return cc_record; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Destroy the call completion record database + * + * \return Nothing + */ +static void misdn_cc_destroy(void) +{ + struct misdn_cc_record *current; + + while ((current = AST_LIST_REMOVE_HEAD(&misdn_cc_records_db, list))) { + /* Do a misdn_cc_delete(current) inline */ + ast_free(current); + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Initialize the call completion record database + * + * \return Nothing + */ +static void misdn_cc_init(void) +{ + misdn_cc_record_id = 0; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Check the status of an outstanding invocation request. + * + * \param data Points to an integer containing the call completion record id. + * + * \retval 0 if got a response. + * \retval -1 if no response yet. + */ +static int misdn_cc_response_check(void *data) +{ + int not_responded; + struct misdn_cc_record *cc_record; + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_id(*(long *) data); + if (cc_record) { + if (cc_record->outstanding_message) { + not_responded = -1; + } else { + not_responded = 0; + } + } else { + /* No record so there is no response to check. */ + not_responded = 0; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + + return not_responded; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Wait for a response from the switch for an outstanding + * invocation request. + * + * \param chan Asterisk channel to operate upon. + * \param wait_seconds Number of seconds to wait + * \param record_id Call completion record ID. + * + * \return Nothing + */ +static void misdn_cc_response_wait(struct ast_channel *chan, int wait_seconds, long record_id) +{ + unsigned count; + + for (count = 2 * MISDN_CC_REQUEST_WAIT_MAX; count--;) { + /* Sleep in 500 ms increments */ + if (ast_safe_sleep_conditional(chan, 500, misdn_cc_response_check, &record_id) != 0) { + /* We got hung up or our response came in. */ + break; + } + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Convert the mISDN reject code to a string + * + * \param code mISDN reject code. + * + * \return The mISDN reject code as a string + */ +static const char *misdn_to_str_reject_code(enum FacRejectCode code) +{ + static const struct { + enum FacRejectCode code; + char *name; + } arr[] = { +/* *INDENT-OFF* */ + { FacReject_None, "No reject occurred" }, + { FacReject_Unknown, "Unknown reject code" }, + + { FacReject_Gen_UnrecognizedComponent, "General: Unrecognized Component" }, + { FacReject_Gen_MistypedComponent, "General: Mistyped Component" }, + { FacReject_Gen_BadlyStructuredComponent, "General: Badly Structured Component" }, + + { FacReject_Inv_DuplicateInvocation, "Invoke: Duplicate Invocation" }, + { FacReject_Inv_UnrecognizedOperation, "Invoke: Unrecognized Operation" }, + { FacReject_Inv_MistypedArgument, "Invoke: Mistyped Argument" }, + { FacReject_Inv_ResourceLimitation, "Invoke: Resource Limitation" }, + { FacReject_Inv_InitiatorReleasing, "Invoke: Initiator Releasing" }, + { FacReject_Inv_UnrecognizedLinkedID, "Invoke: Unrecognized Linked ID" }, + { FacReject_Inv_LinkedResponseUnexpected, "Invoke: Linked Response Unexpected" }, + { FacReject_Inv_UnexpectedChildOperation, "Invoke: Unexpected Child Operation" }, + + { FacReject_Res_UnrecognizedInvocation, "Result: Unrecognized Invocation" }, + { FacReject_Res_ResultResponseUnexpected, "Result: Result Response Unexpected" }, + { FacReject_Res_MistypedResult, "Result: Mistyped Result" }, + + { FacReject_Err_UnrecognizedInvocation, "Error: Unrecognized Invocation" }, + { FacReject_Err_ErrorResponseUnexpected, "Error: Error Response Unexpected" }, + { FacReject_Err_UnrecognizedError, "Error: Unrecognized Error" }, + { FacReject_Err_UnexpectedError, "Error: Unexpected Error" }, + { FacReject_Err_MistypedParameter, "Error: Mistyped Parameter" }, +/* *INDENT-ON* */ + }; + + unsigned index; + + for (index = 0; index < ARRAY_LEN(arr); ++index) { + if (arr[index].code == code) { + return arr[index].name; + } + } + + return "unknown"; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Convert the mISDN error code to a string + * + * \param code mISDN error code. + * + * \return The mISDN error code as a string + */ +static const char *misdn_to_str_error_code(enum FacErrorCode code) +{ + static const struct { + enum FacErrorCode code; + char *name; + } arr[] = { +/* *INDENT-OFF* */ + { FacError_None, "No error occurred" }, + { FacError_Unknown, "Unknown OID error code" }, + + { FacError_Gen_NotSubscribed, "General: Not Subscribed" }, + { FacError_Gen_NotAvailable, "General: Not Available" }, + { FacError_Gen_NotImplemented, "General: Not Implemented" }, + { FacError_Gen_InvalidServedUserNr, "General: Invalid Served User Number" }, + { FacError_Gen_InvalidCallState, "General: Invalid Call State" }, + { FacError_Gen_BasicServiceNotProvided, "General: Basic Service Not Provided" }, + { FacError_Gen_NotIncomingCall, "General: Not Incoming Call" }, + { FacError_Gen_SupplementaryServiceInteractionNotAllowed,"General: Supplementary Service Interaction Not Allowed" }, + { FacError_Gen_ResourceUnavailable, "General: Resource Unavailable" }, + + { FacError_Div_InvalidDivertedToNr, "Diversion: Invalid Diverted To Number" }, + { FacError_Div_SpecialServiceNr, "Diversion: Special Service Number" }, + { FacError_Div_DiversionToServedUserNr, "Diversion: Diversion To Served User Number" }, + { FacError_Div_IncomingCallAccepted, "Diversion: Incoming Call Accepted" }, + { FacError_Div_NumberOfDiversionsExceeded, "Diversion: Number Of Diversions Exceeded" }, + { FacError_Div_NotActivated, "Diversion: Not Activated" }, + { FacError_Div_RequestAlreadyAccepted, "Diversion: Request Already Accepted" }, + + { FacError_AOC_NoChargingInfoAvailable, "AOC: No Charging Info Available" }, + + { FacError_CCBS_InvalidCallLinkageID, "CCBS: Invalid Call Linkage ID" }, + { FacError_CCBS_InvalidCCBSReference, "CCBS: Invalid CCBS Reference" }, + { FacError_CCBS_LongTermDenial, "CCBS: Long Term Denial" }, + { FacError_CCBS_ShortTermDenial, "CCBS: Short Term Denial" }, + { FacError_CCBS_IsAlreadyActivated, "CCBS: Is Already Activated" }, + { FacError_CCBS_AlreadyAccepted, "CCBS: Already Accepted" }, + { FacError_CCBS_OutgoingCCBSQueueFull, "CCBS: Outgoing CCBS Queue Full" }, + { FacError_CCBS_CallFailureReasonNotBusy, "CCBS: Call Failure Reason Not Busy" }, + { FacError_CCBS_NotReadyForCall, "CCBS: Not Ready For Call" }, + + { FacError_CCBS_T_LongTermDenial, "CCBS-T: Long Term Denial" }, + { FacError_CCBS_T_ShortTermDenial, "CCBS-T: Short Term Denial" }, + + { FacError_ECT_LinkIdNotAssignedByNetwork, "ECT: Link ID Not Assigned By Network" }, +/* *INDENT-ON* */ + }; + + unsigned index; + + for (index = 0; index < ARRAY_LEN(arr); ++index) { + if (arr[index].code == code) { + return arr[index].name; + } + } + + return "unknown"; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Convert mISDN redirecting reason to diversion reason. + * + * \param reason mISDN redirecting reason code. + * + * \return Supported diversion reason code. + */ +static unsigned misdn_to_diversion_reason(enum mISDN_REDIRECTING_REASON reason) +{ + unsigned diversion_reason; + + switch (reason) { + case mISDN_REDIRECTING_REASON_CALL_FWD: + diversion_reason = 1;/* cfu */ + break; + case mISDN_REDIRECTING_REASON_CALL_FWD_BUSY: + diversion_reason = 2;/* cfb */ + break; + case mISDN_REDIRECTING_REASON_NO_REPLY: + diversion_reason = 3;/* cfnr */ + break; + default: + diversion_reason = 0;/* unknown */ + break; + } + + return diversion_reason; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Convert diversion reason to mISDN redirecting reason + * + * \param diversion_reason Diversion reason to convert + * + * \return Supported redirecting reason code. + */ +static enum mISDN_REDIRECTING_REASON diversion_reason_to_misdn(unsigned diversion_reason) +{ + enum mISDN_REDIRECTING_REASON reason; + + switch (diversion_reason) { + case 1:/* cfu */ + reason = mISDN_REDIRECTING_REASON_CALL_FWD; + break; + case 2:/* cfb */ + reason = mISDN_REDIRECTING_REASON_CALL_FWD_BUSY; + break; + case 3:/* cfnr */ + reason = mISDN_REDIRECTING_REASON_NO_REPLY; + break; + default: + reason = mISDN_REDIRECTING_REASON_UNKNOWN; + break; + } + + return reason; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Convert the mISDN presentation to PresentedNumberUnscreened type + * + * \param presentation mISDN presentation to convert + * \param number_present TRUE if the number is present + * + * \return PresentedNumberUnscreened type + */ +static unsigned misdn_to_PresentedNumberUnscreened_type(int presentation, int number_present) +{ + unsigned type; + + switch (presentation) { + case 0:/* allowed */ + if (number_present) { + type = 0;/* presentationAllowedNumber */ + } else { + type = 2;/* numberNotAvailableDueToInterworking */ + } + break; + case 1:/* restricted */ + if (number_present) { + type = 3;/* presentationRestrictedNumber */ + } else { + type = 1;/* presentationRestricted */ + } + break; + default: + type = 2;/* numberNotAvailableDueToInterworking */ + break; + } + + return type; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Convert the PresentedNumberUnscreened type to mISDN presentation + * + * \param type PresentedNumberUnscreened type + * + * \return mISDN presentation + */ +static int PresentedNumberUnscreened_to_misdn_pres(unsigned type) +{ + int presentation; + + switch (type) { + default: + case 0:/* presentationAllowedNumber */ + presentation = 0;/* allowed */ + break; + + case 1:/* presentationRestricted */ + case 3:/* presentationRestrictedNumber */ + presentation = 1;/* restricted */ + break; + + case 2:/* numberNotAvailableDueToInterworking */ + presentation = 2;/* unavailable */ + break; + } + + return presentation; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Convert the mISDN numbering plan to PartyNumber numbering plan + * + * \param number_plan mISDN numbering plan + * + * \return PartyNumber numbering plan + */ +static unsigned misdn_to_PartyNumber_plan(enum mISDN_NUMBER_PLAN number_plan) +{ + unsigned party_plan; + + switch (number_plan) { + default: + case NUMPLAN_UNKNOWN: + party_plan = 0;/* unknown */ + break; + + case NUMPLAN_ISDN: + party_plan = 1;/* public */ + break; + + case NUMPLAN_DATA: + party_plan = 3;/* data */ + break; + + case NUMPLAN_TELEX: + party_plan = 4;/* telex */ + break; + + case NUMPLAN_NATIONAL: + party_plan = 8;/* nationalStandard */ + break; + + case NUMPLAN_PRIVATE: + party_plan = 5;/* private */ + break; + } + + return party_plan; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Convert PartyNumber numbering plan to mISDN numbering plan + * + * \param party_plan PartyNumber numbering plan + * + * \return mISDN numbering plan + */ +static enum mISDN_NUMBER_PLAN PartyNumber_to_misdn_plan(unsigned party_plan) +{ + enum mISDN_NUMBER_PLAN number_plan; + + switch (party_plan) { + default: + case 0:/* unknown */ + number_plan = NUMPLAN_UNKNOWN; + break; + case 1:/* public */ + number_plan = NUMPLAN_ISDN; + break; + case 3:/* data */ + number_plan = NUMPLAN_DATA; + break; + case 4:/* telex */ + number_plan = NUMPLAN_TELEX; + break; + case 8:/* nationalStandard */ + number_plan = NUMPLAN_NATIONAL; + break; + case 5:/* private */ + number_plan = NUMPLAN_PRIVATE; + break; + } + + return number_plan; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Convert mISDN type-of-number to PartyNumber public type-of-number + * + * \param ton mISDN type-of-number + * + * \return PartyNumber public type-of-number + */ +static unsigned misdn_to_PartyNumber_ton_public(enum mISDN_NUMBER_TYPE ton) +{ + unsigned party_ton; + + switch (ton) { + default: + case NUMTYPE_UNKNOWN: + party_ton = 0;/* unknown */ + break; + + case NUMTYPE_INTERNATIONAL: + party_ton = 1;/* internationalNumber */ + break; + + case NUMTYPE_NATIONAL: + party_ton = 2;/* nationalNumber */ + break; + + case NUMTYPE_NETWORK_SPECIFIC: + party_ton = 3;/* networkSpecificNumber */ + break; + + case NUMTYPE_SUBSCRIBER: + party_ton = 4;/* subscriberNumber */ + break; + + case NUMTYPE_ABBREVIATED: + party_ton = 6;/* abbreviatedNumber */ + break; + } + + return party_ton; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Convert the PartyNumber public type-of-number to mISDN type-of-number + * + * \param party_ton PartyNumber public type-of-number + * + * \return mISDN type-of-number + */ +static enum mISDN_NUMBER_TYPE PartyNumber_to_misdn_ton_public(unsigned party_ton) +{ + enum mISDN_NUMBER_TYPE ton; + + switch (party_ton) { + default: + case 0:/* unknown */ + ton = NUMTYPE_UNKNOWN; + break; + + case 1:/* internationalNumber */ + ton = NUMTYPE_INTERNATIONAL; + break; + + case 2:/* nationalNumber */ + ton = NUMTYPE_NATIONAL; + break; + + case 3:/* networkSpecificNumber */ + ton = NUMTYPE_NETWORK_SPECIFIC; + break; + + case 4:/* subscriberNumber */ + ton = NUMTYPE_SUBSCRIBER; + break; + + case 6:/* abbreviatedNumber */ + ton = NUMTYPE_ABBREVIATED; + break; + } + + return ton; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Convert mISDN type-of-number to PartyNumber private type-of-number + * + * \param ton mISDN type-of-number + * + * \return PartyNumber private type-of-number + */ +static unsigned misdn_to_PartyNumber_ton_private(enum mISDN_NUMBER_TYPE ton) +{ + unsigned party_ton; + + switch (ton) { + default: + case NUMTYPE_UNKNOWN: + party_ton = 0;/* unknown */ + break; + + case NUMTYPE_INTERNATIONAL: + party_ton = 1;/* level2RegionalNumber */ + break; + + case NUMTYPE_NATIONAL: + party_ton = 2;/* level1RegionalNumber */ + break; + + case NUMTYPE_NETWORK_SPECIFIC: + party_ton = 3;/* pTNSpecificNumber */ + break; + + case NUMTYPE_SUBSCRIBER: + party_ton = 4;/* localNumber */ + break; + + case NUMTYPE_ABBREVIATED: + party_ton = 6;/* abbreviatedNumber */ + break; + } + + return party_ton; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Convert the PartyNumber private type-of-number to mISDN type-of-number + * + * \param party_ton PartyNumber private type-of-number + * + * \return mISDN type-of-number + */ +static enum mISDN_NUMBER_TYPE PartyNumber_to_misdn_ton_private(unsigned party_ton) +{ + enum mISDN_NUMBER_TYPE ton; + + switch (party_ton) { + default: + case 0:/* unknown */ + ton = NUMTYPE_UNKNOWN; + break; + + case 1:/* level2RegionalNumber */ + ton = NUMTYPE_INTERNATIONAL; + break; + + case 2:/* level1RegionalNumber */ + ton = NUMTYPE_NATIONAL; + break; + + case 3:/* pTNSpecificNumber */ + ton = NUMTYPE_NETWORK_SPECIFIC; + break; + + case 4:/* localNumber */ + ton = NUMTYPE_SUBSCRIBER; + break; + + case 6:/* abbreviatedNumber */ + ton = NUMTYPE_ABBREVIATED; + break; + } + + return ton; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +/*! + * \internal + * \brief Convert the mISDN type of number code to a string + * + * \param number_type mISDN type of number code. + * + * \return The mISDN type of number code as a string + */ +static const char *misdn_to_str_ton(enum mISDN_NUMBER_TYPE number_type) +{ + const char *str; + + switch (number_type) { + default: + case NUMTYPE_UNKNOWN: + str = "Unknown"; + break; + + case NUMTYPE_INTERNATIONAL: + str = "International"; + break; + + case NUMTYPE_NATIONAL: + str = "National"; + break; + + case NUMTYPE_NETWORK_SPECIFIC: + str = "Network Specific"; + break; + + case NUMTYPE_SUBSCRIBER: + str = "Subscriber"; + break; + + case NUMTYPE_ABBREVIATED: + str = "Abbreviated"; + break; + } + + return str; +} + +/*! + * \internal + * \brief Convert the mISDN type of number code to Asterisk type of number code + * + * \param number_type mISDN type of number code. + * + * \return Asterisk type of number code + */ +static int misdn_to_ast_ton(enum mISDN_NUMBER_TYPE number_type) +{ + int ast_number_type; + + switch (number_type) { + default: + case NUMTYPE_UNKNOWN: + ast_number_type = NUMTYPE_UNKNOWN << 4; + break; + + case NUMTYPE_INTERNATIONAL: + ast_number_type = NUMTYPE_INTERNATIONAL << 4; + break; + + case NUMTYPE_NATIONAL: + ast_number_type = NUMTYPE_NATIONAL << 4; + break; + + case NUMTYPE_NETWORK_SPECIFIC: + ast_number_type = NUMTYPE_NETWORK_SPECIFIC << 4; + break; + + case NUMTYPE_SUBSCRIBER: + ast_number_type = NUMTYPE_SUBSCRIBER << 4; + break; + + case NUMTYPE_ABBREVIATED: + ast_number_type = NUMTYPE_ABBREVIATED << 4; + break; + } + + return ast_number_type; +} + +/*! + * \internal + * \brief Convert the Asterisk type of number code to mISDN type of number code + * + * \param ast_number_type Asterisk type of number code. + * + * \return mISDN type of number code + */ +static enum mISDN_NUMBER_TYPE ast_to_misdn_ton(unsigned ast_number_type) +{ + enum mISDN_NUMBER_TYPE number_type; + + switch ((ast_number_type >> 4) & 0x07) { + default: + case NUMTYPE_UNKNOWN: + number_type = NUMTYPE_UNKNOWN; + break; + + case NUMTYPE_INTERNATIONAL: + number_type = NUMTYPE_INTERNATIONAL; + break; + + case NUMTYPE_NATIONAL: + number_type = NUMTYPE_NATIONAL; + break; + + case NUMTYPE_NETWORK_SPECIFIC: + number_type = NUMTYPE_NETWORK_SPECIFIC; + break; + + case NUMTYPE_SUBSCRIBER: + number_type = NUMTYPE_SUBSCRIBER; + break; + + case NUMTYPE_ABBREVIATED: + number_type = NUMTYPE_ABBREVIATED; + break; + } + + return number_type; +} + +/*! + * \internal + * \brief Convert the mISDN numbering plan code to a string + * + * \param number_plan mISDN numbering plan code. + * + * \return The mISDN numbering plan code as a string + */ +static const char *misdn_to_str_plan(enum mISDN_NUMBER_PLAN number_plan) +{ + const char *str; + + switch (number_plan) { + default: + case NUMPLAN_UNKNOWN: + str = "Unknown"; + break; + + case NUMPLAN_ISDN: + str = "ISDN"; + break; + + case NUMPLAN_DATA: + str = "Data"; + break; + + case NUMPLAN_TELEX: + str = "Telex"; + break; + + case NUMPLAN_NATIONAL: + str = "National"; + break; + + case NUMPLAN_PRIVATE: + str = "Private"; + break; + } + + return str; +} + +/*! + * \internal + * \brief Convert the mISDN numbering plan code to Asterisk numbering plan code + * + * \param number_plan mISDN numbering plan code. + * + * \return Asterisk numbering plan code + */ +static int misdn_to_ast_plan(enum mISDN_NUMBER_PLAN number_plan) +{ + int ast_number_plan; + + switch (number_plan) { + default: + case NUMPLAN_UNKNOWN: + ast_number_plan = NUMPLAN_UNKNOWN; + break; + + case NUMPLAN_ISDN: + ast_number_plan = NUMPLAN_ISDN; + break; + + case NUMPLAN_DATA: + ast_number_plan = NUMPLAN_DATA; + break; + + case NUMPLAN_TELEX: + ast_number_plan = NUMPLAN_TELEX; + break; + + case NUMPLAN_NATIONAL: + ast_number_plan = NUMPLAN_NATIONAL; + break; + + case NUMPLAN_PRIVATE: + ast_number_plan = NUMPLAN_PRIVATE; + break; + } + + return ast_number_plan; +} + +/*! + * \internal + * \brief Convert the Asterisk numbering plan code to mISDN numbering plan code + * + * \param ast_number_plan Asterisk numbering plan code. + * + * \return mISDN numbering plan code + */ +static enum mISDN_NUMBER_PLAN ast_to_misdn_plan(unsigned ast_number_plan) +{ + enum mISDN_NUMBER_PLAN number_plan; + + switch (ast_number_plan & 0x0F) { + default: + case NUMPLAN_UNKNOWN: + number_plan = NUMPLAN_UNKNOWN; + break; + + case NUMPLAN_ISDN: + number_plan = NUMPLAN_ISDN; + break; + + case NUMPLAN_DATA: + number_plan = NUMPLAN_DATA; + break; + + case NUMPLAN_TELEX: + number_plan = NUMPLAN_TELEX; + break; + + case NUMPLAN_NATIONAL: + number_plan = NUMPLAN_NATIONAL; + break; + + case NUMPLAN_PRIVATE: + number_plan = NUMPLAN_PRIVATE; + break; + } + + return number_plan; +} + +/*! + * \internal + * \brief Convert the mISDN presentation code to a string + * + * \param presentation mISDN number presentation restriction code. + * + * \return The mISDN presentation code as a string + */ +static const char *misdn_to_str_pres(int presentation) +{ + const char *str; + + switch (presentation) { + case 0: + str = "Allowed"; + break; + + case 1: + str = "Restricted"; + break; + + case 2: + str = "Unavailable"; + break; + + default: + str = "Unknown"; + break; + } + + return str; +} + +/*! + * \internal + * \brief Convert the mISDN presentation code to Asterisk presentation code + * + * \param presentation mISDN number presentation restriction code. + * + * \return Asterisk presentation code + */ +static int misdn_to_ast_pres(int presentation) +{ + switch (presentation) { + default: + case 0: + presentation = AST_PRES_ALLOWED; + break; + + case 1: + presentation = AST_PRES_RESTRICTED; + break; + + case 2: + presentation = AST_PRES_UNAVAILABLE; + break; + } + + return presentation; +} + +/*! + * \internal + * \brief Convert the Asterisk presentation code to mISDN presentation code + * + * \param presentation Asterisk number presentation restriction code. + * + * \return mISDN presentation code + */ +static int ast_to_misdn_pres(int presentation) +{ + switch (presentation & AST_PRES_RESTRICTION) { + default: + case AST_PRES_ALLOWED: + presentation = 0; + break; + + case AST_PRES_RESTRICTED: + presentation = 1; + break; + + case AST_PRES_UNAVAILABLE: + presentation = 2; + break; + } + + return presentation; +} + +/*! + * \internal + * \brief Convert the mISDN screening code to a string + * + * \param screening mISDN number screening code. + * + * \return The mISDN screening code as a string + */ +static const char *misdn_to_str_screen(int screening) +{ + const char *str; + + switch (screening) { + case 0: + str = "Unscreened"; + break; + + case 1: + str = "Passed Screen"; + break; + + case 2: + str = "Failed Screen"; + break; + + case 3: + str = "Network Number"; + break; + + default: + str = "Unknown"; + break; + } + + return str; +} + +/*! + * \internal + * \brief Convert the mISDN screening code to Asterisk screening code + * + * \param screening mISDN number screening code. + * + * \return Asterisk screening code + */ +static int misdn_to_ast_screen(int screening) +{ + switch (screening) { + default: + case 0: + screening = AST_PRES_USER_NUMBER_UNSCREENED; + break; + + case 1: + screening = AST_PRES_USER_NUMBER_PASSED_SCREEN; + break; + + case 2: + screening = AST_PRES_USER_NUMBER_FAILED_SCREEN; + break; + + case 3: + screening = AST_PRES_NETWORK_NUMBER; + break; + } + + return screening; +} + +/*! + * \internal + * \brief Convert the Asterisk screening code to mISDN screening code + * + * \param screening Asterisk number screening code. + * + * \return mISDN screening code + */ +static int ast_to_misdn_screen(int screening) +{ + switch (screening & AST_PRES_NUMBER_TYPE) { + default: + case AST_PRES_USER_NUMBER_UNSCREENED: + screening = 0; + break; + + case AST_PRES_USER_NUMBER_PASSED_SCREEN: + screening = 1; + break; + + case AST_PRES_USER_NUMBER_FAILED_SCREEN: + screening = 2; + break; + + case AST_PRES_NETWORK_NUMBER: + screening = 3; + break; + } + + return screening; +} + +/*! + * \internal + * \brief Convert Asterisk redirecting reason to mISDN redirecting reason code. + * + * \param ast Asterisk redirecting reason code. + * + * \return mISDN reason code + */ +static enum mISDN_REDIRECTING_REASON ast_to_misdn_reason(const enum AST_REDIRECTING_REASON ast) +{ + unsigned index; + + static const struct misdn_reasons { + enum AST_REDIRECTING_REASON ast; + enum mISDN_REDIRECTING_REASON q931; + } misdn_reason_table[] = { + /* *INDENT-OFF* */ + { AST_REDIRECTING_REASON_UNKNOWN, mISDN_REDIRECTING_REASON_UNKNOWN }, + { AST_REDIRECTING_REASON_USER_BUSY, mISDN_REDIRECTING_REASON_CALL_FWD_BUSY }, + { AST_REDIRECTING_REASON_NO_ANSWER, mISDN_REDIRECTING_REASON_NO_REPLY }, + { AST_REDIRECTING_REASON_UNAVAILABLE, mISDN_REDIRECTING_REASON_NO_REPLY }, + { AST_REDIRECTING_REASON_UNCONDITIONAL, mISDN_REDIRECTING_REASON_CALL_FWD }, + { AST_REDIRECTING_REASON_TIME_OF_DAY, mISDN_REDIRECTING_REASON_UNKNOWN }, + { AST_REDIRECTING_REASON_DO_NOT_DISTURB, mISDN_REDIRECTING_REASON_UNKNOWN }, + { AST_REDIRECTING_REASON_DEFLECTION, mISDN_REDIRECTING_REASON_DEFLECTION }, + { AST_REDIRECTING_REASON_FOLLOW_ME, mISDN_REDIRECTING_REASON_UNKNOWN }, + { AST_REDIRECTING_REASON_OUT_OF_ORDER, mISDN_REDIRECTING_REASON_OUT_OF_ORDER }, + { AST_REDIRECTING_REASON_AWAY, mISDN_REDIRECTING_REASON_UNKNOWN }, + { AST_REDIRECTING_REASON_CALL_FWD_DTE, mISDN_REDIRECTING_REASON_CALL_FWD_DTE } + /* *INDENT-ON* */ + }; + + for (index = 0; index < ARRAY_LEN(misdn_reason_table); ++index) { + if (misdn_reason_table[index].ast == ast) { + return misdn_reason_table[index].q931; + } + } + return mISDN_REDIRECTING_REASON_UNKNOWN; +} + +/*! + * \internal + * \brief Convert the mISDN redirecting reason to Asterisk redirecting reason code + * + * \param q931 mISDN redirecting reason code. + * + * \return Asterisk redirecting reason code + */ +static enum AST_REDIRECTING_REASON misdn_to_ast_reason(const enum mISDN_REDIRECTING_REASON q931) +{ + enum AST_REDIRECTING_REASON ast; + + switch (q931) { + default: + case mISDN_REDIRECTING_REASON_UNKNOWN: + ast = AST_REDIRECTING_REASON_UNKNOWN; + break; + + case mISDN_REDIRECTING_REASON_CALL_FWD_BUSY: + ast = AST_REDIRECTING_REASON_USER_BUSY; + break; + + case mISDN_REDIRECTING_REASON_NO_REPLY: + ast = AST_REDIRECTING_REASON_NO_ANSWER; + break; + + case mISDN_REDIRECTING_REASON_DEFLECTION: + ast = AST_REDIRECTING_REASON_DEFLECTION; + break; + + case mISDN_REDIRECTING_REASON_OUT_OF_ORDER: + ast = AST_REDIRECTING_REASON_OUT_OF_ORDER; + break; + + case mISDN_REDIRECTING_REASON_CALL_FWD_DTE: + ast = AST_REDIRECTING_REASON_CALL_FWD_DTE; + break; + + case mISDN_REDIRECTING_REASON_CALL_FWD: + ast = AST_REDIRECTING_REASON_UNCONDITIONAL; + break; + } + + return ast; +} + + + struct allowed_bearers { char *name; /*!< Bearer capability name string used in /etc/misdn.conf allowed_bearers */ char *display; /*!< Bearer capability displayable name */ @@ -591,7 +2318,7 @@ }; /* *INDENT-OFF* */ -static const struct allowed_bearers allowed_bearers_array[]= { +static const struct allowed_bearers allowed_bearers_array[] = { /* Name, Displayable Name Bearer Capability, Deprecated */ { "speech", "Speech", INFO_CAPABILITY_SPEECH, 0 }, { "3_1khz", "3.1KHz Audio", INFO_CAPABILITY_AUDIO_3_1K, 0 }, @@ -610,30 +2337,594 @@ if (allowed_bearers_array[index].cap == cap) { return allowed_bearers_array[index].display; } - } /* end for */ + } return "Unknown Bearer"; } +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Fill in facility PartyNumber information + * + * \param party PartyNumber structure to fill in. + * \param id Information to put in PartyNumber structure. + * + * \return Nothing + */ +static void misdn_PartyNumber_fill(struct FacPartyNumber *party, const struct misdn_party_id *id) +{ + ast_copy_string((char *) party->Number, id->number, sizeof(party->Number)); + party->LengthOfNumber = strlen((char *) party->Number); + party->Type = misdn_to_PartyNumber_plan(id->number_plan); + switch (party->Type) { + case 1:/* public */ + party->TypeOfNumber = misdn_to_PartyNumber_ton_public(id->number_type); + break; + case 5:/* private */ + party->TypeOfNumber = misdn_to_PartyNumber_ton_private(id->number_type); + break; + default: + party->TypeOfNumber = 0;/* Dont't care */ + break; + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ -static void print_facility(struct FacParm *fac, struct misdn_bchannel *bc) +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Extract the information from PartyNumber + * + * \param id Where to put extracted PartyNumber information + * \param party PartyNumber information to extract + * + * \return Nothing + */ +static void misdn_PartyNumber_extract(struct misdn_party_id *id, const struct FacPartyNumber *party) { + if (party->LengthOfNumber) { + ast_copy_string(id->number, (char *) party->Number, sizeof(id->number)); + id->number_plan = PartyNumber_to_misdn_plan(party->Type); + switch (party->Type) { + case 1:/* public */ + id->number_type = PartyNumber_to_misdn_ton_public(party->TypeOfNumber); + break; + case 5:/* private */ + id->number_type = PartyNumber_to_misdn_ton_private(party->TypeOfNumber); + break; + default: + id->number_type = NUMTYPE_UNKNOWN; + break; + } + } else { + /* Number not present */ + id->number_type = NUMTYPE_UNKNOWN; + id->number_plan = NUMPLAN_ISDN; + id->number[0] = 0; + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Fill in facility Address information + * + * \param Address Address structure to fill in. + * \param id Information to put in Address structure. + * + * \return Nothing + */ +static void misdn_Address_fill(struct FacAddress *Address, const struct misdn_party_id *id) +{ + misdn_PartyNumber_fill(&Address->Party, id); + + /* Subaddresses are not supported yet */ + Address->Subaddress.Length = 0; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Fill in facility PresentedNumberUnscreened information + * + * \param presented PresentedNumberUnscreened structure to fill in. + * \param id Information to put in PresentedNumberUnscreened structure. + * + * \return Nothing + */ +static void misdn_PresentedNumberUnscreened_fill(struct FacPresentedNumberUnscreened *presented, const struct misdn_party_id *id) +{ + presented->Type = misdn_to_PresentedNumberUnscreened_type(id->presentation, id->number[0] ? 1 : 0); + misdn_PartyNumber_fill(&presented->Unscreened, id); +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Extract the information from PartyNumber + * + * \param id Where to put extracted PresentedNumberUnscreened information + * \param presented PresentedNumberUnscreened information to extract + * + * \return Nothing + */ +static void misdn_PresentedNumberUnscreened_extract(struct misdn_party_id *id, const struct FacPresentedNumberUnscreened *presented) +{ + id->presentation = PresentedNumberUnscreened_to_misdn_pres(presented->Type); + id->screening = 0;/* unscreened */ + misdn_PartyNumber_extract(id, &presented->Unscreened); +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static const char Level_Spacing[] = " ";/* Work for up to 10 levels */ +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static void print_facility_PartyNumber(unsigned Level, const struct FacPartyNumber *Party, const struct misdn_bchannel *bc) +{ + if (Party->LengthOfNumber) { + const char *Spacing; + + Spacing = &Level_Spacing[sizeof(Level_Spacing) - 1 - Level]; + chan_misdn_log(1, bc->port, " -->%s PartyNumber: Type:%d\n", + Spacing, Party->Type); + switch (Party->Type) { + case 0: /* Unknown PartyNumber */ + chan_misdn_log(1, bc->port, " -->%s Unknown: %s\n", + Spacing, Party->Number); + break; + case 1: /* Public PartyNumber */ + chan_misdn_log(1, bc->port, " -->%s Public TON:%d %s\n", + Spacing, Party->TypeOfNumber, Party->Number); + break; + case 2: /* NSAP encoded PartyNumber */ + chan_misdn_log(1, bc->port, " -->%s NSAP: %s\n", + Spacing, Party->Number); + break; + case 3: /* Data PartyNumber (Not used) */ + chan_misdn_log(1, bc->port, " -->%s Data: %s\n", + Spacing, Party->Number); + break; + case 4: /* Telex PartyNumber (Not used) */ + chan_misdn_log(1, bc->port, " -->%s Telex: %s\n", + Spacing, Party->Number); + break; + case 5: /* Private PartyNumber */ + chan_misdn_log(1, bc->port, " -->%s Private TON:%d %s\n", + Spacing, Party->TypeOfNumber, Party->Number); + break; + case 8: /* National Standard PartyNumber (Not used) */ + chan_misdn_log(1, bc->port, " -->%s National: %s\n", + Spacing, Party->Number); + break; + default: + break; + } + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static void print_facility_Subaddress(unsigned Level, const struct FacPartySubaddress *Subaddress, const struct misdn_bchannel *bc) +{ + if (Subaddress->Length) { + const char *Spacing; + + Spacing = &Level_Spacing[sizeof(Level_Spacing) - 1 - Level]; + chan_misdn_log(1, bc->port, " -->%s Subaddress: Type:%d\n", + Spacing, Subaddress->Type); + switch (Subaddress->Type) { + case 0: /* UserSpecified */ + if (Subaddress->u.UserSpecified.OddCountPresent) { + chan_misdn_log(1, bc->port, " -->%s User BCD OddCount:%d NumOctets:%d\n", + Spacing, Subaddress->u.UserSpecified.OddCount, Subaddress->Length); + } else { + chan_misdn_log(1, bc->port, " -->%s User: %s\n", + Spacing, Subaddress->u.UserSpecified.Information); + } + break; + case 1: /* NSAP */ + chan_misdn_log(1, bc->port, " -->%s NSAP: %s\n", + Spacing, Subaddress->u.Nsap); + break; + default: + break; + } + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static void print_facility_Address(unsigned Level, const struct FacAddress *Address, const struct misdn_bchannel *bc) +{ + print_facility_PartyNumber(Level, &Address->Party, bc); + print_facility_Subaddress(Level, &Address->Subaddress, bc); +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static void print_facility_PresentedNumberUnscreened(unsigned Level, const struct FacPresentedNumberUnscreened *Presented, const struct misdn_bchannel *bc) +{ + const char *Spacing; + + Spacing = &Level_Spacing[sizeof(Level_Spacing) - 1 - Level]; + chan_misdn_log(1, bc->port, " -->%s Unscreened Type:%d\n", Spacing, Presented->Type); + switch (Presented->Type) { + case 0: /* presentationAllowedNumber */ + chan_misdn_log(1, bc->port, " -->%s Allowed:\n", Spacing); + print_facility_PartyNumber(Level + 2, &Presented->Unscreened, bc); + break; + case 1: /* presentationRestricted */ + chan_misdn_log(1, bc->port, " -->%s Restricted\n", Spacing); + break; + case 2: /* numberNotAvailableDueToInterworking */ + chan_misdn_log(1, bc->port, " -->%s Not Available\n", Spacing); + break; + case 3: /* presentationRestrictedNumber */ + chan_misdn_log(1, bc->port, " -->%s Restricted:\n", Spacing); + print_facility_PartyNumber(Level + 2, &Presented->Unscreened, bc); + break; + default: + break; + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static void print_facility_AddressScreened(unsigned Level, const struct FacAddressScreened *Address, const struct misdn_bchannel *bc) +{ + const char *Spacing; + + Spacing = &Level_Spacing[sizeof(Level_Spacing) - 1 - Level]; + chan_misdn_log(1, bc->port, " -->%s ScreeningIndicator:%d\n", Spacing, Address->ScreeningIndicator); + print_facility_PartyNumber(Level, &Address->Party, bc); + print_facility_Subaddress(Level, &Address->Subaddress, bc); +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static void print_facility_PresentedAddressScreened(unsigned Level, const struct FacPresentedAddressScreened *Presented, const struct misdn_bchannel *bc) +{ + const char *Spacing; + + Spacing = &Level_Spacing[sizeof(Level_Spacing) - 1 - Level]; + chan_misdn_log(1, bc->port, " -->%s Screened Type:%d\n", Spacing, Presented->Type); + switch (Presented->Type) { + case 0: /* presentationAllowedAddress */ + chan_misdn_log(1, bc->port, " -->%s Allowed:\n", Spacing); + print_facility_AddressScreened(Level + 2, &Presented->Address, bc); + break; + case 1: /* presentationRestricted */ + chan_misdn_log(1, bc->port, " -->%s Restricted\n", Spacing); + break; + case 2: /* numberNotAvailableDueToInterworking */ + chan_misdn_log(1, bc->port, " -->%s Not Available\n", Spacing); + break; + case 3: /* presentationRestrictedAddress */ + chan_misdn_log(1, bc->port, " -->%s Restricted:\n", Spacing); + print_facility_AddressScreened(Level + 2, &Presented->Address, bc); + break; + default: + break; + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static void print_facility_Q931_Bc_Hlc_Llc(unsigned Level, const struct Q931_Bc_Hlc_Llc *Q931ie, const struct misdn_bchannel *bc) +{ + const char *Spacing; + + Spacing = &Level_Spacing[sizeof(Level_Spacing) - 1 - Level]; + chan_misdn_log(1, bc->port, " -->%s Q931ie:\n", Spacing); + if (Q931ie->Bc.Length) { + chan_misdn_log(1, bc->port, " -->%s Bc Len:%d\n", Spacing, Q931ie->Bc.Length); + } + if (Q931ie->Hlc.Length) { + chan_misdn_log(1, bc->port, " -->%s Hlc Len:%d\n", Spacing, Q931ie->Hlc.Length); + } + if (Q931ie->Llc.Length) { + chan_misdn_log(1, bc->port, " -->%s Llc Len:%d\n", Spacing, Q931ie->Llc.Length); + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static void print_facility_Q931_Bc_Hlc_Llc_Uu(unsigned Level, const struct Q931_Bc_Hlc_Llc_Uu *Q931ie, const struct misdn_bchannel *bc) +{ + const char *Spacing; + + Spacing = &Level_Spacing[sizeof(Level_Spacing) - 1 - Level]; + chan_misdn_log(1, bc->port, " -->%s Q931ie:\n", Spacing); + if (Q931ie->Bc.Length) { + chan_misdn_log(1, bc->port, " -->%s Bc Len:%d\n", Spacing, Q931ie->Bc.Length); + } + if (Q931ie->Hlc.Length) { + chan_misdn_log(1, bc->port, " -->%s Hlc Len:%d\n", Spacing, Q931ie->Hlc.Length); + } + if (Q931ie->Llc.Length) { + chan_misdn_log(1, bc->port, " -->%s Llc Len:%d\n", Spacing, Q931ie->Llc.Length); + } + if (Q931ie->UserInfo.Length) { + chan_misdn_log(1, bc->port, " -->%s UserInfo Len:%d\n", Spacing, Q931ie->UserInfo.Length); + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static void print_facility_CallInformation(unsigned Level, const struct FacCallInformation *CallInfo, const struct misdn_bchannel *bc) +{ + const char *Spacing; + + Spacing = &Level_Spacing[sizeof(Level_Spacing) - 1 - Level]; + chan_misdn_log(1, bc->port, " -->%s CCBSReference:%d\n", + Spacing, CallInfo->CCBSReference); + chan_misdn_log(1, bc->port, " -->%s AddressOfB:\n", Spacing); + print_facility_Address(Level + 1, &CallInfo->AddressOfB, bc); + print_facility_Q931_Bc_Hlc_Llc(Level, &CallInfo->Q931ie, bc); + if (CallInfo->SubaddressOfA.Length) { + chan_misdn_log(1, bc->port, " -->%s SubaddressOfA:\n", Spacing); + print_facility_Subaddress(Level + 1, &CallInfo->SubaddressOfA, bc); + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static void print_facility_ServedUserNr(unsigned Level, const struct FacPartyNumber *Party, const struct misdn_bchannel *bc) +{ + const char *Spacing; + + Spacing = &Level_Spacing[sizeof(Level_Spacing) - 1 - Level]; + if (Party->LengthOfNumber) { + print_facility_PartyNumber(Level, Party, bc); + } else { + chan_misdn_log(1, bc->port, " -->%s All Numbers\n", Spacing); + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static void print_facility_IntResult(unsigned Level, const struct FacForwardingRecord *ForwardingRecord, const struct misdn_bchannel *bc) +{ + const char *Spacing; + + Spacing = &Level_Spacing[sizeof(Level_Spacing) - 1 - Level]; + chan_misdn_log(1, bc->port, " -->%s Procedure:%d BasicService:%d\n", + Spacing, + ForwardingRecord->Procedure, + ForwardingRecord->BasicService); + chan_misdn_log(1, bc->port, " -->%s ForwardedTo:\n", Spacing); + print_facility_Address(Level + 1, &ForwardingRecord->ForwardedTo, bc); + chan_misdn_log(1, bc->port, " -->%s ServedUserNr:\n", Spacing); + print_facility_ServedUserNr(Level + 1, &ForwardingRecord->ServedUser, bc); +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +static void print_facility(const struct FacParm *fac, const const struct misdn_bchannel *bc) +{ +#if defined(AST_MISDN_ENHANCEMENTS) + unsigned Index; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + switch (fac->Function) { -#ifdef HAVE_MISDN_FAC_RESULT - case Fac_RESULT: - chan_misdn_log(0, bc->port, " --> Received RESULT Operation\n"); +#if defined(AST_MISDN_ENHANCEMENTS) + case Fac_ActivationDiversion: + chan_misdn_log(1, bc->port, " --> ActivationDiversion: InvokeID:%d\n", + fac->u.ActivationDiversion.InvokeID); + switch (fac->u.ActivationDiversion.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke: Procedure:%d BasicService:%d\n", + fac->u.ActivationDiversion.Component.Invoke.Procedure, + fac->u.ActivationDiversion.Component.Invoke.BasicService); + chan_misdn_log(1, bc->port, " --> ForwardedTo:\n"); + print_facility_Address(3, &fac->u.ActivationDiversion.Component.Invoke.ForwardedTo, bc); + chan_misdn_log(1, bc->port, " --> ServedUserNr:\n"); + print_facility_ServedUserNr(3, &fac->u.ActivationDiversion.Component.Invoke.ServedUser, bc); + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result\n"); + break; + default: + break; + } break; -#endif -#ifdef HAVE_MISDN_FAC_ERROR - case Fac_ERROR: - chan_misdn_log(0, bc->port, " --> Received Error Operation\n"); - chan_misdn_log(0, bc->port, " --> Value:%d Error:%s\n", fac->u.ERROR.errorValue, fac->u.ERROR.error); + case Fac_DeactivationDiversion: + chan_misdn_log(1, bc->port, " --> DeactivationDiversion: InvokeID:%d\n", + fac->u.DeactivationDiversion.InvokeID); + switch (fac->u.DeactivationDiversion.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke: Procedure:%d BasicService:%d\n", + fac->u.DeactivationDiversion.Component.Invoke.Procedure, + fac->u.DeactivationDiversion.Component.Invoke.BasicService); + chan_misdn_log(1, bc->port, " --> ServedUserNr:\n"); + print_facility_ServedUserNr(3, &fac->u.DeactivationDiversion.Component.Invoke.ServedUser, bc); + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result\n"); + break; + default: + break; + } break; -#endif + case Fac_ActivationStatusNotificationDiv: + chan_misdn_log(1, bc->port, " --> ActivationStatusNotificationDiv: InvokeID:%d Procedure:%d BasicService:%d\n", + fac->u.ActivationStatusNotificationDiv.InvokeID, + fac->u.ActivationStatusNotificationDiv.Procedure, + fac->u.ActivationStatusNotificationDiv.BasicService); + chan_misdn_log(1, bc->port, " --> ForwardedTo:\n"); + print_facility_Address(2, &fac->u.ActivationStatusNotificationDiv.ForwardedTo, bc); + chan_misdn_log(1, bc->port, " --> ServedUserNr:\n"); + print_facility_ServedUserNr(2, &fac->u.ActivationStatusNotificationDiv.ServedUser, bc); + break; + case Fac_DeactivationStatusNotificationDiv: + chan_misdn_log(1, bc->port, " --> DeactivationStatusNotificationDiv: InvokeID:%d Procedure:%d BasicService:%d\n", + fac->u.DeactivationStatusNotificationDiv.InvokeID, + fac->u.DeactivationStatusNotificationDiv.Procedure, + fac->u.DeactivationStatusNotificationDiv.BasicService); + chan_misdn_log(1, bc->port, " --> ServedUserNr:\n"); + print_facility_ServedUserNr(2, &fac->u.DeactivationStatusNotificationDiv.ServedUser, bc); + break; + case Fac_InterrogationDiversion: + chan_misdn_log(1, bc->port, " --> InterrogationDiversion: InvokeID:%d\n", + fac->u.InterrogationDiversion.InvokeID); + switch (fac->u.InterrogationDiversion.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke: Procedure:%d BasicService:%d\n", + fac->u.InterrogationDiversion.Component.Invoke.Procedure, + fac->u.InterrogationDiversion.Component.Invoke.BasicService); + chan_misdn_log(1, bc->port, " --> ServedUserNr:\n"); + print_facility_ServedUserNr(3, &fac->u.InterrogationDiversion.Component.Invoke.ServedUser, bc); + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result:\n"); + if (fac->u.InterrogationDiversion.Component.Result.NumRecords) { + for (Index = 0; Index < fac->u.InterrogationDiversion.Component.Result.NumRecords; ++Index) { + chan_misdn_log(1, bc->port, " --> IntResult[%d]:\n", Index); + print_facility_IntResult(3, &fac->u.InterrogationDiversion.Component.Result.List[Index], bc); + } + } + break; + default: + break; + } + break; + case Fac_DiversionInformation: + chan_misdn_log(1, bc->port, " --> DiversionInformation: InvokeID:%d Reason:%d BasicService:%d\n", + fac->u.DiversionInformation.InvokeID, + fac->u.DiversionInformation.DiversionReason, + fac->u.DiversionInformation.BasicService); + if (fac->u.DiversionInformation.ServedUserSubaddress.Length) { + chan_misdn_log(1, bc->port, " --> ServedUserSubaddress:\n"); + print_facility_Subaddress(2, &fac->u.DiversionInformation.ServedUserSubaddress, bc); + } + if (fac->u.DiversionInformation.CallingAddressPresent) { + chan_misdn_log(1, bc->port, " --> CallingAddress:\n"); + print_facility_PresentedAddressScreened(2, &fac->u.DiversionInformation.CallingAddress, bc); + } + if (fac->u.DiversionInformation.OriginalCalledPresent) { + chan_misdn_log(1, bc->port, " --> OriginalCalledNr:\n"); + print_facility_PresentedNumberUnscreened(2, &fac->u.DiversionInformation.OriginalCalled, bc); + } + if (fac->u.DiversionInformation.LastDivertingPresent) { + chan_misdn_log(1, bc->port, " --> LastDivertingNr:\n"); + print_facility_PresentedNumberUnscreened(2, &fac->u.DiversionInformation.LastDiverting, bc); + } + if (fac->u.DiversionInformation.LastDivertingReasonPresent) { + chan_misdn_log(1, bc->port, " --> LastDivertingReason:%d\n", fac->u.DiversionInformation.LastDivertingReason); + } + if (fac->u.DiversionInformation.UserInfo.Length) { + chan_misdn_log(1, bc->port, " --> UserInfo Length:%d\n", fac->u.DiversionInformation.UserInfo.Length); + } + break; + case Fac_CallDeflection: + chan_misdn_log(1, bc->port, " --> CallDeflection: InvokeID:%d\n", + fac->u.CallDeflection.InvokeID); + switch (fac->u.CallDeflection.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke:\n"); + if (fac->u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUserPresent) { + chan_misdn_log(1, bc->port, " --> PresentationAllowed:%d\n", + fac->u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUser); + } + chan_misdn_log(1, bc->port, " --> DeflectionAddress:\n"); + print_facility_Address(3, &fac->u.CallDeflection.Component.Invoke.Deflection, bc); + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result\n"); + break; + default: + break; + } + break; + case Fac_CallRerouteing: + chan_misdn_log(1, bc->port, " --> CallRerouteing: InvokeID:%d\n", + fac->u.CallRerouteing.InvokeID); + switch (fac->u.CallRerouteing.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke: Reason:%d Counter:%d\n", + fac->u.CallRerouteing.Component.Invoke.ReroutingReason, + fac->u.CallRerouteing.Component.Invoke.ReroutingCounter); + chan_misdn_log(1, bc->port, " --> CalledAddress:\n"); + print_facility_Address(3, &fac->u.CallRerouteing.Component.Invoke.CalledAddress, bc); + print_facility_Q931_Bc_Hlc_Llc_Uu(2, &fac->u.CallRerouteing.Component.Invoke.Q931ie, bc); + chan_misdn_log(1, bc->port, " --> LastReroutingNr:\n"); + print_facility_PresentedNumberUnscreened(3, &fac->u.CallRerouteing.Component.Invoke.LastRerouting, bc); + chan_misdn_log(1, bc->port, " --> SubscriptionOption:%d\n", + fac->u.CallRerouteing.Component.Invoke.SubscriptionOption); + if (fac->u.CallRerouteing.Component.Invoke.CallingPartySubaddress.Length) { + chan_misdn_log(1, bc->port, " --> CallingParty:\n"); + print_facility_Subaddress(3, &fac->u.CallRerouteing.Component.Invoke.CallingPartySubaddress, bc); + } + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result\n"); + break; + default: + break; + } + break; + case Fac_InterrogateServedUserNumbers: + chan_misdn_log(1, bc->port, " --> InterrogateServedUserNumbers: InvokeID:%d\n", + fac->u.InterrogateServedUserNumbers.InvokeID); + switch (fac->u.InterrogateServedUserNumbers.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke\n"); + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result:\n"); + if (fac->u.InterrogateServedUserNumbers.Component.Result.NumRecords) { + for (Index = 0; Index < fac->u.InterrogateServedUserNumbers.Component.Result.NumRecords; ++Index) { + chan_misdn_log(1, bc->port, " --> ServedUserNr[%d]:\n", Index); + print_facility_PartyNumber(3, &fac->u.InterrogateServedUserNumbers.Component.Result.List[Index], bc); + } + } + break; + default: + break; + } + break; + case Fac_DivertingLegInformation1: + chan_misdn_log(1, bc->port, " --> DivertingLegInformation1: InvokeID:%d Reason:%d SubscriptionOption:%d\n", + fac->u.DivertingLegInformation1.InvokeID, + fac->u.DivertingLegInformation1.DiversionReason, + fac->u.DivertingLegInformation1.SubscriptionOption); + if (fac->u.DivertingLegInformation1.DivertedToPresent) { + chan_misdn_log(1, bc->port, " --> DivertedToNr:\n"); + print_facility_PresentedNumberUnscreened(2, &fac->u.DivertingLegInformation1.DivertedTo, bc); + } + break; + case Fac_DivertingLegInformation2: + chan_misdn_log(1, bc->port, " --> DivertingLegInformation2: InvokeID:%d Reason:%d Count:%d\n", + fac->u.DivertingLegInformation2.InvokeID, + fac->u.DivertingLegInformation2.DiversionReason, + fac->u.DivertingLegInformation2.DiversionCounter); + if (fac->u.DivertingLegInformation2.DivertingPresent) { + chan_misdn_log(1, bc->port, " --> DivertingNr:\n"); + print_facility_PresentedNumberUnscreened(2, &fac->u.DivertingLegInformation2.Diverting, bc); + } + if (fac->u.DivertingLegInformation2.OriginalCalledPresent) { + chan_misdn_log(1, bc->port, " --> OriginalCalledNr:\n"); + print_facility_PresentedNumberUnscreened(2, &fac->u.DivertingLegInformation2.OriginalCalled, bc); + } + break; + case Fac_DivertingLegInformation3: + chan_misdn_log(1, bc->port, " --> DivertingLegInformation3: InvokeID:%d PresentationAllowed:%d\n", + fac->u.DivertingLegInformation3.InvokeID, + fac->u.DivertingLegInformation3.PresentationAllowedIndicator); + break; + +#else /* !defined(AST_MISDN_ENHANCEMENTS) */ + case Fac_CD: chan_misdn_log(1, bc->port, " --> calldeflect to: %s, presentable: %s\n", fac->u.CDeflection.DeflectedToNumber, fac->u.CDeflection.PresentationAllowed ? "yes" : "no"); break; +#endif /* !defined(AST_MISDN_ENHANCEMENTS) */ case Fac_AOCDCurrency: if (fac->u.AOCDcur.chargeNotAvailable) { chan_misdn_log(1, bc->port, " --> AOCD currency: charge not available\n"); @@ -662,17 +2953,355 @@ fac->u.AOCDchu.recordedUnits, (fac->u.AOCDchu.typeOfChargingInfo == 0) ? "subTotal" : "total"); } break; +#if defined(AST_MISDN_ENHANCEMENTS) + case Fac_ERROR: + chan_misdn_log(1, bc->port, " --> ERROR: InvokeID:%d, Code:0x%02x\n", + fac->u.ERROR.invokeId, fac->u.ERROR.errorValue); + break; + case Fac_RESULT: + chan_misdn_log(1, bc->port, " --> RESULT: InvokeID:%d\n", + fac->u.RESULT.InvokeID); + break; + case Fac_REJECT: + if (fac->u.REJECT.InvokeIDPresent) { + chan_misdn_log(1, bc->port, " --> REJECT: InvokeID:%d, Code:0x%02x\n", + fac->u.REJECT.InvokeID, fac->u.REJECT.Code); + } else { + chan_misdn_log(1, bc->port, " --> REJECT: Code:0x%02x\n", + fac->u.REJECT.Code); + } + break; + case Fac_EctExecute: + chan_misdn_log(1, bc->port, " --> EctExecute: InvokeID:%d\n", + fac->u.EctExecute.InvokeID); + break; + case Fac_ExplicitEctExecute: + chan_misdn_log(1, bc->port, " --> ExplicitEctExecute: InvokeID:%d LinkID:%d\n", + fac->u.ExplicitEctExecute.InvokeID, + fac->u.ExplicitEctExecute.LinkID); + break; + case Fac_RequestSubaddress: + chan_misdn_log(1, bc->port, " --> RequestSubaddress: InvokeID:%d\n", + fac->u.RequestSubaddress.InvokeID); + break; + case Fac_SubaddressTransfer: + chan_misdn_log(1, bc->port, " --> SubaddressTransfer: InvokeID:%d\n", + fac->u.SubaddressTransfer.InvokeID); + print_facility_Subaddress(1, &fac->u.SubaddressTransfer.Subaddress, bc); + break; + case Fac_EctLinkIdRequest: + chan_misdn_log(1, bc->port, " --> EctLinkIdRequest: InvokeID:%d\n", + fac->u.EctLinkIdRequest.InvokeID); + switch (fac->u.EctLinkIdRequest.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke\n"); + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result: LinkID:%d\n", + fac->u.EctLinkIdRequest.Component.Result.LinkID); + break; + default: + break; + } + break; + case Fac_EctInform: + chan_misdn_log(1, bc->port, " --> EctInform: InvokeID:%d Status:%d\n", + fac->u.EctInform.InvokeID, + fac->u.EctInform.Status); + if (fac->u.EctInform.RedirectionPresent) { + chan_misdn_log(1, bc->port, " --> Redirection Number\n"); + print_facility_PresentedNumberUnscreened(2, &fac->u.EctInform.Redirection, bc); + } + break; + case Fac_EctLoopTest: + chan_misdn_log(1, bc->port, " --> EctLoopTest: InvokeID:%d\n", + fac->u.EctLoopTest.InvokeID); + switch (fac->u.EctLoopTest.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke: CallTransferID:%d\n", + fac->u.EctLoopTest.Component.Invoke.CallTransferID); + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result: LoopResult:%d\n", + fac->u.EctLoopTest.Component.Result.LoopResult); + break; + default: + break; + } + break; + case Fac_StatusRequest: + chan_misdn_log(1, bc->port, " --> StatusRequest: InvokeID:%d\n", + fac->u.StatusRequest.InvokeID); + switch (fac->u.StatusRequest.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke: Compatibility:%d\n", + fac->u.StatusRequest.Component.Invoke.CompatibilityMode); + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result: Status:%d\n", + fac->u.StatusRequest.Component.Result.Status); + break; + default: + break; + } + break; + case Fac_CallInfoRetain: + chan_misdn_log(1, bc->port, " --> CallInfoRetain: InvokeID:%d, LinkageID:%d\n", + fac->u.CallInfoRetain.InvokeID, fac->u.CallInfoRetain.CallLinkageID); + break; + case Fac_CCBSDeactivate: + chan_misdn_log(1, bc->port, " --> CCBSDeactivate: InvokeID:%d\n", + fac->u.CCBSDeactivate.InvokeID); + switch (fac->u.CCBSDeactivate.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke: CCBSReference:%d\n", + fac->u.CCBSDeactivate.Component.Invoke.CCBSReference); + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result\n"); + break; + default: + break; + } + break; + case Fac_CCBSErase: + chan_misdn_log(1, bc->port, " --> CCBSErase: InvokeID:%d, CCBSReference:%d RecallMode:%d, Reason:%d\n", + fac->u.CCBSErase.InvokeID, fac->u.CCBSErase.CCBSReference, + fac->u.CCBSErase.RecallMode, fac->u.CCBSErase.Reason); + chan_misdn_log(1, bc->port, " --> AddressOfB\n"); + print_facility_Address(2, &fac->u.CCBSErase.AddressOfB, bc); + print_facility_Q931_Bc_Hlc_Llc(1, &fac->u.CCBSErase.Q931ie, bc); + break; + case Fac_CCBSRemoteUserFree: + chan_misdn_log(1, bc->port, " --> CCBSRemoteUserFree: InvokeID:%d, CCBSReference:%d RecallMode:%d\n", + fac->u.CCBSRemoteUserFree.InvokeID, fac->u.CCBSRemoteUserFree.CCBSReference, + fac->u.CCBSRemoteUserFree.RecallMode); + chan_misdn_log(1, bc->port, " --> AddressOfB\n"); + print_facility_Address(2, &fac->u.CCBSRemoteUserFree.AddressOfB, bc); + print_facility_Q931_Bc_Hlc_Llc(1, &fac->u.CCBSRemoteUserFree.Q931ie, bc); + break; + case Fac_CCBSCall: + chan_misdn_log(1, bc->port, " --> CCBSCall: InvokeID:%d, CCBSReference:%d\n", + fac->u.CCBSCall.InvokeID, fac->u.CCBSCall.CCBSReference); + break; + case Fac_CCBSStatusRequest: + chan_misdn_log(1, bc->port, " --> CCBSStatusRequest: InvokeID:%d\n", + fac->u.CCBSStatusRequest.InvokeID); + switch (fac->u.CCBSStatusRequest.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke: CCBSReference:%d RecallMode:%d\n", + fac->u.CCBSStatusRequest.Component.Invoke.CCBSReference, + fac->u.CCBSStatusRequest.Component.Invoke.RecallMode); + print_facility_Q931_Bc_Hlc_Llc(2, &fac->u.CCBSStatusRequest.Component.Invoke.Q931ie, bc); + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result: Free:%d\n", + fac->u.CCBSStatusRequest.Component.Result.Free); + break; + default: + break; + } + break; + case Fac_CCBSBFree: + chan_misdn_log(1, bc->port, " --> CCBSBFree: InvokeID:%d, CCBSReference:%d RecallMode:%d\n", + fac->u.CCBSBFree.InvokeID, fac->u.CCBSBFree.CCBSReference, + fac->u.CCBSBFree.RecallMode); + chan_misdn_log(1, bc->port, " --> AddressOfB\n"); + print_facility_Address(2, &fac->u.CCBSBFree.AddressOfB, bc); + print_facility_Q931_Bc_Hlc_Llc(1, &fac->u.CCBSBFree.Q931ie, bc); + break; + case Fac_EraseCallLinkageID: + chan_misdn_log(1, bc->port, " --> EraseCallLinkageID: InvokeID:%d, LinkageID:%d\n", + fac->u.EraseCallLinkageID.InvokeID, fac->u.EraseCallLinkageID.CallLinkageID); + break; + case Fac_CCBSStopAlerting: + chan_misdn_log(1, bc->port, " --> CCBSStopAlerting: InvokeID:%d, CCBSReference:%d\n", + fac->u.CCBSStopAlerting.InvokeID, fac->u.CCBSStopAlerting.CCBSReference); + break; + case Fac_CCBSRequest: + chan_misdn_log(1, bc->port, " --> CCBSRequest: InvokeID:%d\n", + fac->u.CCBSRequest.InvokeID); + switch (fac->u.CCBSRequest.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke: LinkageID:%d\n", + fac->u.CCBSRequest.Component.Invoke.CallLinkageID); + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result: CCBSReference:%d RecallMode:%d\n", + fac->u.CCBSRequest.Component.Result.CCBSReference, + fac->u.CCBSRequest.Component.Result.RecallMode); + break; + default: + break; + } + break; + case Fac_CCBSInterrogate: + chan_misdn_log(1, bc->port, " --> CCBSInterrogate: InvokeID:%d\n", + fac->u.CCBSInterrogate.InvokeID); + switch (fac->u.CCBSInterrogate.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke\n"); + if (fac->u.CCBSInterrogate.Component.Invoke.CCBSReferencePresent) { + chan_misdn_log(1, bc->port, " --> CCBSReference:%d\n", + fac->u.CCBSInterrogate.Component.Invoke.CCBSReference); + } + if (fac->u.CCBSInterrogate.Component.Invoke.AParty.LengthOfNumber) { + chan_misdn_log(1, bc->port, " --> AParty\n"); + print_facility_PartyNumber(3, &fac->u.CCBSInterrogate.Component.Invoke.AParty, bc); + } + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result: RecallMode:%d\n", + fac->u.CCBSInterrogate.Component.Result.RecallMode); + if (fac->u.CCBSInterrogate.Component.Result.NumRecords) { + for (Index = 0; Index < fac->u.CCBSInterrogate.Component.Result.NumRecords; ++Index) { + chan_misdn_log(1, bc->port, " --> CallDetails[%d]:\n", Index); + print_facility_CallInformation(3, &fac->u.CCBSInterrogate.Component.Result.CallDetails[Index], bc); + } + } + break; + default: + break; + } + break; + case Fac_CCNRRequest: + chan_misdn_log(1, bc->port, " --> CCNRRequest: InvokeID:%d\n", + fac->u.CCNRRequest.InvokeID); + switch (fac->u.CCNRRequest.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke: LinkageID:%d\n", + fac->u.CCNRRequest.Component.Invoke.CallLinkageID); + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result: CCBSReference:%d RecallMode:%d\n", + fac->u.CCNRRequest.Component.Result.CCBSReference, + fac->u.CCNRRequest.Component.Result.RecallMode); + break; + default: + break; + } + break; + case Fac_CCNRInterrogate: + chan_misdn_log(1, bc->port, " --> CCNRInterrogate: InvokeID:%d\n", + fac->u.CCNRInterrogate.InvokeID); + switch (fac->u.CCNRInterrogate.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke\n"); + if (fac->u.CCNRInterrogate.Component.Invoke.CCBSReferencePresent) { + chan_misdn_log(1, bc->port, " --> CCBSReference:%d\n", + fac->u.CCNRInterrogate.Component.Invoke.CCBSReference); + } + if (fac->u.CCNRInterrogate.Component.Invoke.AParty.LengthOfNumber) { + chan_misdn_log(1, bc->port, " --> AParty\n"); + print_facility_PartyNumber(3, &fac->u.CCNRInterrogate.Component.Invoke.AParty, bc); + } + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result: RecallMode:%d\n", + fac->u.CCNRInterrogate.Component.Result.RecallMode); + if (fac->u.CCNRInterrogate.Component.Result.NumRecords) { + for (Index = 0; Index < fac->u.CCNRInterrogate.Component.Result.NumRecords; ++Index) { + chan_misdn_log(1, bc->port, " --> CallDetails[%d]:\n", Index); + print_facility_CallInformation(3, &fac->u.CCNRInterrogate.Component.Result.CallDetails[Index], bc); + } + } + break; + default: + break; + } + break; + case Fac_CCBS_T_Call: + chan_misdn_log(1, bc->port, " --> CCBS_T_Call: InvokeID:%d\n", + fac->u.CCBS_T_Call.InvokeID); + break; + case Fac_CCBS_T_Suspend: + chan_misdn_log(1, bc->port, " --> CCBS_T_Suspend: InvokeID:%d\n", + fac->u.CCBS_T_Suspend.InvokeID); + break; + case Fac_CCBS_T_Resume: + chan_misdn_log(1, bc->port, " --> CCBS_T_Resume: InvokeID:%d\n", + fac->u.CCBS_T_Resume.InvokeID); + break; + case Fac_CCBS_T_RemoteUserFree: + chan_misdn_log(1, bc->port, " --> CCBS_T_RemoteUserFree: InvokeID:%d\n", + fac->u.CCBS_T_RemoteUserFree.InvokeID); + break; + case Fac_CCBS_T_Available: + chan_misdn_log(1, bc->port, " --> CCBS_T_Available: InvokeID:%d\n", + fac->u.CCBS_T_Available.InvokeID); + break; + case Fac_CCBS_T_Request: + chan_misdn_log(1, bc->port, " --> CCBS_T_Request: InvokeID:%d\n", + fac->u.CCBS_T_Request.InvokeID); + switch (fac->u.CCBS_T_Request.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke\n"); + chan_misdn_log(1, bc->port, " --> DestinationAddress:\n"); + print_facility_Address(3, &fac->u.CCBS_T_Request.Component.Invoke.Destination, bc); + print_facility_Q931_Bc_Hlc_Llc(2, &fac->u.CCBS_T_Request.Component.Invoke.Q931ie, bc); + if (fac->u.CCBS_T_Request.Component.Invoke.RetentionSupported) { + chan_misdn_log(1, bc->port, " --> RetentionSupported:1\n"); + } + if (fac->u.CCBS_T_Request.Component.Invoke.PresentationAllowedIndicatorPresent) { + chan_misdn_log(1, bc->port, " --> PresentationAllowed:%d\n", + fac->u.CCBS_T_Request.Component.Invoke.PresentationAllowedIndicator); + } + if (fac->u.CCBS_T_Request.Component.Invoke.Originating.Party.LengthOfNumber) { + chan_misdn_log(1, bc->port, " --> OriginatingAddress:\n"); + print_facility_Address(3, &fac->u.CCBS_T_Request.Component.Invoke.Originating, bc); + } + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result: RetentionSupported:%d\n", + fac->u.CCBS_T_Request.Component.Result.RetentionSupported); + break; + default: + break; + } + break; + case Fac_CCNR_T_Request: + chan_misdn_log(1, bc->port, " --> CCNR_T_Request: InvokeID:%d\n", + fac->u.CCNR_T_Request.InvokeID); + switch (fac->u.CCNR_T_Request.ComponentType) { + case FacComponent_Invoke: + chan_misdn_log(1, bc->port, " --> Invoke\n"); + chan_misdn_log(1, bc->port, " --> DestinationAddress:\n"); + print_facility_Address(3, &fac->u.CCNR_T_Request.Component.Invoke.Destination, bc); + print_facility_Q931_Bc_Hlc_Llc(2, &fac->u.CCNR_T_Request.Component.Invoke.Q931ie, bc); + if (fac->u.CCNR_T_Request.Component.Invoke.RetentionSupported) { + chan_misdn_log(1, bc->port, " --> RetentionSupported:1\n"); + } + if (fac->u.CCNR_T_Request.Component.Invoke.PresentationAllowedIndicatorPresent) { + chan_misdn_log(1, bc->port, " --> PresentationAllowed:%d\n", + fac->u.CCNR_T_Request.Component.Invoke.PresentationAllowedIndicator); + } + if (fac->u.CCNR_T_Request.Component.Invoke.Originating.Party.LengthOfNumber) { + chan_misdn_log(1, bc->port, " --> OriginatingAddress:\n"); + print_facility_Address(3, &fac->u.CCNR_T_Request.Component.Invoke.Originating, bc); + } + break; + case FacComponent_Result: + chan_misdn_log(1, bc->port, " --> Result: RetentionSupported:%d\n", + fac->u.CCNR_T_Request.Component.Result.RetentionSupported); + break; + default: + break; + } + break; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ case Fac_None: + /* No facility so print nothing */ + break; default: chan_misdn_log(1, bc->port, " --> unknown facility\n"); break; } } -static void print_bearer(struct misdn_bchannel *bc) +static void print_bearer(struct misdn_bchannel *bc) { chan_misdn_log(2, bc->port, " --> Bearer: %s\n", bearer2str(bc->capability)); - + switch(bc->law) { case INFO_CODEC_ALAW: chan_misdn_log(2, bc->port, " --> Codec: Alaw\n"); @@ -683,6 +3312,95 @@ } } +/*! + * \internal + * \brief Prefix a string to another string in place. + * + * \param str_prefix String to prefix to the main string. + * \param str_main String to get the prefix added to it. + * \param size Buffer size of the main string (Includes null terminator). + * + * \note The str_main buffer size must be greater than one. + * + * \return Nothing + */ +static void misdn_prefix_string(const char *str_prefix, char *str_main, size_t size) +{ + size_t len_over; + size_t len_total; + size_t len_main; + size_t len_prefix; + + len_prefix = strlen(str_prefix); + if (!len_prefix) { + /* There is no prefix to prepend. */ + return; + } + len_main = strlen(str_main); + len_total = len_prefix + len_main; + if (size <= len_total) { + /* We need to truncate since the buffer is too small. */ + len_over = len_total + 1 - size; + if (len_over <= len_main) { + len_main -= len_over; + } else { + len_over -= len_main; + len_main = 0; + len_prefix -= len_over; + } + } + if (len_main) { + memmove(str_main + len_prefix, str_main, len_main); + } + memcpy(str_main, str_prefix, len_prefix); + str_main[len_prefix + len_main] = '\0'; +} + +/*! + * \internal + * \brief Add a configured prefix to the given number. + * + * \param port Logical port number + * \param number_type Type-of-number passed in. + * \param number Given number string to add prefix + * \param size Buffer size number string occupies. + * + * \return Nothing + */ +static void misdn_add_number_prefix(int port, enum mISDN_NUMBER_TYPE number_type, char *number, size_t size) +{ + enum misdn_cfg_elements type_prefix; + char num_prefix[MISDN_MAX_NUMBER_LEN]; + + /* Get prefix string. */ + switch (number_type) { + case NUMTYPE_UNKNOWN: + type_prefix = MISDN_CFG_TON_PREFIX_UNKNOWN; + break; + case NUMTYPE_INTERNATIONAL: + type_prefix = MISDN_CFG_TON_PREFIX_INTERNATIONAL; + break; + case NUMTYPE_NATIONAL: + type_prefix = MISDN_CFG_TON_PREFIX_NATIONAL; + break; + case NUMTYPE_NETWORK_SPECIFIC: + type_prefix = MISDN_CFG_TON_PREFIX_NETWORK_SPECIFIC; + break; + case NUMTYPE_SUBSCRIBER: + type_prefix = MISDN_CFG_TON_PREFIX_SUBSCRIBER; + break; + case NUMTYPE_ABBREVIATED: + type_prefix = MISDN_CFG_TON_PREFIX_ABBREVIATED; + break; + default: + /* Type-of-number does not have a prefix that can be added. */ + return; + } + misdn_cfg_get(port, type_prefix, num_prefix, sizeof(num_prefix)); + + misdn_prefix_string(num_prefix, number, size); +} + static void export_aoc_vars(int originator, struct ast_channel *ast, struct misdn_bchannel *bc) { char buf[128]; @@ -692,7 +3410,8 @@ } if (originator == ORG_AST) { - if (!(ast = ast_bridged_channel(ast))) { + ast = ast_bridged_channel(ast); + if (!ast) { return; } } @@ -739,14 +3458,15 @@ default: break; } - + bc->AOCD_need_export = 0; } /*************** Helpers END *************/ static void sighandler(int sig) -{} +{ +} static void *misdn_tasks_thread_func(void *data) { @@ -758,7 +3478,7 @@ sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGUSR1); sigaction(SIGUSR1, &sa, NULL); - + sem_post((sem_t *)data); while (1) { @@ -785,11 +3505,12 @@ } chan_misdn_log(4, 0, "Starting misdn_tasks thread\n"); - + misdn_tasks = sched_context_create(); pthread_create(&misdn_tasks_thread, NULL, misdn_tasks_thread_func, &blocker); - while (sem_wait(&blocker) && --i); + while (sem_wait(&blocker) && --i) { + } sem_destroy(&blocker); } @@ -797,7 +3518,7 @@ { if (misdn_tasks) { chan_misdn_log(4, 0, "Killing misdn_tasks thread\n"); - if ( pthread_cancel(misdn_tasks_thread) == 0 ) { + if (pthread_cancel(misdn_tasks_thread) == 0) { cb_log(4, 0, "Joining misdn_tasks thread\n"); pthread_join(misdn_tasks_thread, NULL); } @@ -841,6 +3562,7 @@ static int misdn_l1_task(const void *vdata) { const int *data = vdata; + misdn_lib_isdn_l1watcher(*data); chan_misdn_log(5, *data, "L1watcher timeout\n"); return 1; @@ -867,21 +3589,22 @@ tv_end.tv_sec += ch->overlap_dial; tv_now = ast_tvnow(); - if ((diff = ast_tvdiff_ms(tv_end, tv_now)) > 100) { + diff = ast_tvdiff_ms(tv_end, tv_now); + if (100 < diff) { return diff; } /* if we are 100ms near the timeout, we are satisfied.. */ stop_indicate(ch); - if (ast_strlen_zero(ch->bc->dad)) { + if (ast_strlen_zero(ch->bc->dialed.number)) { dad = "s"; - ast_copy_string(ch->ast->exten, "s", sizeof(ch->ast->exten)); + strcpy(ch->ast->exten, dad); } else { - dad = ch->bc->dad; + dad = ch->bc->dialed.number; } - if (ast_exists_extension(ch->ast, ch->context, dad, 1, ch->bc->oad)) { + if (ast_exists_extension(ch->ast, ch->context, dad, 1, ch->bc->caller.number)) { ch->state = MISDN_DIALING; if (pbx_start_chan(ch) < 0) { chan_misdn_log(-1, ch->bc->port, "ast_pbx_start returned < 0 in misdn_overlap_dial_task\n"); @@ -900,7 +3623,8 @@ static void send_digit_to_chan(struct chan_list *cl, char digit) { - static const char *dtmf_tones[] = { + static const char * const dtmf_tones[] = { +/* *INDENT-OFF* */ "!941+1336/100,!0/100", /* 0 */ "!697+1209/100,!0/100", /* 1 */ "!697+1336/100,!0/100", /* 2 */ @@ -917,9 +3641,10 @@ "!941+1633/100,!0/100", /* D */ "!941+1209/100,!0/100", /* * */ "!941+1477/100,!0/100", /* # */ +/* *INDENT-ON* */ }; - struct ast_channel *chan = cl->ast; - + struct ast_channel *chan = cl->ast; + if (digit >= '0' && digit <='9') { ast_playtones_start(chan, 0, dtmf_tones[digit - '0'], 0); } else if (digit >= 'A' && digit <= 'D') { @@ -958,8 +3683,10 @@ level = 1; } else if (!strcasecmp(a->argv[3], "off")) { level = 0; + } else if (isdigit(a->argv[3][0])) { + level = atoi(a->argv[3]); } else { - level = atoi(a->argv[3]); + return CLI_SHOWUSAGE; } switch (a->argc) { @@ -975,7 +3702,7 @@ only = 1; } } - + for (i = 0; i <= max_ports; i++) { misdn_debug[i] = level; misdn_debug_only[i] = only; @@ -1251,7 +3978,9 @@ ast_cli(a->fd, "Unknown option: %s\n", a->argv[3]); return CLI_SHOWUSAGE; } - } else if (a->argc == 3 || onlyport == 0) { + } + + if (a->argc == 3 || onlyport == 0) { ast_cli(a->fd, "mISDN General-Config:\n"); for (elem = MISDN_GEN_FIRST + 1, linebreak = 1; elem < MISDN_GEN_LAST; elem++, linebreak++) { misdn_cfg_get_config_string(0, elem, buffer, sizeof(buffer)); @@ -1262,23 +3991,24 @@ if (onlyport < 0) { int port = misdn_cfg_get_next_port(0); + for (; port > 0; port = misdn_cfg_get_next_port(port)) { ast_cli(a->fd, "\n[PORT %d]\n", port); for (elem = MISDN_CFG_FIRST + 1, linebreak = 1; elem < MISDN_CFG_LAST; elem++, linebreak++) { misdn_cfg_get_config_string(port, elem, buffer, sizeof(buffer)); ast_cli(a->fd, "%-36s%s", buffer, !(linebreak % 2) ? "\n" : ""); - } + } ast_cli(a->fd, "\n"); } } - + if (onlyport > 0) { if (misdn_cfg_is_port_valid(onlyport)) { ast_cli(a->fd, "[PORT %d]\n", onlyport); for (elem = MISDN_CFG_FIRST + 1, linebreak = 1; elem < MISDN_CFG_LAST; elem++, linebreak++) { misdn_cfg_get_config_string(onlyport, elem, buffer, sizeof(buffer)); ast_cli(a->fd, "%-36s%s", buffer, !(linebreak % 2) ? "\n" : ""); - } + } ast_cli(a->fd, "\n"); } else { ast_cli(a->fd, "Port %d is not active!\n", onlyport); @@ -1293,39 +4023,41 @@ char txt[255]; }; -static struct state_struct state_array[] = { +static const struct state_struct state_array[] = { +/* *INDENT-OFF* */ { MISDN_NOTHING, "NOTHING" }, /* at beginning */ - { MISDN_WAITING4DIGS, "WAITING4DIGS" }, /* when waiting for infos */ - { MISDN_EXTCANTMATCH, "EXTCANTMATCH" }, /* when asterisk couldn't match our ext */ - { MISDN_INCOMING_SETUP, "INCOMING SETUP" }, /* when pbx_start */ - { MISDN_DIALING, "DIALING" }, /* when pbx_start */ - { MISDN_PROGRESS, "PROGRESS" }, /* when pbx_start */ - { MISDN_PROCEEDING, "PROCEEDING" }, /* when pbx_start */ - { MISDN_CALLING, "CALLING" }, /* when misdn_call is called */ - { MISDN_CALLING_ACKNOWLEDGE, "CALLING_ACKNOWLEDGE" }, /* when misdn_call is called */ - { MISDN_ALERTING, "ALERTING" }, /* when Alerting */ - { MISDN_BUSY, "BUSY" }, /* when BUSY */ - { MISDN_CONNECTED, "CONNECTED" }, /* when connected */ - { MISDN_PRECONNECTED, "PRECONNECTED" }, /* when connected */ - { MISDN_DISCONNECTED, "DISCONNECTED" }, /* when connected */ - { MISDN_RELEASED, "RELEASED" }, /* when connected */ - { MISDN_BRIDGED, "BRIDGED" }, /* when bridged */ + { MISDN_WAITING4DIGS, "WAITING4DIGS" }, /* when waiting for infos */ + { MISDN_EXTCANTMATCH, "EXTCANTMATCH" }, /* when asterisk couldn't match our ext */ + { MISDN_INCOMING_SETUP, "INCOMING SETUP" }, /* when pbx_start */ + { MISDN_DIALING, "DIALING" }, /* when pbx_start */ + { MISDN_PROGRESS, "PROGRESS" }, /* when pbx_start */ + { MISDN_PROCEEDING, "PROCEEDING" }, /* when pbx_start */ + { MISDN_CALLING, "CALLING" }, /* when misdn_call is called */ + { MISDN_CALLING_ACKNOWLEDGE, "CALLING_ACKNOWLEDGE" }, /* when misdn_call is called */ + { MISDN_ALERTING, "ALERTING" }, /* when Alerting */ + { MISDN_BUSY, "BUSY" }, /* when BUSY */ + { MISDN_CONNECTED, "CONNECTED" }, /* when connected */ + { MISDN_PRECONNECTED, "PRECONNECTED" }, /* when connected */ + { MISDN_DISCONNECTED, "DISCONNECTED" }, /* when connected */ + { MISDN_RELEASED, "RELEASED" }, /* when connected */ + { MISDN_BRIDGED, "BRIDGED" }, /* when bridged */ { MISDN_CLEANING, "CLEANING" }, /* when hangup from * but we were connected before */ - { MISDN_HUNGUP_FROM_MISDN, "HUNGUP_FROM_MISDN" }, /* when DISCONNECT/RELEASE/REL_COMP came from misdn */ - { MISDN_HOLDED, "HOLDED" }, /* when DISCONNECT/RELEASE/REL_COMP came from misdn */ - { MISDN_HOLD_DISCONNECT, "HOLD_DISCONNECT" }, /* when DISCONNECT/RELEASE/REL_COMP came from misdn */ + { MISDN_HUNGUP_FROM_MISDN, "HUNGUP_FROM_MISDN" }, /* when DISCONNECT/RELEASE/REL_COMP came from misdn */ + { MISDN_HOLDED, "HOLDED" }, /* when DISCONNECT/RELEASE/REL_COMP came from misdn */ + { MISDN_HOLD_DISCONNECT, "HOLD_DISCONNECT" }, /* when DISCONNECT/RELEASE/REL_COMP came from misdn */ { MISDN_HUNGUP_FROM_AST, "HUNGUP_FROM_AST" }, /* when DISCONNECT/RELEASE/REL_COMP came out of misdn_hangup */ +/* *INDENT-ON* */ }; -static const char *misdn_get_ch_state(struct chan_list *p) +static const char *misdn_get_ch_state(struct chan_list *p) { int i; static char state[8]; - + if (!p) { return NULL; } - + for (i = 0; i < ARRAY_LEN(state_array); i++) { if (state_array[i].state == p->state) { return state_array[i].txt; @@ -1346,7 +4078,7 @@ ast_log(LOG_WARNING, "chan_misdn is not initialized properly, still reloading ?\n"); return ; } - + free_robin_list(); misdn_cfg_reload(); misdn_cfg_update_ptp(); @@ -1382,21 +4114,30 @@ return CLI_SUCCESS; } -static void print_bc_info (int fd, struct chan_list *help, struct misdn_bchannel *bc) +static void print_bc_info(int fd, struct chan_list *help, struct misdn_bchannel *bc) { struct ast_channel *ast = help->ast; + ast_cli(fd, - "* Pid:%d Prt:%d Ch:%d Mode:%s Org:%s dad:%s oad:%s rad:%s ctx:%s state:%s\n", - - bc->pid, bc->port, bc->channel, + "* Pid:%d Port:%d Ch:%d Mode:%s Orig:%s dialed:%s\n" + " --> caller:\"%s\" <%s>\n" + " --> redirecting-from:\"%s\" <%s>\n" + " --> redirecting-to:\"%s\" <%s>\n" + " --> context:%s state:%s\n", + bc->pid, + bc->port, + bc->channel, bc->nt ? "NT" : "TE", help->originator == ORG_AST ? "*" : "I", - ast ? ast->exten : NULL, - ast ? ast->cid.cid_num : NULL, - bc->rad, - ast ? ast->context : NULL, - misdn_get_ch_state(help) - ); + ast ? ast->exten : "", + (ast && ast->cid.cid_name) ? ast->cid.cid_name : "", + (ast && ast->cid.cid_num) ? ast->cid.cid_num : "", + bc->redirecting.from.name, + bc->redirecting.from.number, + bc->redirecting.to.name, + bc->redirecting.to.number, + ast ? ast->context : "", + misdn_get_ch_state(help)); if (misdn_debug[bc->port] > 0) { ast_cli(fd, " --> astname: %s\n" @@ -1419,21 +4160,18 @@ help->l3id, help->addr, bc->addr, - bc ? bc->l3_id : -1, + bc->l3_id, bc->display, - bc->active, bc_state2str(bc->bc_state), bearer2str(bc->capability), #ifdef MISDN_1_2 bc->pipeline, #else - bc->ec_enable, + bc->ec_enable, #endif - help->norxtone, help->notxtone, - bc->holded - ); + bc->holded); } } @@ -1457,11 +4195,11 @@ } help = cl_te; - + ast_cli(a->fd, "Channel List: %p\n", cl_te); for (; help; help = help->next) { - struct misdn_bchannel *bc = help->bc; + struct misdn_bchannel *bc = help->bc; struct ast_channel *ast = help->ast; if (!ast) { if (!bc) { @@ -1481,15 +4219,17 @@ if (help->state == MISDN_HOLDED) { ast_cli(a->fd, "ITS A HOLDED BC:\n"); ast_cli(a->fd, " --> l3_id: %x\n" - " --> dad:%s oad:%s\n" - " --> hold_port: %d\n" - " --> hold_channel: %d\n", - help->l3id, - ast->exten, - ast->cid.cid_num, - help->hold_info.port, - help->hold_info.channel - ); + " --> dialed:%s\n" + " --> caller:\"%s\" <%s>\n" + " --> hold_port: %d\n" + " --> hold_channel: %d\n", + help->l3id, + ast->exten, + ast->cid.cid_name ? ast->cid.cid_name : "", + ast->cid.cid_num ? ast->cid.cid_num : "", + help->hold_info.port, + help->hold_info.channel + ); } else { ast_cli(a->fd, "* Channel in unknown STATE !!! Exten:%s, Callerid:%s\n", ast->exten, ast->cid.cid_num); } @@ -1523,13 +4263,13 @@ help = cl_te; for (; help; help = help->next) { - struct misdn_bchannel *bc = help->bc; + struct misdn_bchannel *bc = help->bc; struct ast_channel *ast = help->ast; - + if (bc && ast) { if (!strcasecmp(ast->name, a->argv[3])) { print_bc_info(a->fd, help, bc); - break; + break; } } } @@ -1580,8 +4320,9 @@ ast_cli(a->fd, "BEGIN STACK_LIST:\n"); for (port = misdn_cfg_get_next_port(0); port > 0; - port = misdn_cfg_get_next_port(port)) { + port = misdn_cfg_get_next_port(port)) { char buf[128]; + get_show_stack_details(port, buf); ast_cli(a->fd, " %s Debug:%d%s\n", buf, misdn_debug[port], misdn_debug_only[port] ? "(only)" : ""); } @@ -1610,7 +4351,7 @@ ast_cli(a->fd, "Port\tin_calls\tout_calls\n"); for (port = misdn_cfg_get_next_port(0); port > 0; - port = misdn_cfg_get_next_port(port)) { + port = misdn_cfg_get_next_port(port)) { ast_cli(a->fd, "%d\t%d\t\t%d\n", port, misdn_in_calls[port], misdn_out_calls[port]); } ast_cli(a->fd, "\n"); @@ -1639,7 +4380,7 @@ } port = atoi(a->argv[3]); - + ast_cli(a->fd, "BEGIN STACK_LIST:\n"); get_show_stack_details(port, buf); ast_cli(a->fd, " %s Debug:%d%s\n", buf, misdn_debug[port], misdn_debug_only[port] ? "(only)" : ""); @@ -1647,15 +4388,749 @@ return CLI_SUCCESS; } +#if defined(AST_MISDN_ENHANCEMENTS) && defined(CCBS_TEST_MESSAGES) +static const struct FacParm Fac_Msgs[] = { +/* *INDENT-OFF* */ + [0].Function = Fac_ERROR, + [0].u.ERROR.invokeId = 8, + [0].u.ERROR.errorValue = FacError_CCBS_AlreadyAccepted, + + [1].Function = Fac_RESULT, + [1].u.RESULT.InvokeID = 9, + + [2].Function = Fac_REJECT, + [2].u.REJECT.Code = FacReject_Gen_BadlyStructuredComponent, + + [3].Function = Fac_REJECT, + [3].u.REJECT.InvokeIDPresent = 1, + [3].u.REJECT.InvokeID = 10, + [3].u.REJECT.Code = FacReject_Inv_InitiatorReleasing, + + [4].Function = Fac_REJECT, + [4].u.REJECT.InvokeIDPresent = 1, + [4].u.REJECT.InvokeID = 11, + [4].u.REJECT.Code = FacReject_Res_MistypedResult, + + [5].Function = Fac_REJECT, + [5].u.REJECT.InvokeIDPresent = 1, + [5].u.REJECT.InvokeID = 12, + [5].u.REJECT.Code = FacReject_Err_ErrorResponseUnexpected, + + [6].Function = Fac_StatusRequest, + [6].u.StatusRequest.InvokeID = 13, + [6].u.StatusRequest.ComponentType = FacComponent_Invoke, + [6].u.StatusRequest.Component.Invoke.Q931ie.Bc.Length = 2, + [6].u.StatusRequest.Component.Invoke.Q931ie.Bc.Contents = "AB", + [6].u.StatusRequest.Component.Invoke.Q931ie.Llc.Length = 3, + [6].u.StatusRequest.Component.Invoke.Q931ie.Llc.Contents = "CDE", + [6].u.StatusRequest.Component.Invoke.Q931ie.Hlc.Length = 4, + [6].u.StatusRequest.Component.Invoke.Q931ie.Hlc.Contents = "FGHI", + [6].u.StatusRequest.Component.Invoke.CompatibilityMode = 1, + + [7].Function = Fac_StatusRequest, + [7].u.StatusRequest.InvokeID = 14, + [7].u.StatusRequest.ComponentType = FacComponent_Result, + [7].u.StatusRequest.Component.Result.Status = 2, + + [8].Function = Fac_CallInfoRetain, + [8].u.CallInfoRetain.InvokeID = 15, + [8].u.CallInfoRetain.CallLinkageID = 115, + + [9].Function = Fac_EraseCallLinkageID, + [9].u.EraseCallLinkageID.InvokeID = 16, + [9].u.EraseCallLinkageID.CallLinkageID = 105, + + [10].Function = Fac_CCBSDeactivate, + [10].u.CCBSDeactivate.InvokeID = 17, + [10].u.CCBSDeactivate.ComponentType = FacComponent_Invoke, + [10].u.CCBSDeactivate.Component.Invoke.CCBSReference = 2, + + [11].Function = Fac_CCBSDeactivate, + [11].u.CCBSDeactivate.InvokeID = 18, + [11].u.CCBSDeactivate.ComponentType = FacComponent_Result, + + [12].Function = Fac_CCBSErase, + [12].u.CCBSErase.InvokeID = 19, + [12].u.CCBSErase.Q931ie.Bc.Length = 2, + [12].u.CCBSErase.Q931ie.Bc.Contents = "JK", + [12].u.CCBSErase.AddressOfB.Party.Type = 0, + [12].u.CCBSErase.AddressOfB.Party.LengthOfNumber = 5, + [12].u.CCBSErase.AddressOfB.Party.Number = "33403", + [12].u.CCBSErase.AddressOfB.Subaddress.Type = 0, + [12].u.CCBSErase.AddressOfB.Subaddress.Length = 4, + [12].u.CCBSErase.AddressOfB.Subaddress.u.UserSpecified.Information = "3748", + [12].u.CCBSErase.RecallMode = 1, + [12].u.CCBSErase.CCBSReference = 102, + [12].u.CCBSErase.Reason = 3, + + [13].Function = Fac_CCBSErase, + [13].u.CCBSErase.InvokeID = 20, + [13].u.CCBSErase.Q931ie.Bc.Length = 2, + [13].u.CCBSErase.Q931ie.Bc.Contents = "JK", + [13].u.CCBSErase.AddressOfB.Party.Type = 1, + [13].u.CCBSErase.AddressOfB.Party.LengthOfNumber = 11, + [13].u.CCBSErase.AddressOfB.Party.TypeOfNumber = 1, + [13].u.CCBSErase.AddressOfB.Party.Number = "18003020102", + [13].u.CCBSErase.AddressOfB.Subaddress.Type = 0, + [13].u.CCBSErase.AddressOfB.Subaddress.Length = 4, + [13].u.CCBSErase.AddressOfB.Subaddress.u.UserSpecified.OddCountPresent = 1, + [13].u.CCBSErase.AddressOfB.Subaddress.u.UserSpecified.OddCount = 1, + [13].u.CCBSErase.AddressOfB.Subaddress.u.UserSpecified.Information = "3748", + [13].u.CCBSErase.RecallMode = 1, + [13].u.CCBSErase.CCBSReference = 102, + [13].u.CCBSErase.Reason = 3, + + [14].Function = Fac_CCBSErase, + [14].u.CCBSErase.InvokeID = 21, + [14].u.CCBSErase.Q931ie.Bc.Length = 2, + [14].u.CCBSErase.Q931ie.Bc.Contents = "JK", + [14].u.CCBSErase.AddressOfB.Party.Type = 2, + [14].u.CCBSErase.AddressOfB.Party.LengthOfNumber = 4, + [14].u.CCBSErase.AddressOfB.Party.Number = "1803", + [14].u.CCBSErase.AddressOfB.Subaddress.Type = 1, + [14].u.CCBSErase.AddressOfB.Subaddress.Length = 4, + [14].u.CCBSErase.AddressOfB.Subaddress.u.Nsap = "6492", + [14].u.CCBSErase.RecallMode = 1, + [14].u.CCBSErase.CCBSReference = 102, + [14].u.CCBSErase.Reason = 3, + + [15].Function = Fac_CCBSErase, + [15].u.CCBSErase.InvokeID = 22, + [15].u.CCBSErase.Q931ie.Bc.Length = 2, + [15].u.CCBSErase.Q931ie.Bc.Contents = "JK", + [15].u.CCBSErase.AddressOfB.Party.Type = 3, + [15].u.CCBSErase.AddressOfB.Party.LengthOfNumber = 4, + [15].u.CCBSErase.AddressOfB.Party.Number = "1803", + [15].u.CCBSErase.RecallMode = 1, + [15].u.CCBSErase.CCBSReference = 102, + [15].u.CCBSErase.Reason = 3, + + [16].Function = Fac_CCBSErase, + [16].u.CCBSErase.InvokeID = 23, + [16].u.CCBSErase.Q931ie.Bc.Length = 2, + [16].u.CCBSErase.Q931ie.Bc.Contents = "JK", + [16].u.CCBSErase.AddressOfB.Party.Type = 4, + [16].u.CCBSErase.AddressOfB.Party.LengthOfNumber = 4, + [16].u.CCBSErase.AddressOfB.Party.Number = "1803", + [16].u.CCBSErase.RecallMode = 1, + [16].u.CCBSErase.CCBSReference = 102, + [16].u.CCBSErase.Reason = 3, + + [17].Function = Fac_CCBSErase, + [17].u.CCBSErase.InvokeID = 24, + [17].u.CCBSErase.Q931ie.Bc.Length = 2, + [17].u.CCBSErase.Q931ie.Bc.Contents = "JK", + [17].u.CCBSErase.AddressOfB.Party.Type = 5, + [17].u.CCBSErase.AddressOfB.Party.LengthOfNumber = 11, + [17].u.CCBSErase.AddressOfB.Party.TypeOfNumber = 4, + [17].u.CCBSErase.AddressOfB.Party.Number = "18003020102", + [17].u.CCBSErase.RecallMode = 1, + [17].u.CCBSErase.CCBSReference = 102, + [17].u.CCBSErase.Reason = 3, + + [18].Function = Fac_CCBSErase, + [18].u.CCBSErase.InvokeID = 25, + [18].u.CCBSErase.Q931ie.Bc.Length = 2, + [18].u.CCBSErase.Q931ie.Bc.Contents = "JK", + [18].u.CCBSErase.AddressOfB.Party.Type = 8, + [18].u.CCBSErase.AddressOfB.Party.LengthOfNumber = 4, + [18].u.CCBSErase.AddressOfB.Party.Number = "1803", + [18].u.CCBSErase.RecallMode = 1, + [18].u.CCBSErase.CCBSReference = 102, + [18].u.CCBSErase.Reason = 3, + + [19].Function = Fac_CCBSRemoteUserFree, + [19].u.CCBSRemoteUserFree.InvokeID = 26, + [19].u.CCBSRemoteUserFree.Q931ie.Bc.Length = 2, + [19].u.CCBSRemoteUserFree.Q931ie.Bc.Contents = "JK", + [19].u.CCBSRemoteUserFree.AddressOfB.Party.Type = 8, + [19].u.CCBSRemoteUserFree.AddressOfB.Party.LengthOfNumber = 4, + [19].u.CCBSRemoteUserFree.AddressOfB.Party.Number = "1803", + [19].u.CCBSRemoteUserFree.RecallMode = 1, + [19].u.CCBSRemoteUserFree.CCBSReference = 102, + + [20].Function = Fac_CCBSCall, + [20].u.CCBSCall.InvokeID = 27, + [20].u.CCBSCall.CCBSReference = 115, + + [21].Function = Fac_CCBSStatusRequest, + [21].u.CCBSStatusRequest.InvokeID = 28, + [21].u.CCBSStatusRequest.ComponentType = FacComponent_Invoke, + [21].u.CCBSStatusRequest.Component.Invoke.Q931ie.Bc.Length = 2, + [21].u.CCBSStatusRequest.Component.Invoke.Q931ie.Bc.Contents = "JK", + [21].u.CCBSStatusRequest.Component.Invoke.RecallMode = 1, + [21].u.CCBSStatusRequest.Component.Invoke.CCBSReference = 102, + + [22].Function = Fac_CCBSStatusRequest, + [22].u.CCBSStatusRequest.InvokeID = 29, + [22].u.CCBSStatusRequest.ComponentType = FacComponent_Result, + [22].u.CCBSStatusRequest.Component.Result.Free = 1, + + [23].Function = Fac_CCBSBFree, + [23].u.CCBSBFree.InvokeID = 30, + [23].u.CCBSBFree.Q931ie.Bc.Length = 2, + [23].u.CCBSBFree.Q931ie.Bc.Contents = "JK", + [23].u.CCBSBFree.AddressOfB.Party.Type = 8, + [23].u.CCBSBFree.AddressOfB.Party.LengthOfNumber = 4, + [23].u.CCBSBFree.AddressOfB.Party.Number = "1803", + [23].u.CCBSBFree.RecallMode = 1, + [23].u.CCBSBFree.CCBSReference = 14, + + [24].Function = Fac_CCBSStopAlerting, + [24].u.CCBSStopAlerting.InvokeID = 31, + [24].u.CCBSStopAlerting.CCBSReference = 37, + + [25].Function = Fac_CCBSRequest, + [25].u.CCBSRequest.InvokeID = 32, + [25].u.CCBSRequest.ComponentType = FacComponent_Invoke, + [25].u.CCBSRequest.Component.Invoke.CallLinkageID = 57, + + [26].Function = Fac_CCBSRequest, + [26].u.CCBSRequest.InvokeID = 33, + [26].u.CCBSRequest.ComponentType = FacComponent_Result, + [26].u.CCBSRequest.Component.Result.RecallMode = 1, + [26].u.CCBSRequest.Component.Result.CCBSReference = 102, + + [27].Function = Fac_CCBSInterrogate, + [27].u.CCBSInterrogate.InvokeID = 34, + [27].u.CCBSInterrogate.ComponentType = FacComponent_Invoke, + [27].u.CCBSInterrogate.Component.Invoke.AParty.Type = 8, + [27].u.CCBSInterrogate.Component.Invoke.AParty.LengthOfNumber = 4, + [27].u.CCBSInterrogate.Component.Invoke.AParty.Number = "1803", + [27].u.CCBSInterrogate.Component.Invoke.CCBSReferencePresent = 1, + [27].u.CCBSInterrogate.Component.Invoke.CCBSReference = 76, + + [28].Function = Fac_CCBSInterrogate, + [28].u.CCBSInterrogate.InvokeID = 35, + [28].u.CCBSInterrogate.ComponentType = FacComponent_Invoke, + [28].u.CCBSInterrogate.Component.Invoke.AParty.Type = 8, + [28].u.CCBSInterrogate.Component.Invoke.AParty.LengthOfNumber = 4, + [28].u.CCBSInterrogate.Component.Invoke.AParty.Number = "1803", + + [29].Function = Fac_CCBSInterrogate, + [29].u.CCBSInterrogate.InvokeID = 36, + [29].u.CCBSInterrogate.ComponentType = FacComponent_Invoke, + [29].u.CCBSInterrogate.Component.Invoke.CCBSReferencePresent = 1, + [29].u.CCBSInterrogate.Component.Invoke.CCBSReference = 76, + + [30].Function = Fac_CCBSInterrogate, + [30].u.CCBSInterrogate.InvokeID = 37, + [30].u.CCBSInterrogate.ComponentType = FacComponent_Invoke, + + [31].Function = Fac_CCBSInterrogate, + [31].u.CCBSInterrogate.InvokeID = 38, + [31].u.CCBSInterrogate.ComponentType = FacComponent_Result, + [31].u.CCBSInterrogate.Component.Result.RecallMode = 1, + + [32].Function = Fac_CCBSInterrogate, + [32].u.CCBSInterrogate.InvokeID = 39, + [32].u.CCBSInterrogate.ComponentType = FacComponent_Result, + [32].u.CCBSInterrogate.Component.Result.RecallMode = 1, + [32].u.CCBSInterrogate.Component.Result.NumRecords = 1, + [32].u.CCBSInterrogate.Component.Result.CallDetails[0].CCBSReference = 12, + [32].u.CCBSInterrogate.Component.Result.CallDetails[0].Q931ie.Bc.Length = 2, + [32].u.CCBSInterrogate.Component.Result.CallDetails[0].Q931ie.Bc.Contents = "JK", + [32].u.CCBSInterrogate.Component.Result.CallDetails[0].AddressOfB.Party.Type = 8, + [32].u.CCBSInterrogate.Component.Result.CallDetails[0].AddressOfB.Party.LengthOfNumber = 4, + [32].u.CCBSInterrogate.Component.Result.CallDetails[0].AddressOfB.Party.Number = "1803", + [32].u.CCBSInterrogate.Component.Result.CallDetails[0].SubaddressOfA.Type = 1, + [32].u.CCBSInterrogate.Component.Result.CallDetails[0].SubaddressOfA.Length = 4, + [32].u.CCBSInterrogate.Component.Result.CallDetails[0].SubaddressOfA.u.Nsap = "6492", + + [33].Function = Fac_CCBSInterrogate, + [33].u.CCBSInterrogate.InvokeID = 40, + [33].u.CCBSInterrogate.ComponentType = FacComponent_Result, + [33].u.CCBSInterrogate.Component.Result.RecallMode = 1, + [33].u.CCBSInterrogate.Component.Result.NumRecords = 2, + [33].u.CCBSInterrogate.Component.Result.CallDetails[0].CCBSReference = 12, + [33].u.CCBSInterrogate.Component.Result.CallDetails[0].Q931ie.Bc.Length = 2, + [33].u.CCBSInterrogate.Component.Result.CallDetails[0].Q931ie.Bc.Contents = "JK", + [33].u.CCBSInterrogate.Component.Result.CallDetails[0].AddressOfB.Party.Type = 8, + [33].u.CCBSInterrogate.Component.Result.CallDetails[0].AddressOfB.Party.LengthOfNumber = 4, + [33].u.CCBSInterrogate.Component.Result.CallDetails[0].AddressOfB.Party.Number = "1803", + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].CCBSReference = 102, + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].Q931ie.Bc.Length = 2, + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].Q931ie.Bc.Contents = "LM", + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].AddressOfB.Party.Type = 8, + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].AddressOfB.Party.LengthOfNumber = 4, + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].AddressOfB.Party.Number = "6229", + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].AddressOfB.Subaddress.Type = 1, + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].AddressOfB.Subaddress.Length = 4, + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].AddressOfB.Subaddress.u.Nsap = "8592", + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].SubaddressOfA.Type = 1, + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].SubaddressOfA.Length = 4, + [33].u.CCBSInterrogate.Component.Result.CallDetails[1].SubaddressOfA.u.Nsap = "6492", + + [34].Function = Fac_CCNRRequest, + [34].u.CCNRRequest.InvokeID = 512, + [34].u.CCNRRequest.ComponentType = FacComponent_Invoke, + [34].u.CCNRRequest.Component.Invoke.CallLinkageID = 57, + + [35].Function = Fac_CCNRRequest, + [35].u.CCNRRequest.InvokeID = 150, + [35].u.CCNRRequest.ComponentType = FacComponent_Result, + [35].u.CCNRRequest.Component.Result.RecallMode = 1, + [35].u.CCNRRequest.Component.Result.CCBSReference = 102, + + [36].Function = Fac_CCNRInterrogate, + [36].u.CCNRInterrogate.InvokeID = -129, + [36].u.CCNRInterrogate.ComponentType = FacComponent_Invoke, + + [37].Function = Fac_CCNRInterrogate, + [37].u.CCNRInterrogate.InvokeID = -3, + [37].u.CCNRInterrogate.ComponentType = FacComponent_Result, + [37].u.CCNRInterrogate.Component.Result.RecallMode = 1, + + [38].Function = Fac_CCBS_T_Call, + [38].u.EctExecute.InvokeID = 41, + + [39].Function = Fac_CCBS_T_Suspend, + [39].u.EctExecute.InvokeID = 42, + + [40].Function = Fac_CCBS_T_Resume, + [40].u.EctExecute.InvokeID = 43, + + [41].Function = Fac_CCBS_T_RemoteUserFree, + [41].u.EctExecute.InvokeID = 44, + + [42].Function = Fac_CCBS_T_Available, + [42].u.EctExecute.InvokeID = 45, + + [43].Function = Fac_CCBS_T_Request, + [43].u.CCBS_T_Request.InvokeID = 46, + [43].u.CCBS_T_Request.ComponentType = FacComponent_Invoke, + [43].u.CCBS_T_Request.Component.Invoke.Destination.Party.Type = 8, + [43].u.CCBS_T_Request.Component.Invoke.Destination.Party.LengthOfNumber = 4, + [43].u.CCBS_T_Request.Component.Invoke.Destination.Party.Number = "6229", + [43].u.CCBS_T_Request.Component.Invoke.Q931ie.Bc.Length = 2, + [43].u.CCBS_T_Request.Component.Invoke.Q931ie.Bc.Contents = "LM", + [43].u.CCBS_T_Request.Component.Invoke.RetentionSupported = 1, + [43].u.CCBS_T_Request.Component.Invoke.PresentationAllowedIndicatorPresent = 1, + [43].u.CCBS_T_Request.Component.Invoke.PresentationAllowedIndicator = 1, + [43].u.CCBS_T_Request.Component.Invoke.Originating.Party.Type = 8, + [43].u.CCBS_T_Request.Component.Invoke.Originating.Party.LengthOfNumber = 4, + [43].u.CCBS_T_Request.Component.Invoke.Originating.Party.Number = "9864", + + [44].Function = Fac_CCBS_T_Request, + [44].u.CCBS_T_Request.InvokeID = 47, + [44].u.CCBS_T_Request.ComponentType = FacComponent_Invoke, + [44].u.CCBS_T_Request.Component.Invoke.Destination.Party.Type = 8, + [44].u.CCBS_T_Request.Component.Invoke.Destination.Party.LengthOfNumber = 4, + [44].u.CCBS_T_Request.Component.Invoke.Destination.Party.Number = "6229", + [44].u.CCBS_T_Request.Component.Invoke.Q931ie.Bc.Length = 2, + [44].u.CCBS_T_Request.Component.Invoke.Q931ie.Bc.Contents = "LM", + [44].u.CCBS_T_Request.Component.Invoke.PresentationAllowedIndicatorPresent = 1, + [44].u.CCBS_T_Request.Component.Invoke.PresentationAllowedIndicator = 1, + [44].u.CCBS_T_Request.Component.Invoke.Originating.Party.Type = 8, + [44].u.CCBS_T_Request.Component.Invoke.Originating.Party.LengthOfNumber = 4, + [44].u.CCBS_T_Request.Component.Invoke.Originating.Party.Number = "9864", + + [45].Function = Fac_CCBS_T_Request, + [45].u.CCBS_T_Request.InvokeID = 48, + [45].u.CCBS_T_Request.ComponentType = FacComponent_Invoke, + [45].u.CCBS_T_Request.Component.Invoke.Destination.Party.Type = 8, + [45].u.CCBS_T_Request.Component.Invoke.Destination.Party.LengthOfNumber = 4, + [45].u.CCBS_T_Request.Component.Invoke.Destination.Party.Number = "6229", + [45].u.CCBS_T_Request.Component.Invoke.Q931ie.Bc.Length = 2, + [45].u.CCBS_T_Request.Component.Invoke.Q931ie.Bc.Contents = "LM", + [45].u.CCBS_T_Request.Component.Invoke.Originating.Party.Type = 8, + [45].u.CCBS_T_Request.Component.Invoke.Originating.Party.LengthOfNumber = 4, + [45].u.CCBS_T_Request.Component.Invoke.Originating.Party.Number = "9864", + + [46].Function = Fac_CCBS_T_Request, + [46].u.CCBS_T_Request.InvokeID = 49, + [46].u.CCBS_T_Request.ComponentType = FacComponent_Invoke, + [46].u.CCBS_T_Request.Component.Invoke.Destination.Party.Type = 8, + [46].u.CCBS_T_Request.Component.Invoke.Destination.Party.LengthOfNumber = 4, + [46].u.CCBS_T_Request.Component.Invoke.Destination.Party.Number = "6229", + [46].u.CCBS_T_Request.Component.Invoke.Q931ie.Bc.Length = 2, + [46].u.CCBS_T_Request.Component.Invoke.Q931ie.Bc.Contents = "LM", + [46].u.CCBS_T_Request.Component.Invoke.PresentationAllowedIndicatorPresent = 1, + [46].u.CCBS_T_Request.Component.Invoke.PresentationAllowedIndicator = 1, + + [47].Function = Fac_CCBS_T_Request, + [47].u.CCBS_T_Request.InvokeID = 50, + [47].u.CCBS_T_Request.ComponentType = FacComponent_Invoke, + [47].u.CCBS_T_Request.Component.Invoke.Destination.Party.Type = 8, + [47].u.CCBS_T_Request.Component.Invoke.Destination.Party.LengthOfNumber = 4, + [47].u.CCBS_T_Request.Component.Invoke.Destination.Party.Number = "6229", + [47].u.CCBS_T_Request.Component.Invoke.Q931ie.Bc.Length = 2, + [47].u.CCBS_T_Request.Component.Invoke.Q931ie.Bc.Contents = "LM", + + [48].Function = Fac_CCBS_T_Request, + [48].u.CCBS_T_Request.InvokeID = 51, + [48].u.CCBS_T_Request.ComponentType = FacComponent_Result, + [48].u.CCBS_T_Request.Component.Result.RetentionSupported = 1, + + [49].Function = Fac_CCNR_T_Request, + [49].u.CCNR_T_Request.InvokeID = 52, + [49].u.CCNR_T_Request.ComponentType = FacComponent_Invoke, + [49].u.CCNR_T_Request.Component.Invoke.Destination.Party.Type = 8, + [49].u.CCNR_T_Request.Component.Invoke.Destination.Party.LengthOfNumber = 4, + [49].u.CCNR_T_Request.Component.Invoke.Destination.Party.Number = "6229", + [49].u.CCNR_T_Request.Component.Invoke.Q931ie.Bc.Length = 2, + [49].u.CCNR_T_Request.Component.Invoke.Q931ie.Bc.Contents = "LM", + + [50].Function = Fac_CCNR_T_Request, + [50].u.CCNR_T_Request.InvokeID = 53, + [50].u.CCNR_T_Request.ComponentType = FacComponent_Result, + [50].u.CCNR_T_Request.Component.Result.RetentionSupported = 1, + + [51].Function = Fac_EctExecute, + [51].u.EctExecute.InvokeID = 54, + + [52].Function = Fac_ExplicitEctExecute, + [52].u.ExplicitEctExecute.InvokeID = 55, + [52].u.ExplicitEctExecute.LinkID = 23, + + [53].Function = Fac_RequestSubaddress, + [53].u.RequestSubaddress.InvokeID = 56, + + [54].Function = Fac_SubaddressTransfer, + [54].u.SubaddressTransfer.InvokeID = 57, + [54].u.SubaddressTransfer.Subaddress.Type = 1, + [54].u.SubaddressTransfer.Subaddress.Length = 4, + [54].u.SubaddressTransfer.Subaddress.u.Nsap = "6492", + + [55].Function = Fac_EctLinkIdRequest, + [55].u.EctLinkIdRequest.InvokeID = 58, + [55].u.EctLinkIdRequest.ComponentType = FacComponent_Invoke, + + [56].Function = Fac_EctLinkIdRequest, + [56].u.EctLinkIdRequest.InvokeID = 59, + [56].u.EctLinkIdRequest.ComponentType = FacComponent_Result, + [56].u.EctLinkIdRequest.Component.Result.LinkID = 76, + + [57].Function = Fac_EctInform, + [57].u.EctInform.InvokeID = 60, + [57].u.EctInform.Status = 1, + [57].u.EctInform.RedirectionPresent = 1, + [57].u.EctInform.Redirection.Type = 0, + [57].u.EctInform.Redirection.Unscreened.Type = 8, + [57].u.EctInform.Redirection.Unscreened.LengthOfNumber = 4, + [57].u.EctInform.Redirection.Unscreened.Number = "6229", + + [58].Function = Fac_EctInform, + [58].u.EctInform.InvokeID = 61, + [58].u.EctInform.Status = 1, + [58].u.EctInform.RedirectionPresent = 1, + [58].u.EctInform.Redirection.Type = 1, + + [59].Function = Fac_EctInform, + [59].u.EctInform.InvokeID = 62, + [59].u.EctInform.Status = 1, + [59].u.EctInform.RedirectionPresent = 1, + [59].u.EctInform.Redirection.Type = 2, + + [60].Function = Fac_EctInform, + [60].u.EctInform.InvokeID = 63, + [60].u.EctInform.Status = 1, + [60].u.EctInform.RedirectionPresent = 1, + [60].u.EctInform.Redirection.Type = 3, + [60].u.EctInform.Redirection.Unscreened.Type = 8, + [60].u.EctInform.Redirection.Unscreened.LengthOfNumber = 4, + [60].u.EctInform.Redirection.Unscreened.Number = "3340", + + [61].Function = Fac_EctInform, + [61].u.EctInform.InvokeID = 64, + [61].u.EctInform.Status = 1, + [61].u.EctInform.RedirectionPresent = 0, + + [62].Function = Fac_EctLoopTest, + [62].u.EctLoopTest.InvokeID = 65, + [62].u.EctLoopTest.ComponentType = FacComponent_Invoke, + [62].u.EctLoopTest.Component.Invoke.CallTransferID = 7, + + [63].Function = Fac_EctLoopTest, + [63].u.EctLoopTest.InvokeID = 66, + [63].u.EctLoopTest.ComponentType = FacComponent_Result, + [63].u.EctLoopTest.Component.Result.LoopResult = 2, + + [64].Function = Fac_ActivationDiversion, + [64].u.ActivationDiversion.InvokeID = 67, + [64].u.ActivationDiversion.ComponentType = FacComponent_Invoke, + [64].u.ActivationDiversion.Component.Invoke.Procedure = 2, + [64].u.ActivationDiversion.Component.Invoke.BasicService = 3, + [64].u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.Type = 4, + [64].u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.LengthOfNumber = 4, + [64].u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.Number = "1803", + [64].u.ActivationDiversion.Component.Invoke.ServedUser.Type = 4, + [64].u.ActivationDiversion.Component.Invoke.ServedUser.LengthOfNumber = 4, + [64].u.ActivationDiversion.Component.Invoke.ServedUser.Number = "5398", + + [65].Function = Fac_ActivationDiversion, + [65].u.ActivationDiversion.InvokeID = 68, + [65].u.ActivationDiversion.ComponentType = FacComponent_Invoke, + [65].u.ActivationDiversion.Component.Invoke.Procedure = 1, + [65].u.ActivationDiversion.Component.Invoke.BasicService = 5, + [65].u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.Type = 4, + [65].u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.LengthOfNumber = 4, + [65].u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.Number = "1803", + + [66].Function = Fac_ActivationDiversion, + [66].u.ActivationDiversion.InvokeID = 69, + [66].u.ActivationDiversion.ComponentType = FacComponent_Result, + + [67].Function = Fac_DeactivationDiversion, + [67].u.DeactivationDiversion.InvokeID = 70, + [67].u.DeactivationDiversion.ComponentType = FacComponent_Invoke, + [67].u.DeactivationDiversion.Component.Invoke.Procedure = 1, + [67].u.DeactivationDiversion.Component.Invoke.BasicService = 5, + + [68].Function = Fac_DeactivationDiversion, + [68].u.DeactivationDiversion.InvokeID = 71, + [68].u.DeactivationDiversion.ComponentType = FacComponent_Result, + + [69].Function = Fac_ActivationStatusNotificationDiv, + [69].u.ActivationStatusNotificationDiv.InvokeID = 72, + [69].u.ActivationStatusNotificationDiv.Procedure = 1, + [69].u.ActivationStatusNotificationDiv.BasicService = 5, + [69].u.ActivationStatusNotificationDiv.ForwardedTo.Party.Type = 4, + [69].u.ActivationStatusNotificationDiv.ForwardedTo.Party.LengthOfNumber = 4, + [69].u.ActivationStatusNotificationDiv.ForwardedTo.Party.Number = "1803", + + [70].Function = Fac_DeactivationStatusNotificationDiv, + [70].u.DeactivationStatusNotificationDiv.InvokeID = 73, + [70].u.DeactivationStatusNotificationDiv.Procedure = 1, + [70].u.DeactivationStatusNotificationDiv.BasicService = 5, + + [71].Function = Fac_InterrogationDiversion, + [71].u.InterrogationDiversion.InvokeID = 74, + [71].u.InterrogationDiversion.ComponentType = FacComponent_Invoke, + [71].u.InterrogationDiversion.Component.Invoke.Procedure = 1, + [71].u.InterrogationDiversion.Component.Invoke.BasicService = 5, + + [72].Function = Fac_InterrogationDiversion, + [72].u.InterrogationDiversion.InvokeID = 75, + [72].u.InterrogationDiversion.ComponentType = FacComponent_Invoke, + [72].u.InterrogationDiversion.Component.Invoke.Procedure = 1, + + [73].Function = Fac_InterrogationDiversion, + [73].u.InterrogationDiversion.InvokeID = 76, + [73].u.InterrogationDiversion.ComponentType = FacComponent_Result, + [73].u.InterrogationDiversion.Component.Result.NumRecords = 2, + [73].u.InterrogationDiversion.Component.Result.List[0].Procedure = 2, + [73].u.InterrogationDiversion.Component.Result.List[0].BasicService = 5, + [73].u.InterrogationDiversion.Component.Result.List[0].ForwardedTo.Party.Type = 4, + [73].u.InterrogationDiversion.Component.Result.List[0].ForwardedTo.Party.LengthOfNumber = 4, + [73].u.InterrogationDiversion.Component.Result.List[0].ForwardedTo.Party.Number = "1803", + [73].u.InterrogationDiversion.Component.Result.List[1].Procedure = 1, + [73].u.InterrogationDiversion.Component.Result.List[1].BasicService = 3, + [73].u.InterrogationDiversion.Component.Result.List[1].ForwardedTo.Party.Type = 4, + [73].u.InterrogationDiversion.Component.Result.List[1].ForwardedTo.Party.LengthOfNumber = 4, + [73].u.InterrogationDiversion.Component.Result.List[1].ForwardedTo.Party.Number = "1903", + [73].u.InterrogationDiversion.Component.Result.List[1].ServedUser.Type = 4, + [73].u.InterrogationDiversion.Component.Result.List[1].ServedUser.LengthOfNumber = 4, + [73].u.InterrogationDiversion.Component.Result.List[1].ServedUser.Number = "5398", + + [74].Function = Fac_DiversionInformation, + [74].u.DiversionInformation.InvokeID = 77, + [74].u.DiversionInformation.DiversionReason = 3, + [74].u.DiversionInformation.BasicService = 5, + [74].u.DiversionInformation.ServedUserSubaddress.Type = 1, + [74].u.DiversionInformation.ServedUserSubaddress.Length = 4, + [74].u.DiversionInformation.ServedUserSubaddress.u.Nsap = "6492", + [74].u.DiversionInformation.CallingAddressPresent = 1, + [74].u.DiversionInformation.CallingAddress.Type = 0, + [74].u.DiversionInformation.CallingAddress.Address.ScreeningIndicator = 3, + [74].u.DiversionInformation.CallingAddress.Address.Party.Type = 4, + [74].u.DiversionInformation.CallingAddress.Address.Party.LengthOfNumber = 4, + [74].u.DiversionInformation.CallingAddress.Address.Party.Number = "1803", + [74].u.DiversionInformation.OriginalCalledPresent = 1, + [74].u.DiversionInformation.OriginalCalled.Type = 1, + [74].u.DiversionInformation.LastDivertingPresent = 1, + [74].u.DiversionInformation.LastDiverting.Type = 2, + [74].u.DiversionInformation.LastDivertingReasonPresent = 1, + [74].u.DiversionInformation.LastDivertingReason = 3, + [74].u.DiversionInformation.UserInfo.Length = 5, + [74].u.DiversionInformation.UserInfo.Contents = "79828", + + [75].Function = Fac_DiversionInformation, + [75].u.DiversionInformation.InvokeID = 78, + [75].u.DiversionInformation.DiversionReason = 3, + [75].u.DiversionInformation.BasicService = 5, + [75].u.DiversionInformation.CallingAddressPresent = 1, + [75].u.DiversionInformation.CallingAddress.Type = 1, + [75].u.DiversionInformation.OriginalCalledPresent = 1, + [75].u.DiversionInformation.OriginalCalled.Type = 2, + [75].u.DiversionInformation.LastDivertingPresent = 1, + [75].u.DiversionInformation.LastDiverting.Type = 1, + + [76].Function = Fac_DiversionInformation, + [76].u.DiversionInformation.InvokeID = 79, + [76].u.DiversionInformation.DiversionReason = 2, + [76].u.DiversionInformation.BasicService = 3, + [76].u.DiversionInformation.CallingAddressPresent = 1, + [76].u.DiversionInformation.CallingAddress.Type = 2, + + [77].Function = Fac_DiversionInformation, + [77].u.DiversionInformation.InvokeID = 80, + [77].u.DiversionInformation.DiversionReason = 3, + [77].u.DiversionInformation.BasicService = 5, + [77].u.DiversionInformation.CallingAddressPresent = 1, + [77].u.DiversionInformation.CallingAddress.Type = 3, + [77].u.DiversionInformation.CallingAddress.Address.ScreeningIndicator = 2, + [77].u.DiversionInformation.CallingAddress.Address.Party.Type = 4, + [77].u.DiversionInformation.CallingAddress.Address.Party.LengthOfNumber = 4, + [77].u.DiversionInformation.CallingAddress.Address.Party.Number = "1803", + + [78].Function = Fac_DiversionInformation, + [78].u.DiversionInformation.InvokeID = 81, + [78].u.DiversionInformation.DiversionReason = 2, + [78].u.DiversionInformation.BasicService = 4, + [78].u.DiversionInformation.UserInfo.Length = 5, + [78].u.DiversionInformation.UserInfo.Contents = "79828", + + [79].Function = Fac_DiversionInformation, + [79].u.DiversionInformation.InvokeID = 82, + [79].u.DiversionInformation.DiversionReason = 2, + [79].u.DiversionInformation.BasicService = 4, + + [80].Function = Fac_CallDeflection, + [80].u.CallDeflection.InvokeID = 83, + [80].u.CallDeflection.ComponentType = FacComponent_Invoke, + [80].u.CallDeflection.Component.Invoke.Deflection.Party.Type = 4, + [80].u.CallDeflection.Component.Invoke.Deflection.Party.LengthOfNumber = 4, + [80].u.CallDeflection.Component.Invoke.Deflection.Party.Number = "1803", + [80].u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUserPresent = 1, + [80].u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUser = 1, + + [81].Function = Fac_CallDeflection, + [81].u.CallDeflection.InvokeID = 84, + [81].u.CallDeflection.ComponentType = FacComponent_Invoke, + [81].u.CallDeflection.Component.Invoke.Deflection.Party.Type = 4, + [81].u.CallDeflection.Component.Invoke.Deflection.Party.LengthOfNumber = 4, + [81].u.CallDeflection.Component.Invoke.Deflection.Party.Number = "1803", + [81].u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUserPresent = 1, + [81].u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUser = 0, + + [82].Function = Fac_CallDeflection, + [82].u.CallDeflection.InvokeID = 85, + [82].u.CallDeflection.ComponentType = FacComponent_Invoke, + [82].u.CallDeflection.Component.Invoke.Deflection.Party.Type = 4, + [82].u.CallDeflection.Component.Invoke.Deflection.Party.LengthOfNumber = 4, + [82].u.CallDeflection.Component.Invoke.Deflection.Party.Number = "1803", + + [83].Function = Fac_CallDeflection, + [83].u.CallDeflection.InvokeID = 86, + [83].u.CallDeflection.ComponentType = FacComponent_Result, + + [84].Function = Fac_CallRerouteing, + [84].u.CallRerouteing.InvokeID = 87, + [84].u.CallRerouteing.ComponentType = FacComponent_Invoke, + [84].u.CallRerouteing.Component.Invoke.ReroutingReason = 3, + [84].u.CallRerouteing.Component.Invoke.ReroutingCounter = 2, + [84].u.CallRerouteing.Component.Invoke.CalledAddress.Party.Type = 4, + [84].u.CallRerouteing.Component.Invoke.CalledAddress.Party.LengthOfNumber = 4, + [84].u.CallRerouteing.Component.Invoke.CalledAddress.Party.Number = "1803", + [84].u.CallRerouteing.Component.Invoke.Q931ie.Bc.Length = 2, + [84].u.CallRerouteing.Component.Invoke.Q931ie.Bc.Contents = "RT", + [84].u.CallRerouteing.Component.Invoke.Q931ie.Hlc.Length = 3, + [84].u.CallRerouteing.Component.Invoke.Q931ie.Hlc.Contents = "RTG", + [84].u.CallRerouteing.Component.Invoke.Q931ie.Llc.Length = 2, + [84].u.CallRerouteing.Component.Invoke.Q931ie.Llc.Contents = "MY", + [84].u.CallRerouteing.Component.Invoke.Q931ie.UserInfo.Length = 5, + [84].u.CallRerouteing.Component.Invoke.Q931ie.UserInfo.Contents = "YEHAW", + [84].u.CallRerouteing.Component.Invoke.LastRerouting.Type = 1, + [84].u.CallRerouteing.Component.Invoke.SubscriptionOption = 2, + [84].u.CallRerouteing.Component.Invoke.CallingPartySubaddress.Type = 1, + [84].u.CallRerouteing.Component.Invoke.CallingPartySubaddress.Length = 4, + [84].u.CallRerouteing.Component.Invoke.CallingPartySubaddress.u.Nsap = "6492", + + [85].Function = Fac_CallRerouteing, + [85].u.CallRerouteing.InvokeID = 88, + [85].u.CallRerouteing.ComponentType = FacComponent_Invoke, + [85].u.CallRerouteing.Component.Invoke.ReroutingReason = 3, + [85].u.CallRerouteing.Component.Invoke.ReroutingCounter = 2, + [85].u.CallRerouteing.Component.Invoke.CalledAddress.Party.Type = 4, + [85].u.CallRerouteing.Component.Invoke.CalledAddress.Party.LengthOfNumber = 4, + [85].u.CallRerouteing.Component.Invoke.CalledAddress.Party.Number = "1803", + [85].u.CallRerouteing.Component.Invoke.Q931ie.Bc.Length = 2, + [85].u.CallRerouteing.Component.Invoke.Q931ie.Bc.Contents = "RT", + [85].u.CallRerouteing.Component.Invoke.LastRerouting.Type = 1, + [85].u.CallRerouteing.Component.Invoke.SubscriptionOption = 2, + + [86].Function = Fac_CallRerouteing, + [86].u.CallRerouteing.InvokeID = 89, + [86].u.CallRerouteing.ComponentType = FacComponent_Invoke, + [86].u.CallRerouteing.Component.Invoke.ReroutingReason = 3, + [86].u.CallRerouteing.Component.Invoke.ReroutingCounter = 2, + [86].u.CallRerouteing.Component.Invoke.CalledAddress.Party.Type = 4, + [86].u.CallRerouteing.Component.Invoke.CalledAddress.Party.LengthOfNumber = 4, + [86].u.CallRerouteing.Component.Invoke.CalledAddress.Party.Number = "1803", + [86].u.CallRerouteing.Component.Invoke.Q931ie.Bc.Length = 2, + [86].u.CallRerouteing.Component.Invoke.Q931ie.Bc.Contents = "RT", + [86].u.CallRerouteing.Component.Invoke.LastRerouting.Type = 2, + + [87].Function = Fac_CallRerouteing, + [87].u.CallRerouteing.InvokeID = 90, + [87].u.CallRerouteing.ComponentType = FacComponent_Result, + + [88].Function = Fac_InterrogateServedUserNumbers, + [88].u.InterrogateServedUserNumbers.InvokeID = 91, + [88].u.InterrogateServedUserNumbers.ComponentType = FacComponent_Invoke, + + [89].Function = Fac_InterrogateServedUserNumbers, + [89].u.InterrogateServedUserNumbers.InvokeID = 92, + [89].u.InterrogateServedUserNumbers.ComponentType = FacComponent_Result, + [89].u.InterrogateServedUserNumbers.Component.Result.NumRecords = 2, + [89].u.InterrogateServedUserNumbers.Component.Result.List[0].Type = 4, + [89].u.InterrogateServedUserNumbers.Component.Result.List[0].LengthOfNumber = 4, + [89].u.InterrogateServedUserNumbers.Component.Result.List[0].Number = "1803", + [89].u.InterrogateServedUserNumbers.Component.Result.List[1].Type = 4, + [89].u.InterrogateServedUserNumbers.Component.Result.List[1].LengthOfNumber = 4, + [89].u.InterrogateServedUserNumbers.Component.Result.List[1].Number = "5786", + + [90].Function = Fac_DivertingLegInformation1, + [90].u.DivertingLegInformation1.InvokeID = 93, + [90].u.DivertingLegInformation1.DiversionReason = 4, + [90].u.DivertingLegInformation1.SubscriptionOption = 1, + [90].u.DivertingLegInformation1.DivertedToPresent = 1, + [90].u.DivertingLegInformation1.DivertedTo.Type = 2, + + [91].Function = Fac_DivertingLegInformation1, + [91].u.DivertingLegInformation1.InvokeID = 94, + [91].u.DivertingLegInformation1.DiversionReason = 4, + [91].u.DivertingLegInformation1.SubscriptionOption = 1, + + [92].Function = Fac_DivertingLegInformation2, + [92].u.DivertingLegInformation2.InvokeID = 95, + [92].u.DivertingLegInformation2.DiversionCounter = 3, + [92].u.DivertingLegInformation2.DiversionReason = 2, + [92].u.DivertingLegInformation2.DivertingPresent = 1, + [92].u.DivertingLegInformation2.Diverting.Type = 2, + [92].u.DivertingLegInformation2.OriginalCalledPresent = 1, + [92].u.DivertingLegInformation2.OriginalCalled.Type = 1, + + [93].Function = Fac_DivertingLegInformation2, + [93].u.DivertingLegInformation2.InvokeID = 96, + [93].u.DivertingLegInformation2.DiversionCounter = 3, + [93].u.DivertingLegInformation2.DiversionReason = 2, + [93].u.DivertingLegInformation2.OriginalCalledPresent = 1, + [93].u.DivertingLegInformation2.OriginalCalled.Type = 1, + + [94].Function = Fac_DivertingLegInformation2, + [94].u.DivertingLegInformation2.InvokeID = 97, + [94].u.DivertingLegInformation2.DiversionCounter = 1, + [94].u.DivertingLegInformation2.DiversionReason = 2, + + [95].Function = Fac_DivertingLegInformation3, + [95].u.DivertingLegInformation3.InvokeID = 98, + [95].u.DivertingLegInformation3.PresentationAllowedIndicator = 1, +/* *INDENT-ON* */ +}; +#endif /* defined(AST_MISDN_ENHANCEMENTS) && defined(CCBS_TEST_MESSAGES) */ + static char *handle_cli_misdn_send_facility(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - char *channame; - char *nr; + const char *channame; + const char *nr; struct chan_list *tmp; - int port; - char *served_nr; + int port; + const char *served_nr; struct misdn_bchannel dummy, *bc=&dummy; - + unsigned max_len; + switch (cmd) { case CLI_INIT: e->command = "misdn send facility"; @@ -1673,7 +5148,7 @@ if (a->argc < 5) { return CLI_SHOWUSAGE; } - + if (strstr(a->argv[3], "calldeflect")) { if (a->argc < 6) { ast_verbose("calldeflect requires 1 arg: ToNumber\n\n"); @@ -1686,15 +5161,42 @@ tmp = get_chan_by_ast_name(channame); if (!tmp) { ast_verbose("Sending CD with nr %s to %s failed: Channel does not exist.\n", nr, channame); - return 0; + return 0; } - if (strlen(nr) >= 15) { - ast_verbose("Sending CD with nr %s to %s failed: Number too long (up to 15 digits are allowed).\n", nr, channame); - return 0; +#if defined(AST_MISDN_ENHANCEMENTS) + max_len = sizeof(tmp->bc->fac_out.u.CallDeflection.Component.Invoke.Deflection.Party.Number) - 1; + if (max_len < strlen(nr)) { + ast_verbose("Sending CD with nr %s to %s failed: Number too long (up to %u digits are allowed).\n", + nr, channame, max_len); + return 0; } + tmp->bc->fac_out.Function = Fac_CallDeflection; + tmp->bc->fac_out.u.CallDeflection.InvokeID = ++misdn_invoke_id; + tmp->bc->fac_out.u.CallDeflection.ComponentType = FacComponent_Invoke; + tmp->bc->fac_out.u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUserPresent = 1; + tmp->bc->fac_out.u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUser = 0; + tmp->bc->fac_out.u.CallDeflection.Component.Invoke.Deflection.Party.Type = 0;/* unknown */ + tmp->bc->fac_out.u.CallDeflection.Component.Invoke.Deflection.Party.LengthOfNumber = strlen(nr); + strcpy((char *) tmp->bc->fac_out.u.CallDeflection.Component.Invoke.Deflection.Party.Number, nr); + tmp->bc->fac_out.u.CallDeflection.Component.Invoke.Deflection.Subaddress.Length = 0; + +#else /* !defined(AST_MISDN_ENHANCEMENTS) */ + + max_len = sizeof(tmp->bc->fac_out.u.CDeflection.DeflectedToNumber) - 1; + if (max_len < strlen(nr)) { + ast_verbose("Sending CD with nr %s to %s failed: Number too long (up to %u digits are allowed).\n", + nr, channame, max_len); + return 0; + } tmp->bc->fac_out.Function = Fac_CD; - ast_copy_string((char *)tmp->bc->fac_out.u.CDeflection.DeflectedToNumber, nr, sizeof(tmp->bc->fac_out.u.CDeflection.DeflectedToNumber)); + tmp->bc->fac_out.u.CDeflection.PresentationAllowed = 0; + //tmp->bc->fac_out.u.CDeflection.DeflectedToSubaddress[0] = 0; + strcpy((char *) tmp->bc->fac_out.u.CDeflection.DeflectedToNumber, nr); +#endif /* !defined(AST_MISDN_ENHANCEMENTS) */ + + /* Send message */ + print_facility(&tmp->bc->fac_out, tmp->bc); misdn_lib_send_event(tmp->bc, EVENT_FACILITY); } else if (strstr(a->argv[3], "CFActivate")) { if (a->argc < 7) { @@ -1709,31 +5211,135 @@ ast_verbose("Sending CFActivate Port:(%d) FromNr. (%s) to Nr. (%s)\n", port, served_nr, nr); +#if defined(AST_MISDN_ENHANCEMENTS) + bc->fac_out.Function = Fac_ActivationDiversion; + bc->fac_out.u.ActivationDiversion.InvokeID = ++misdn_invoke_id; + bc->fac_out.u.ActivationDiversion.ComponentType = FacComponent_Invoke; + bc->fac_out.u.ActivationDiversion.Component.Invoke.BasicService = 0;/* allServices */ + bc->fac_out.u.ActivationDiversion.Component.Invoke.Procedure = 0;/* cfu (Call Forward Unconditional) */ + ast_copy_string((char *) bc->fac_out.u.ActivationDiversion.Component.Invoke.ServedUser.Number, + served_nr, sizeof(bc->fac_out.u.ActivationDiversion.Component.Invoke.ServedUser.Number)); + bc->fac_out.u.ActivationDiversion.Component.Invoke.ServedUser.LengthOfNumber = + strlen((char *) bc->fac_out.u.ActivationDiversion.Component.Invoke.ServedUser.Number); + bc->fac_out.u.ActivationDiversion.Component.Invoke.ServedUser.Type = 0;/* unknown */ + ast_copy_string((char *) bc->fac_out.u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.Number, + nr, sizeof(bc->fac_out.u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.Number)); + bc->fac_out.u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.LengthOfNumber = + strlen((char *) bc->fac_out.u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.Number); + bc->fac_out.u.ActivationDiversion.Component.Invoke.ForwardedTo.Party.Type = 0;/* unknown */ + bc->fac_out.u.ActivationDiversion.Component.Invoke.ForwardedTo.Subaddress.Length = 0; + +#else /* !defined(AST_MISDN_ENHANCEMENTS) */ + bc->fac_out.Function = Fac_CFActivate; bc->fac_out.u.CFActivate.BasicService = 0; /* All Services */ bc->fac_out.u.CFActivate.Procedure = 0; /* Unconditional */ - ast_copy_string((char *)bc->fac_out.u.CFActivate.ServedUserNumber, served_nr, sizeof(bc->fac_out.u.CFActivate.ServedUserNumber)); - ast_copy_string((char *)bc->fac_out.u.CFActivate.ForwardedToNumber, nr, sizeof(bc->fac_out.u.CFActivate.ForwardedToNumber)); + ast_copy_string((char *) bc->fac_out.u.CFActivate.ServedUserNumber, served_nr, sizeof(bc->fac_out.u.CFActivate.ServedUserNumber)); + ast_copy_string((char *) bc->fac_out.u.CFActivate.ForwardedToNumber, nr, sizeof(bc->fac_out.u.CFActivate.ForwardedToNumber)); +#endif /* !defined(AST_MISDN_ENHANCEMENTS) */ + /* Send message */ + print_facility(&bc->fac_out, bc); misdn_lib_send_event(bc, EVENT_FACILITY); } else if (strstr(a->argv[3], "CFDeactivate")) { - if (a->argc < 6) { - ast_verbose("CFActivate requires 1 arg: FromNumber\n\n"); + ast_verbose("CFDeactivate requires 1 arg: FromNumber\n\n"); return 0; } port = atoi(a->argv[4]); served_nr = a->argv[5]; - + misdn_make_dummy(bc, port, 0, misdn_lib_port_is_nt(port), 0); ast_verbose("Sending CFDeactivate Port:(%d) FromNr. (%s)\n", port, served_nr); +#if defined(AST_MISDN_ENHANCEMENTS) + bc->fac_out.Function = Fac_DeactivationDiversion; + bc->fac_out.u.DeactivationDiversion.InvokeID = ++misdn_invoke_id; + bc->fac_out.u.DeactivationDiversion.ComponentType = FacComponent_Invoke; + bc->fac_out.u.DeactivationDiversion.Component.Invoke.BasicService = 0;/* allServices */ + bc->fac_out.u.DeactivationDiversion.Component.Invoke.Procedure = 0;/* cfu (Call Forward Unconditional) */ + ast_copy_string((char *) bc->fac_out.u.DeactivationDiversion.Component.Invoke.ServedUser.Number, + served_nr, sizeof(bc->fac_out.u.DeactivationDiversion.Component.Invoke.ServedUser.Number)); + bc->fac_out.u.DeactivationDiversion.Component.Invoke.ServedUser.LengthOfNumber = + strlen((char *) bc->fac_out.u.DeactivationDiversion.Component.Invoke.ServedUser.Number); + bc->fac_out.u.DeactivationDiversion.Component.Invoke.ServedUser.Type = 0;/* unknown */ + +#else /* !defined(AST_MISDN_ENHANCEMENTS) */ + bc->fac_out.Function = Fac_CFDeactivate; - bc->fac_out.u.CFDeactivate.BasicService = 0; //All Services - bc->fac_out.u.CFDeactivate.Procedure = 0; //Unconditional - - ast_copy_string((char *)bc->fac_out.u.CFActivate.ServedUserNumber, served_nr, sizeof(bc->fac_out.u.CFActivate.ServedUserNumber)); + bc->fac_out.u.CFDeactivate.BasicService = 0; /* All Services */ + bc->fac_out.u.CFDeactivate.Procedure = 0; /* Unconditional */ + ast_copy_string((char *) bc->fac_out.u.CFActivate.ServedUserNumber, served_nr, sizeof(bc->fac_out.u.CFActivate.ServedUserNumber)); +#endif /* !defined(AST_MISDN_ENHANCEMENTS) */ + + /* Send message */ + print_facility(&bc->fac_out, bc); misdn_lib_send_event(bc, EVENT_FACILITY); +#if defined(AST_MISDN_ENHANCEMENTS) && defined(CCBS_TEST_MESSAGES) + } else if (strstr(a->argv[3], "test")) { + int msg_number; + + if (a->argc < 5) { + ast_verbose("test ( []) | ( )\n\n"); + return 0; + } + port = atoi(a->argv[4]); + + channame = argv[4]; + tmp = get_chan_by_ast_name(channame); + if (tmp) { + /* We are going to send this FACILITY message out on an existing connection */ + msg_number = atoi(argv[5]); + if (msg_number < ARRAY_LEN(Fac_Msgs)) { + tmp->bc->fac_out = Fac_Msgs[msg_number]; + + /* Send message */ + print_facility(&tmp->bc->fac_out, tmp->bc); + misdn_lib_send_event(tmp->bc, EVENT_FACILITY); + } else { + ast_verbose("test \n\n"); + } + } else if (a->argc < 6) { + for (msg_number = 0; msg_number < ARRAY_LEN(Fac_Msgs); ++msg_number) { + misdn_make_dummy(bc, port, 0, misdn_lib_port_is_nt(port), 0); + bc->fac_out = Fac_Msgs[msg_number]; + + /* Send message */ + print_facility(&bc->fac_out, bc); + misdn_lib_send_event(bc, EVENT_FACILITY); + sleep(1); + } + } else { + msg_number = atoi(a->argv[5]); + if (msg_number < ARRAY_LEN(Fac_Msgs)) { + misdn_make_dummy(bc, port, 0, misdn_lib_port_is_nt(port), 0); + bc->fac_out = Fac_Msgs[msg_number]; + + /* Send message */ + print_facility(&bc->fac_out, bc); + misdn_lib_send_event(bc, EVENT_FACILITY); + } else { + ast_verbose("test []\n\n"); + } + } + } else if (strstr(argv[3], "register")) { + if (argc < 5) { + ast_cli(fd, "register \n\n"); + return 0; + } + port = atoi(argv[4]); + + bc = misdn_lib_get_register_bc(port); + if (!bc) { + ast_cli(fd, "Could not allocate REGISTER bc struct\n\n"); + return 0; + } + bc->fac_out = Fac_Msgs[45]; + + /* Send message */ + print_facility(&bc->fac_out, bc); + misdn_lib_send_event(bc, EVENT_REGISTER); +#endif /* defined(AST_MISDN_ENHANCEMENTS) && defined(CCBS_TEST_MESSAGES) */ } return CLI_SUCCESS; @@ -1773,8 +5379,8 @@ static char *handle_cli_misdn_send_digit(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - char *channame; - char *msg; + const char *channame; + const char *msg; struct chan_list *tmp; int i, msglen; @@ -1803,7 +5409,7 @@ tmp = get_chan_by_ast_name(channame); if (!tmp) { ast_cli(a->fd, "Sending %s to %s failed Channel does not exist\n", msg, channame); - return CLI_SUCCESS; + return CLI_SUCCESS; } #if 1 for (i = 0; i < msglen; i++) { @@ -1822,7 +5428,7 @@ static char *handle_cli_misdn_toggle_echocancel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - char *channame; + const char *channame; struct chan_list *tmp; switch (cmd) { @@ -1841,9 +5447,9 @@ } channame = a->argv[3]; - + ast_cli(a->fd, "Toggling EchoCancel on %s\n", channame); - + tmp = get_chan_by_ast_name(channame); if (!tmp) { ast_cli(a->fd, "Toggling EchoCancel %s failed Channel does not exist\n", channame); @@ -1868,8 +5474,8 @@ static char *handle_cli_misdn_send_display(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - char *channame; - char *msg; + const char *channame; + const char *msg; struct chan_list *tmp; switch (cmd) { @@ -1893,7 +5499,7 @@ ast_cli(a->fd, "Sending %s to %s\n", msg, channame); tmp = get_chan_by_ast_name(channame); - + if (tmp && tmp->bc) { ast_copy_string(tmp->bc->display, msg, sizeof(tmp->bc->display)); misdn_lib_send_event(tmp->bc, EVENT_INFORMATION); @@ -1986,6 +5592,7 @@ } static struct ast_cli_entry chan_misdn_clis[] = { +/* *INDENT-OFF* */ AST_CLI_DEFINE(handle_cli_misdn_port_block, "Block the given port"), AST_CLI_DEFINE(handle_cli_misdn_port_down, "Try to deactivate the L1 on the given port"), AST_CLI_DEFINE(handle_cli_misdn_port_unblock, "Unblock the given port"), @@ -2007,26 +5614,29 @@ AST_CLI_DEFINE(handle_cli_misdn_set_debug, "Set Debuglevel of chan_misdn"), AST_CLI_DEFINE(handle_cli_misdn_set_tics, "???"), AST_CLI_DEFINE(handle_cli_misdn_toggle_echocancel, "Toggle EchoCancel on mISDN Channel"), +/* *INDENT-ON* */ }; /*! \brief Updates caller ID information from config */ -static int update_config(struct chan_list *ch, int orig) +static void update_config(struct chan_list *ch) { struct ast_channel *ast; struct misdn_bchannel *bc; - int port, hdlc = 0; - int pres, screen; + int port; + int hdlc = 0; + int pres; + int screen; if (!ch) { ast_log(LOG_WARNING, "Cannot configure without chanlist\n"); - return -1; + return; } ast = ch->ast; bc = ch->bc; if (! ast || ! bc) { ast_log(LOG_WARNING, "Cannot configure without ast || bc\n"); - return -1; + return; } port = bc->port; @@ -2034,7 +5644,6 @@ chan_misdn_log(7, port, "update_config: Getting Config\n"); misdn_cfg_get(port, MISDN_CFG_HDLC, &hdlc, sizeof(int)); - if (hdlc) { switch (bc->capability) { case INFO_CAPABILITY_DIGITAL_UNRESTRICTED: @@ -2049,63 +5658,33 @@ misdn_cfg_get(port, MISDN_CFG_PRES, &pres, sizeof(pres)); misdn_cfg_get(port, MISDN_CFG_SCREEN, &screen, sizeof(screen)); chan_misdn_log(2, port, " --> pres: %d screen: %d\n", pres, screen); - + if (pres < 0 || screen < 0) { - chan_misdn_log(2, port, " --> pres: %x\n", ast->cid.cid_pres); - - switch (ast->cid.cid_pres & 0x60) { - case AST_PRES_RESTRICTED: - bc->pres = 1; - chan_misdn_log(2, port, " --> PRES: Restricted (1)\n"); - break; - case AST_PRES_UNAVAILABLE: - bc->pres = 2; - chan_misdn_log(2, port, " --> PRES: Unavailable (2)\n"); - break; - default: - bc->pres = 0; - chan_misdn_log(2, port, " --> PRES: Allowed (0)\n"); - break; - } + chan_misdn_log(2, port, " --> pres: %x\n", ast->connected.id.number_presentation); - switch (ast->cid.cid_pres & 0x3) { - default: - case AST_PRES_USER_NUMBER_UNSCREENED: - bc->screen = 0; - chan_misdn_log(2, port, " --> SCREEN: Unscreened (0)\n"); - break; - case AST_PRES_USER_NUMBER_PASSED_SCREEN: - bc->screen = 1; - chan_misdn_log(2, port, " --> SCREEN: Passed Screen (1)\n"); - break; - case AST_PRES_USER_NUMBER_FAILED_SCREEN: - bc->screen = 2; - chan_misdn_log(2, port, " --> SCREEN: Failed Screen (2)\n"); - break; - case AST_PRES_NETWORK_NUMBER: - bc->screen = 3; - chan_misdn_log(2, port, " --> SCREEN: Network Nr. (3)\n"); - break; - } + bc->caller.presentation = ast_to_misdn_pres(ast->connected.id.number_presentation); + chan_misdn_log(2, port, " --> PRES: %s(%d)\n", misdn_to_str_pres(bc->caller.presentation), bc->caller.presentation); + + bc->caller.screening = ast_to_misdn_screen(ast->connected.id.number_presentation); + chan_misdn_log(2, port, " --> SCREEN: %s(%d)\n", misdn_to_str_screen(bc->caller.screening), bc->caller.screening); } else { - bc->screen = screen; - bc->pres = pres; + bc->caller.screening = screen; + bc->caller.presentation = pres; } - - return 0; } static void config_jitterbuffer(struct chan_list *ch) { struct misdn_bchannel *bc = ch->bc; - int len = ch->jb_len, threshold = ch->jb_upper_threshold; - + int len = ch->jb_len; + int threshold = ch->jb_upper_threshold; + chan_misdn_log(5, bc->port, "config_jb: Called\n"); - - if (! len) { + + if (!len) { chan_misdn_log(1, bc->port, "config_jb: Deactivating Jitterbuffer\n"); - bc->nojitter=1; + bc->nojitter = 1; } else { if (len <= 100 || len > 8000) { chan_misdn_log(0, bc->port, "config_jb: Jitterbuffer out of Bounds, setting to 1000\n"); @@ -2116,7 +5695,7 @@ chan_misdn_log(0, bc->port, "config_jb: Jitterbuffer Threshold > Jitterbuffer setting to Jitterbuffer -1\n"); } - if ( ch->jb) { + if (ch->jb) { cb_log(0, bc->port, "config_jb: We've got a Jitterbuffer Already on this port.\n"); misdn_jb_destroy(ch->jb); ch->jb = NULL; @@ -2131,20 +5710,26 @@ } -void debug_numplan(int port, int numplan, char *type) +void debug_numtype(int port, int numtype, char *type) { - switch (numplan) { - case NUMPLAN_INTERNATIONAL: + switch (numtype) { + case NUMTYPE_UNKNOWN: + chan_misdn_log(2, port, " --> %s: Unknown\n", type); + break; + case NUMTYPE_INTERNATIONAL: chan_misdn_log(2, port, " --> %s: International\n", type); break; - case NUMPLAN_NATIONAL: + case NUMTYPE_NATIONAL: chan_misdn_log(2, port, " --> %s: National\n", type); break; - case NUMPLAN_SUBSCRIBER: + case NUMTYPE_NETWORK_SPECIFIC: + chan_misdn_log(2, port, " --> %s: Network Specific\n", type); + break; + case NUMTYPE_SUBSCRIBER: chan_misdn_log(2, port, " --> %s: Subscriber\n", type); break; - case NUMPLAN_UNKNOWN: - chan_misdn_log(2, port, " --> %s: Unknown\n", type); + case NUMTYPE_ABBREVIATED: + chan_misdn_log(2, port, " --> %s: Abbreviated\n", type); break; /* Maybe we should cut off the prefix if present ? */ default: @@ -2194,7 +5779,7 @@ #endif -static int read_config(struct chan_list *ch, int orig) +static int read_config(struct chan_list *ch) { struct ast_channel *ast; struct misdn_bchannel *bc; @@ -2218,7 +5803,7 @@ ast_log(LOG_WARNING, "Cannot configure without ast || bc\n"); return -1; } - + port = bc->port; chan_misdn_log(1, port, "read_config: Getting Config\n"); @@ -2233,9 +5818,8 @@ misdn_cfg_get(port, MISDN_CFG_INCOMING_EARLY_AUDIO, &ch->incoming_early_audio, sizeof(ch->incoming_early_audio)); misdn_cfg_get(port, MISDN_CFG_SENDDTMF, &bc->send_dtmf, sizeof(bc->send_dtmf)); - + misdn_cfg_get(port, MISDN_CFG_ASTDTMF, &ch->ast_dsp, sizeof(int)); - if (ch->ast_dsp) { ch->ignore_dtmf = 1; } @@ -2252,7 +5836,6 @@ misdn_cfg_get(port, MISDN_CFG_FAXDETECT, faxdetect, sizeof(faxdetect)); misdn_cfg_get(port, MISDN_CFG_HDLC, &hdlc, sizeof(hdlc)); - if (hdlc) { switch (bc->capability) { case INFO_CAPABILITY_DIGITAL_UNRESTRICTED: @@ -2261,7 +5844,7 @@ bc->hdlc = 1; break; } - + } /*Initialize new Jitterbuffer*/ misdn_cfg_get(port, MISDN_CFG_JITTERBUFFER, &ch->jb_len, sizeof(ch->jb_len)); @@ -2281,14 +5864,17 @@ misdn_cfg_get(bc->port, MISDN_CFG_EARLY_BCONNECT, &bc->early_bconnect, sizeof(bc->early_bconnect)); + misdn_cfg_get(port, MISDN_CFG_DISPLAY_CONNECTED, &bc->display_connected, sizeof(bc->display_connected)); + misdn_cfg_get(port, MISDN_CFG_DISPLAY_SETUP, &bc->display_setup, sizeof(bc->display_setup)); + misdn_cfg_get(port, MISDN_CFG_OUTGOING_COLP, &bc->outgoing_colp, sizeof(bc->outgoing_colp)); + misdn_cfg_get(port, MISDN_CFG_PICKUPGROUP, &pg, sizeof(pg)); misdn_cfg_get(port, MISDN_CFG_CALLGROUP, &cg, sizeof(cg)); - chan_misdn_log(5, port, " --> * CallGrp:%s PickupGrp:%s\n", ast_print_group(buf, sizeof(buf), cg), ast_print_group(buf2, sizeof(buf2), pg)); ast->pickupgroup = pg; ast->callgroup = cg; - - if (orig == ORG_AST) { + + if (ch->originator == ORG_AST) { char callerid[BUFFERSIZE + 1]; /* ORIGINATOR Asterisk (outgoing call) */ @@ -2301,87 +5887,53 @@ misdn_cfg_get(port, MISDN_CFG_CALLERID, callerid, sizeof(callerid)); if (!ast_strlen_zero(callerid)) { - chan_misdn_log(1, port, " --> * Setting Cid to %s\n", callerid); - ast_copy_string(bc->oad, callerid, sizeof(bc->oad)); + char *cid_name = NULL; + char *cid_num = NULL; + + ast_callerid_parse(callerid, &cid_name, &cid_num); + if (cid_name) { + ast_copy_string(bc->caller.name, cid_name, sizeof(bc->caller.name)); + } else { + bc->caller.name[0] = '\0'; + } + if (cid_num) { + ast_copy_string(bc->caller.number, cid_num, sizeof(bc->caller.number)); + } else { + bc->caller.number[0] = '\0'; + } + chan_misdn_log(1, port, " --> * Setting caller to \"%s\" <%s>\n", bc->caller.name, bc->caller.number); } - misdn_cfg_get(port, MISDN_CFG_DIALPLAN, &bc->dnumplan, sizeof(bc->dnumplan)); - misdn_cfg_get(port, MISDN_CFG_LOCALDIALPLAN, &bc->onumplan, sizeof(bc->onumplan)); - misdn_cfg_get(port, MISDN_CFG_CPNDIALPLAN, &bc->cpnnumplan, sizeof(bc->cpnnumplan)); - debug_numplan(port, bc->dnumplan, "TON"); - debug_numplan(port, bc->onumplan, "LTON"); - debug_numplan(port, bc->cpnnumplan, "CTON"); + misdn_cfg_get(port, MISDN_CFG_DIALPLAN, &bc->dialed.number_type, sizeof(bc->dialed.number_type)); + bc->dialed.number_plan = NUMPLAN_ISDN; + debug_numtype(port, bc->dialed.number_type, "TON"); ch->overlap_dial = 0; } else { /* ORIGINATOR MISDN (incoming call) */ - char prefix[BUFFERSIZE + 1] = ""; if (strstr(faxdetect, "incoming") || strstr(faxdetect, "both")) { ch->faxdetect = (strstr(faxdetect, "nojump")) ? 2 : 1; } - misdn_cfg_get(port, MISDN_CFG_CPNDIALPLAN, &bc->cpnnumplan, sizeof(bc->cpnnumplan)); - debug_numplan(port, bc->cpnnumplan, "CTON"); + /* Add configured prefix to caller.number */ + misdn_add_number_prefix(bc->port, bc->caller.number_type, bc->caller.number, sizeof(bc->caller.number)); - switch (bc->onumplan) { - case NUMPLAN_INTERNATIONAL: - misdn_cfg_get(bc->port, MISDN_CFG_INTERNATPREFIX, prefix, sizeof(prefix)); - break; - - case NUMPLAN_NATIONAL: - misdn_cfg_get(bc->port, MISDN_CFG_NATPREFIX, prefix, sizeof(prefix)); - break; - default: - break; + if (ast_strlen_zero(bc->dialed.number) && !ast_strlen_zero(bc->keypad)) { + ast_copy_string(bc->dialed.number, bc->keypad, sizeof(bc->dialed.number)); } - ast_copy_string(buf, bc->oad, sizeof(buf)); - snprintf(bc->oad, sizeof(bc->oad), "%s%s", prefix, buf); + /* Add configured prefix to dialed.number */ + misdn_add_number_prefix(bc->port, bc->dialed.number_type, bc->dialed.number, sizeof(bc->dialed.number)); - if (!ast_strlen_zero(bc->dad)) { - ast_copy_string(bc->orig_dad, bc->dad, sizeof(bc->orig_dad)); - } + ast_copy_string(ast->exten, bc->dialed.number, sizeof(ast->exten)); - if (ast_strlen_zero(bc->dad) && !ast_strlen_zero(bc->keypad)) { - ast_copy_string(bc->dad, bc->keypad, sizeof(bc->dad)); - } - - prefix[0] = 0; - - switch (bc->dnumplan) { - case NUMPLAN_INTERNATIONAL: - misdn_cfg_get(bc->port, MISDN_CFG_INTERNATPREFIX, prefix, sizeof(prefix)); - break; - case NUMPLAN_NATIONAL: - misdn_cfg_get(bc->port, MISDN_CFG_NATPREFIX, prefix, sizeof(prefix)); - break; - default: - break; - } - - ast_copy_string(buf, bc->dad, sizeof(buf)); - snprintf(bc->dad, sizeof(bc->dad), "%s%s", prefix, buf); - - if (strcmp(bc->dad, ast->exten)) { - ast_copy_string(ast->exten, bc->dad, sizeof(ast->exten)); - } - - ast_set_callerid(ast, bc->oad, NULL, bc->oad); - - if ( !ast_strlen_zero(bc->rad) ) { - if (ast->cid.cid_rdnis) { - ast_free(ast->cid.cid_rdnis); - } - ast->cid.cid_rdnis = ast_strdup(bc->rad); - } - misdn_cfg_get(bc->port, MISDN_CFG_OVERLAP_DIAL, &ch->overlap_dial, sizeof(ch->overlap_dial)); ast_mutex_init(&ch->overlap_tv_lock); } /* ORIG MISDN END */ ch->overlap_dial_task = -1; - + if (ch->faxdetect || ch->ast_dsp) { misdn_cfg_get(port, MISDN_CFG_FAXDETECT_TIMEOUT, &ch->faxdetect_timeout, sizeof(ch->faxdetect_timeout)); if (!ch->dsp) { @@ -2401,7 +5953,289 @@ return 0; } +/*! + * \internal + * \brief Send a connected line update to the other channel + * + * \param ast Current Asterisk channel + * \param id Party id information to send to the other side + * \param source Why are we sending this update + * + * \return Nothing + */ +static void misdn_queue_connected_line_update(struct ast_channel *ast, const struct misdn_party_id *id, enum AST_CONNECTED_LINE_UPDATE_SOURCE source) +{ + struct ast_party_connected_line connected; + ast_party_connected_line_init(&connected); + connected.id.number = (char *) id->number; + connected.id.number_type = misdn_to_ast_ton(id->number_type) + | misdn_to_ast_plan(id->number_plan); + connected.id.number_presentation = misdn_to_ast_pres(id->presentation) + | misdn_to_ast_screen(id->screening); + connected.source = source; + ast_channel_queue_connected_line_update(ast, &connected); +} + +/*! + * \internal + * \brief Get the connected line information out of the Asterisk channel. + * + * \param ast Current Asterisk channel + * \param bc Associated B channel + * \param originator Who originally created this channel. ORG_AST or ORG_MISDN + * + * \return Nothing + */ +static void misdn_get_connected_line(struct ast_channel *ast, struct misdn_bchannel *bc, int originator) +{ + int number_type; + + if (originator == ORG_MISDN) { + /* ORIGINATOR MISDN (incoming call) */ + + ast_copy_string(bc->connected.name, S_OR(ast->connected.id.name, ""), sizeof(bc->connected.name)); + ast_copy_string(bc->connected.number, S_OR(ast->connected.id.number, ""), sizeof(bc->connected.number)); + bc->connected.presentation = ast_to_misdn_pres(ast->connected.id.number_presentation); + bc->connected.screening = ast_to_misdn_screen(ast->connected.id.number_presentation); + + misdn_cfg_get(bc->port, MISDN_CFG_CPNDIALPLAN, &number_type, sizeof(number_type)); + if (number_type < 0) { + bc->connected.number_type = ast_to_misdn_ton(ast->connected.id.number_type); + bc->connected.number_plan = ast_to_misdn_plan(ast->connected.id.number_type); + } else { + /* Force us to send in CONNECT message */ + bc->connected.number_type = number_type; + bc->connected.number_plan = NUMPLAN_ISDN; + } + debug_numtype(bc->port, bc->connected.number_type, "CTON"); + } else { + /* ORIGINATOR Asterisk (outgoing call) */ + + ast_copy_string(bc->caller.name, S_OR(ast->connected.id.name, ""), sizeof(bc->caller.name)); + ast_copy_string(bc->caller.number, S_OR(ast->connected.id.number, ""), sizeof(bc->caller.number)); + bc->caller.presentation = ast_to_misdn_pres(ast->connected.id.number_presentation); + bc->caller.screening = ast_to_misdn_screen(ast->connected.id.number_presentation); + + misdn_cfg_get(bc->port, MISDN_CFG_LOCALDIALPLAN, &number_type, sizeof(number_type)); + if (number_type < 0) { + bc->caller.number_type = ast_to_misdn_ton(ast->connected.id.number_type); + bc->caller.number_plan = ast_to_misdn_plan(ast->connected.id.number_type); + } else { + /* Force us to send in SETUP message */ + bc->caller.number_type = number_type; + bc->caller.number_plan = NUMPLAN_ISDN; + } + debug_numtype(bc->port, bc->caller.number_type, "LTON"); + } +} + +/*! + * \internal + * \brief Notify peer that the connected line has changed. + * + * \param ast Current Asterisk channel + * \param bc Associated B channel + * \param originator Who originally created this channel. ORG_AST or ORG_MISDN + * + * \return Nothing + */ +static void misdn_update_connected_line(struct ast_channel *ast, struct misdn_bchannel *bc, int originator) +{ + struct chan_list *ch; + + misdn_get_connected_line(ast, bc, originator); + if (originator == ORG_MISDN) { + bc->redirecting.to = bc->connected; + } else { + bc->redirecting.to = bc->caller; + } + switch (bc->outgoing_colp) { + case 1:/* restricted */ + case 2:/* blocked */ + bc->redirecting.to.presentation = 1;/* restricted */ + break; + default: + break; + } + + ch = MISDN_ASTERISK_TECH_PVT(ast); + if (ch->state == MISDN_CONNECTED + || originator != ORG_MISDN) { + int is_ptmp; + + is_ptmp = !misdn_lib_is_ptp(bc->port); + if (is_ptmp) { + /* Send NOTIFY(transfer-active, redirecting.to data) */ + bc->redirecting.to_changed = 1; + bc->notify_description_code = mISDN_NOTIFY_CODE_CALL_TRANSFER_ACTIVE; + misdn_lib_send_event(bc, EVENT_NOTIFY); +#if defined(AST_MISDN_ENHANCEMENTS) + } else { + /* Send EctInform(transfer-active, redirecting.to data) */ + bc->fac_out.Function = Fac_EctInform; + bc->fac_out.u.EctInform.InvokeID = ++misdn_invoke_id; + bc->fac_out.u.EctInform.Status = 1;/* active */ + bc->fac_out.u.EctInform.RedirectionPresent = 1;/* Must be present when status is active */ + misdn_PresentedNumberUnscreened_fill(&bc->fac_out.u.EctInform.Redirection, + &bc->redirecting.to); + switch (bc->outgoing_colp) { + case 2:/* blocked */ + /* Block the number going out */ + bc->fac_out.u.EctInform.Redirection.Type = 1;/* presentationRestricted */ + break; + default: + break; + } + + /* Send message */ + print_facility(&bc->fac_out, bc); + misdn_lib_send_event(bc, EVENT_FACILITY); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + } + } +} + +/*! + * \internal + * \brief Copy the redirecting information out of the Asterisk channel + * + * \param bc Associated B channel + * \param ast Current Asterisk channel + * + * \return Nothing + */ +static void misdn_copy_redirecting_from_ast(struct misdn_bchannel *bc, struct ast_channel *ast) +{ + ast_copy_string(bc->redirecting.from.name, S_OR(ast->redirecting.from.name, ""), sizeof(bc->redirecting.from.name)); + ast_copy_string(bc->redirecting.from.number, S_OR(ast->cid.cid_rdnis, ""), sizeof(bc->redirecting.from.number)); + bc->redirecting.from.presentation = ast_to_misdn_pres(ast->redirecting.from.number_presentation); + bc->redirecting.from.screening = ast_to_misdn_screen(ast->redirecting.from.number_presentation); + bc->redirecting.from.number_type = ast_to_misdn_ton(ast->redirecting.from.number_type); + bc->redirecting.from.number_plan = ast_to_misdn_plan(ast->redirecting.from.number_type); + + ast_copy_string(bc->redirecting.to.name, S_OR(ast->redirecting.to.name, ""), sizeof(bc->redirecting.to.name)); + ast_copy_string(bc->redirecting.to.number, S_OR(ast->redirecting.to.number, ""), sizeof(bc->redirecting.to.number)); + bc->redirecting.to.presentation = ast_to_misdn_pres(ast->redirecting.to.number_presentation); + bc->redirecting.to.screening = ast_to_misdn_screen(ast->redirecting.to.number_presentation); + bc->redirecting.to.number_type = ast_to_misdn_ton(ast->redirecting.to.number_type); + bc->redirecting.to.number_plan = ast_to_misdn_plan(ast->redirecting.to.number_type); + + bc->redirecting.reason = ast_to_misdn_reason(ast->redirecting.reason); + bc->redirecting.count = ast->redirecting.count; +} + +/*! + * \internal + * \brief Copy the redirecting info into the Asterisk channel + * + * \param ast Current Asterisk channel + * \param redirect Associated B channel redirecting info + * + * \return Nothing + */ +static void misdn_copy_redirecting_to_ast(struct ast_channel *ast, const struct misdn_party_redirecting *redirect) +{ + struct ast_party_redirecting redirecting; + + ast_party_redirecting_set_init(&redirecting, &ast->redirecting); + + redirecting.from.number = (char *) redirect->from.number; + redirecting.from.number_type = + misdn_to_ast_ton(redirect->from.number_type) + | misdn_to_ast_plan(redirect->from.number_plan); + redirecting.from.number_presentation = + misdn_to_ast_pres(redirect->from.presentation) + | misdn_to_ast_screen(redirect->from.screening); + + redirecting.to.number = (char *) redirect->to.number; + redirecting.to.number_type = + misdn_to_ast_ton(redirect->to.number_type) + | misdn_to_ast_plan(redirect->to.number_plan); + redirecting.to.number_presentation = + misdn_to_ast_pres(redirect->to.presentation) + | misdn_to_ast_screen(redirect->to.screening); + + redirecting.reason = misdn_to_ast_reason(redirect->reason); + redirecting.count = redirect->count; + + ast_channel_set_redirecting(ast, &redirecting); +} + +/*! + * \internal + * \brief Notify peer that the redirecting information has changed. + * + * \param ast Current Asterisk channel + * \param bc Associated B channel + * \param originator Who originally created this channel. ORG_AST or ORG_MISDN + * + * \return Nothing + */ +static void misdn_update_redirecting(struct ast_channel *ast, struct misdn_bchannel *bc, int originator) +{ + int is_ptmp; + + misdn_copy_redirecting_from_ast(bc, ast); + switch (bc->outgoing_colp) { + case 1:/* restricted */ + case 2:/* blocked */ + bc->redirecting.to.presentation = 1;/* restricted */ + break; + default: + break; + } + + if (originator != ORG_MISDN) { + return; + } + + is_ptmp = !misdn_lib_is_ptp(bc->port); + if (is_ptmp) { + /* Send NOTIFY(call-is-diverting, redirecting.to data) */ + bc->redirecting.to_changed = 1; + bc->notify_description_code = mISDN_NOTIFY_CODE_CALL_IS_DIVERTING; + misdn_lib_send_event(bc, EVENT_NOTIFY); +#if defined(AST_MISDN_ENHANCEMENTS) + } else { + int match; /* TRUE if the dialed number matches the redirecting to number */ + + match = (strcmp(ast->exten, bc->redirecting.to.number) == 0) ? 1 : 0; + if (!bc->div_leg_3_tx_pending + || !match) { + /* Send DivertingLegInformation1 */ + bc->fac_out.Function = Fac_DivertingLegInformation1; + bc->fac_out.u.DivertingLegInformation1.InvokeID = ++misdn_invoke_id; + bc->fac_out.u.DivertingLegInformation1.DiversionReason = + misdn_to_diversion_reason(bc->redirecting.reason); + bc->fac_out.u.DivertingLegInformation1.SubscriptionOption = 2;/* notificationWithDivertedToNr */ + bc->fac_out.u.DivertingLegInformation1.DivertedToPresent = 1; + misdn_PresentedNumberUnscreened_fill(&bc->fac_out.u.DivertingLegInformation1.DivertedTo, &bc->redirecting.to); + switch (bc->outgoing_colp) { + case 2:/* blocked */ + /* Block the number going out */ + bc->fac_out.u.DivertingLegInformation1.DivertedTo.Type = 1;/* presentationRestricted */ + break; + default: + break; + } + print_facility(&bc->fac_out, bc); + misdn_lib_send_event(bc, EVENT_FACILITY); + } + bc->div_leg_3_tx_pending = 0; + + /* Send DivertingLegInformation3 */ + bc->fac_out.Function = Fac_DivertingLegInformation3; + bc->fac_out.u.DivertingLegInformation3.InvokeID = ++misdn_invoke_id; + bc->fac_out.u.DivertingLegInformation3.PresentationAllowedIndicator = + bc->redirecting.to.presentation == 0 ? 1 : 0; + print_facility(&bc->fac_out, bc); + misdn_lib_send_event(bc, EVENT_FACILITY); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + } +} + + /*****************************/ /*** AST Indications Start ***/ /*****************************/ @@ -2412,22 +6246,17 @@ int r; int exceed; int bridging; - struct chan_list *ch = MISDN_ASTERISK_TECH_PVT(ast); + int number_type; + struct chan_list *ch; struct misdn_bchannel *newbc; - char *dest_cp = ast_strdupa(dest); + char *dest_cp; + AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(type); - AST_APP_ARG(ext); - AST_APP_ARG(opts); + AST_APP_ARG(intf); /* The interface token is discarded. */ + AST_APP_ARG(ext); /* extension token */ + AST_APP_ARG(opts); /* options token */ ); - AST_NONSTANDARD_APP_ARGS(args, dest_cp, '/'); - - if (ast_strlen_zero(args.ext)) { - chan_misdn_log(0, 0, "misdn_call: No Extension given!\n"); - return -1; - } - if (!ast) { ast_log(LOG_WARNING, " --> ! misdn_call called on ast_channel *ast where ast == NULL\n"); return -1; @@ -2440,87 +6269,212 @@ return -1; } + ch = MISDN_ASTERISK_TECH_PVT(ast); if (!ch) { - ast_log(LOG_WARNING, " --> ! misdn_call called on %s, neither down nor reserved (or dest==NULL)\n", ast->name); + ast_log(LOG_WARNING, " --> ! misdn_call called on %s, chan_list *ch==NULL\n", ast->name); ast->hangupcause = AST_CAUSE_NORMAL_TEMPORARY_FAILURE; ast_setstate(ast, AST_STATE_DOWN); return -1; } - + newbc = ch->bc; - if (!newbc) { - ast_log(LOG_WARNING, " --> ! misdn_call called on %s, neither down nor reserved (or dest==NULL)\n", ast->name); + ast_log(LOG_WARNING, " --> ! misdn_call called on %s, newbc==NULL\n", ast->name); ast->hangupcause = AST_CAUSE_NORMAL_TEMPORARY_FAILURE; ast_setstate(ast, AST_STATE_DOWN); return -1; } - + port = newbc->port; - if ((exceed = add_out_calls(port))) { - char tmp[16]; - snprintf(tmp, sizeof(tmp), "%d", exceed); - pbx_builtin_setvar_helper(ast, "MAX_OVERFLOW", tmp); - return -1; +#if defined(AST_MISDN_ENHANCEMENTS) + if ((ch->peer = misdn_cc_caller_get(ast))) { + chan_misdn_log(3, port, " --> Found CC caller data, peer:%s\n", + ch->peer->chan ? "available" : "NULL"); } - - chan_misdn_log(1, port, "* CALL: %s\n", dest); - - chan_misdn_log(2, port, " --> * dad:%s tech:%s ctx:%s\n", ast->exten, ast->name, ast->context); - - chan_misdn_log(3, port, " --> * adding2newbc ext %s\n", ast->exten); - if (ast->exten) { + + if (ch->record_id != -1) { + struct misdn_cc_record *cc_record; + + /* This is a call completion retry call */ + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_id(ch->record_id); + if (!cc_record) { + AST_LIST_UNLOCK(&misdn_cc_records_db); + ast_log(LOG_WARNING, " --> ! misdn_call called on %s, cc_record==NULL\n", ast->name); + ast->hangupcause = AST_CAUSE_NORMAL_TEMPORARY_FAILURE; + ast_setstate(ast, AST_STATE_DOWN); + return -1; + } + + /* Setup calling parameters to retry the call. */ + newbc->dialed = cc_record->redial.dialed; + newbc->caller = cc_record->redial.caller; + memset(&newbc->redirecting, 0, sizeof(newbc->redirecting)); + newbc->capability = cc_record->redial.capability; + newbc->hdlc = cc_record->redial.hdlc; + newbc->sending_complete = 1; + + if (cc_record->ptp) { + newbc->fac_out.Function = Fac_CCBS_T_Call; + newbc->fac_out.u.CCBS_T_Call.InvokeID = ++misdn_invoke_id; + } else { + newbc->fac_out.Function = Fac_CCBSCall; + newbc->fac_out.u.CCBSCall.InvokeID = ++misdn_invoke_id; + newbc->fac_out.u.CCBSCall.CCBSReference = cc_record->mode.ptmp.reference_id; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + + ast_copy_string(ast->exten, newbc->dialed.number, sizeof(ast->exten)); + + chan_misdn_log(1, port, "* Call completion to: %s\n", newbc->dialed.number); + chan_misdn_log(2, port, " --> * tech:%s context:%s\n", ast->name, ast->context); + } else +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + { + /* + * dest is ---v + * Dial(mISDN/g:group_name[/extension[/options]]) + * Dial(mISDN/port[:preselected_channel][/extension[/options]]) + * + * The dial extension could be empty if you are using MISDN_KEYPAD + * to control ISDN provider features. + */ + dest_cp = ast_strdupa(dest); + AST_NONSTANDARD_APP_ARGS(args, dest_cp, '/'); + if (!args.ext) { + args.ext = ""; + } + + chan_misdn_log(1, port, "* CALL: %s\n", dest); + chan_misdn_log(2, port, " --> * dialed:%s tech:%s context:%s\n", args.ext, ast->name, ast->context); + ast_copy_string(ast->exten, args.ext, sizeof(ast->exten)); - ast_copy_string(newbc->dad, args.ext, sizeof(newbc->dad)); - } + ast_copy_string(newbc->dialed.number, args.ext, sizeof(newbc->dialed.number)); - ast_copy_string(newbc->rad, S_OR(ast->cid.cid_rdnis, ""), sizeof(newbc->rad)); + if (ast_strlen_zero(newbc->caller.name) && !ast_strlen_zero(ast->connected.id.name)) { + ast_copy_string(newbc->caller.name, ast->connected.id.name, sizeof(newbc->caller.name)); + chan_misdn_log(3, port, " --> * set caller:\"%s\" <%s>\n", newbc->caller.name, newbc->caller.number); + } + if (ast_strlen_zero(newbc->caller.number) && !ast_strlen_zero(ast->connected.id.number)) { + ast_copy_string(newbc->caller.number, ast->connected.id.number, sizeof(newbc->caller.number)); + chan_misdn_log(3, port, " --> * set caller:\"%s\" <%s>\n", newbc->caller.name, newbc->caller.number); + } - chan_misdn_log(3, port, " --> * adding2newbc callerid %s\n", ast->cid.cid_num); - if (ast_strlen_zero(newbc->oad) && !ast_strlen_zero(ast->cid.cid_num)) { - ast_copy_string(newbc->oad, ast->cid.cid_num, sizeof(newbc->oad)); - } + misdn_cfg_get(port, MISDN_CFG_LOCALDIALPLAN, &number_type, sizeof(number_type)); + if (number_type < 0) { + newbc->caller.number_type = ast_to_misdn_ton(ast->connected.id.number_type); + newbc->caller.number_plan = ast_to_misdn_plan(ast->connected.id.number_type); + } else { + /* Force us to send in SETUP message */ + newbc->caller.number_type = number_type; + newbc->caller.number_plan = NUMPLAN_ISDN; + } + debug_numtype(port, newbc->caller.number_type, "LTON"); - newbc->capability = ast->transfercapability; - pbx_builtin_setvar_helper(ast, "TRANSFERCAPABILITY", ast_transfercapability2str(newbc->capability)); - if ( ast->transfercapability == INFO_CAPABILITY_DIGITAL_UNRESTRICTED) { - chan_misdn_log(2, port, " --> * Call with flag Digital\n"); - } + newbc->capability = ast->transfercapability; + pbx_builtin_setvar_helper(ast, "TRANSFERCAPABILITY", ast_transfercapability2str(newbc->capability)); + if (ast->transfercapability == INFO_CAPABILITY_DIGITAL_UNRESTRICTED) { + chan_misdn_log(2, port, " --> * Call with flag Digital\n"); + } - /* update screening and presentation */ - update_config(ch, ORG_AST); - - /* fill in some ies from channel vary */ - import_ch(ast, newbc, ch); + /* update caller screening and presentation */ + update_config(ch); - /* Finally The Options Override Everything */ - if (!ast_strlen_zero(args.opts)) { - misdn_set_opt_exec(ast, args.opts); - } else { - chan_misdn_log(2, port, "NO OPTS GIVEN\n"); - } + /* fill in some ies from channel dialplan variables */ + import_ch(ast, newbc, ch); - /*check for bridging*/ - misdn_cfg_get(0, MISDN_GEN_BRIDGING, &bridging, sizeof(bridging)); - if (bridging && ch->other_ch) { + /* Finally The Options Override Everything */ + if (!ast_strlen_zero(args.opts)) { + misdn_set_opt_exec(ast, args.opts); + } else { + chan_misdn_log(2, port, "NO OPTS GIVEN\n"); + } + if (newbc->set_presentation) { + newbc->caller.presentation = newbc->presentation; + } + + misdn_copy_redirecting_from_ast(newbc, ast); + switch (newbc->outgoing_colp) { + case 1:/* restricted */ + case 2:/* blocked */ + newbc->redirecting.from.presentation = 1;/* restricted */ + break; + default: + break; + } +#if defined(AST_MISDN_ENHANCEMENTS) + if (newbc->redirecting.from.number[0] && misdn_lib_is_ptp(port)) { + /* Create DivertingLegInformation2 facility */ + newbc->fac_out.Function = Fac_DivertingLegInformation2; + newbc->fac_out.u.DivertingLegInformation2.InvokeID = ++misdn_invoke_id; + newbc->fac_out.u.DivertingLegInformation2.DiversionCounter = + newbc->redirecting.count; + newbc->fac_out.u.DivertingLegInformation2.DiversionReason = + misdn_to_diversion_reason(newbc->redirecting.reason); + newbc->fac_out.u.DivertingLegInformation2.DivertingPresent = 1; + misdn_PresentedNumberUnscreened_fill( + &newbc->fac_out.u.DivertingLegInformation2.Diverting, + &newbc->redirecting.from); + switch (newbc->outgoing_colp) { + case 2:/* blocked */ + /* Block the number going out */ + newbc->fac_out.u.DivertingLegInformation2.Diverting.Type = 1;/* presentationRestricted */ + break; + default: + break; + } + newbc->fac_out.u.DivertingLegInformation2.OriginalCalledPresent = 0; + if (1 < newbc->redirecting.count) { + newbc->fac_out.u.DivertingLegInformation2.OriginalCalledPresent = 1; + newbc->fac_out.u.DivertingLegInformation2.OriginalCalled.Type = 2;/* numberNotAvailableDueToInterworking */ + } + + /* + * Expect a DivertingLegInformation3 to update the COLR of the + * redirecting-to party we are attempting to call now. + */ + newbc->div_leg_3_rx_wanted = 1; + } +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + + /*check for bridging*/ + misdn_cfg_get(0, MISDN_GEN_BRIDGING, &bridging, sizeof(bridging)); + if (bridging && ch->other_ch) { #ifdef MISDN_1_2 - chan_misdn_log(1, port, "Disabling EC (aka Pipeline) on both Sides\n"); - *ch->bc->pipeline = 0; - *ch->other_ch->bc->pipeline = 0; + chan_misdn_log(1, port, "Disabling EC (aka Pipeline) on both Sides\n"); + *ch->bc->pipeline = 0; + *ch->other_ch->bc->pipeline = 0; #else - chan_misdn_log(1, port, "Disabling EC on both Sides\n"); - ch->bc->ec_enable = 0; - ch->other_ch->bc->ec_enable = 0; + chan_misdn_log(1, port, "Disabling EC on both Sides\n"); + ch->bc->ec_enable = 0; + ch->other_ch->bc->ec_enable = 0; #endif + } } + exceed = add_out_calls(port); + if (exceed != 0) { + char tmp[16]; + + snprintf(tmp, sizeof(tmp), "%d", exceed); + pbx_builtin_setvar_helper(ast, "MAX_OVERFLOW", tmp); + ast->hangupcause = AST_CAUSE_NORMAL_TEMPORARY_FAILURE; + ast_setstate(ast, AST_STATE_DOWN); + return -1; + } + +#if defined(AST_MISDN_ENHANCEMENTS) + if (newbc->fac_out.Function != Fac_None) { + print_facility(&newbc->fac_out, newbc); + } +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ r = misdn_lib_send_event(newbc, EVENT_SETUP); /** we should have l3id after sending setup **/ ch->l3id = newbc->l3_id; - if (r == -ENOCHAN ) { + if (r == -ENOCHAN) { chan_misdn_log(0, port, " --> * Theres no Channel at the moment .. !\n"); chan_misdn_log(1, port, " --> * SEND: State Down pid:%d\n", newbc ? newbc->pid : -1); ast->hangupcause = AST_CAUSE_NORMAL_CIRCUIT_CONGESTION; @@ -2538,8 +6492,8 @@ } ch->state = MISDN_CALLING; - - return 0; + + return 0; } @@ -2551,9 +6505,9 @@ if (!ast || !(p = MISDN_ASTERISK_TECH_PVT(ast))) { return -1; } - + chan_misdn_log(1, p ? (p->bc ? p->bc->port : 0) : 0, "* ANSWER:\n"); - + if (!p) { ast_log(LOG_WARNING, " --> Channel not connected ??\n"); ast_queue_hangup_with_cause(ast, AST_CAUSE_NETWORK_OUT_OF_ORDER); @@ -2586,11 +6540,42 @@ p->state = MISDN_CONNECTED; stop_indicate(p); - if ( ast_strlen_zero(p->bc->cad) ) { - chan_misdn_log(2, p->bc->port, " --> empty cad using dad\n"); - ast_copy_string(p->bc->cad, p->bc->dad, sizeof(p->bc->cad)); + if (ast_strlen_zero(p->bc->connected.number)) { + chan_misdn_log(2,p->bc->port," --> empty connected number using dialed number\n"); + ast_copy_string(p->bc->connected.number, p->bc->dialed.number, sizeof(p->bc->connected.number)); + + /* + * Use the misdn_set_opt() application to set the presentation + * before we answer or you can use the CONECTEDLINE() function + * to set everything before using the Answer() application. + */ + p->bc->connected.presentation = p->bc->presentation; + p->bc->connected.screening = 0; /* unscreened */ + p->bc->connected.number_type = p->bc->dialed.number_type; + p->bc->connected.number_plan = p->bc->dialed.number_plan; } + switch (p->bc->outgoing_colp) { + case 1:/* restricted */ + case 2:/* blocked */ + p->bc->connected.presentation = 1;/* restricted */ + break; + default: + break; + } + +#if defined(AST_MISDN_ENHANCEMENTS) + if (p->bc->div_leg_3_tx_pending) { + p->bc->div_leg_3_tx_pending = 0; + + /* Send DivertingLegInformation3 */ + p->bc->fac_out.Function = Fac_DivertingLegInformation3; + p->bc->fac_out.u.DivertingLegInformation3.InvokeID = ++misdn_invoke_id; + p->bc->fac_out.u.DivertingLegInformation3.PresentationAllowedIndicator = + (p->bc->connected.presentation == 0) ? 1 : 0; + print_facility(&p->bc->fac_out, p->bc); + } +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ misdn_lib_send_event(p->bc, EVENT_CONNECT); start_bc_tones(p); @@ -2615,13 +6600,13 @@ bc = p->bc; chan_misdn_log(1, bc ? bc->port : 0, "* IND : Digit %c\n", digit); - + if (!bc) { ast_log(LOG_WARNING, " --> !! Got Digit Event without having bchannel Object\n"); return -1; } - - switch (p->state ) { + + switch (p->state) { case MISDN_CALLING: if (strlen(bc->infos_pending) < sizeof(bc->infos_pending) - 1) { strncat(bc->infos_pending, buf, sizeof(bc->infos_pending) - strlen(bc->infos_pending) - 1); @@ -2629,15 +6614,15 @@ break; case MISDN_CALLING_ACKNOWLEDGE: ast_copy_string(bc->info_dad, buf, sizeof(bc->info_dad)); - if (strlen(bc->dad) < sizeof(bc->dad) - 1) { - strncat(bc->dad, buf, sizeof(bc->dad) - strlen(bc->dad) - 1); + if (strlen(bc->dialed.number) < sizeof(bc->dialed.number) - 1) { + strncat(bc->dialed.number, buf, sizeof(bc->dialed.number) - strlen(bc->dialed.number) - 1); } - ast_copy_string(p->ast->exten, bc->dad, sizeof(p->ast->exten)); + ast_copy_string(p->ast->exten, bc->dialed.number, sizeof(p->ast->exten)); misdn_lib_send_event(bc, EVENT_INFORMATION); break; default: /* Do not send Digits in CONNECTED State, when - * the other side is too mISDN. */ + * the other side is also mISDN. */ if (p->other_ch) { return 0; } @@ -2673,48 +6658,48 @@ { struct chan_list *p; - if (!ast || ! (p=MISDN_ASTERISK_TECH_PVT(ast))) { + if (!ast || !(p = MISDN_ASTERISK_TECH_PVT(ast))) { ast_log(LOG_WARNING, "Returned -1 in misdn_indication\n"); return -1; } - - if (!p->bc ) { + + if (!p->bc) { chan_misdn_log(1, 0, "* IND : Indication from %s\n", ast->exten); ast_log(LOG_WARNING, "Private Pointer but no bc ?\n"); return -1; } - + chan_misdn_log(5, p->bc->port, "* IND : Indication [%d] from %s\n", cond, ast->exten); - + switch (cond) { case AST_CONTROL_BUSY: - chan_misdn_log(1, p->bc->port, "* IND :\tbusy pid:%d\n", p->bc ? p->bc->pid : -1); + chan_misdn_log(1, p->bc->port, "* IND :\tbusy pid:%d\n", p->bc->pid); ast_setstate(ast, AST_STATE_BUSY); p->bc->out_cause = AST_CAUSE_USER_BUSY; if (p->state != MISDN_CONNECTED) { start_bc_tones(p); - misdn_lib_send_event( p->bc, EVENT_DISCONNECT); + misdn_lib_send_event(p->bc, EVENT_DISCONNECT); } else { chan_misdn_log(-1, p->bc->port, " --> !! Got Busy in Connected State !?! ast:%s\n", ast->name); } return -1; case AST_CONTROL_RING: - chan_misdn_log(1, p->bc->port, "* IND :\tring pid:%d\n", p->bc ? p->bc->pid : -1); + chan_misdn_log(1, p->bc->port, "* IND :\tring pid:%d\n", p->bc->pid); return -1; case AST_CONTROL_RINGING: - chan_misdn_log(1, p->bc->port, "* IND :\tringing pid:%d\n", p->bc ? p->bc->pid : -1); + chan_misdn_log(1, p->bc->port, "* IND :\tringing pid:%d\n", p->bc->pid); switch (p->state) { case MISDN_ALERTING: - chan_misdn_log(2, p->bc->port, " --> * IND :\tringing pid:%d but I was Ringing before, so ignoring it\n", p->bc ? p->bc->pid : -1); + chan_misdn_log(2, p->bc->port, " --> * IND :\tringing pid:%d but I was Ringing before, so ignoring it\n", p->bc->pid); break; case MISDN_CONNECTED: - chan_misdn_log(2, p->bc->port, " --> * IND :\tringing pid:%d but Connected, so just send TONE_ALERTING without state changes \n", p->bc ? p->bc->pid : -1); + chan_misdn_log(2, p->bc->port, " --> * IND :\tringing pid:%d but Connected, so just send TONE_ALERTING without state changes \n", p->bc->pid); return -1; default: p->state = MISDN_ALERTING; - chan_misdn_log(2, p->bc->port, " --> * IND :\tringing pid:%d\n", p->bc ? p->bc->pid : -1); - misdn_lib_send_event( p->bc, EVENT_ALERTING); + chan_misdn_log(2, p->bc->port, " --> * IND :\tringing pid:%d\n", p->bc->pid); + misdn_lib_send_event(p->bc, EVENT_ALERTING); if (p->other_ch && p->other_ch->bc) { if (misdn_inband_avail(p->other_ch->bc)) { @@ -2728,7 +6713,7 @@ } } - chan_misdn_log(3, p->bc->port, " --> * SEND: State Ring pid:%d\n", p->bc ? p->bc->pid : -1); + chan_misdn_log(3, p->bc->port, " --> * SEND: State Ring pid:%d\n", p->bc->pid); ast_setstate(ast, AST_STATE_RING); if (!p->bc->nt && (p->originator == ORG_MISDN) && !p->incoming_early_audio) { @@ -2739,39 +6724,39 @@ } break; case AST_CONTROL_ANSWER: - chan_misdn_log(1, p->bc->port, " --> * IND :\tanswer pid:%d\n", p->bc ? p->bc->pid : -1); + chan_misdn_log(1, p->bc->port, " --> * IND :\tanswer pid:%d\n", p->bc->pid); start_bc_tones(p); break; case AST_CONTROL_TAKEOFFHOOK: - chan_misdn_log(1, p->bc->port, " --> *\ttakeoffhook pid:%d\n", p->bc ? p->bc->pid : -1); + chan_misdn_log(1, p->bc->port, " --> *\ttakeoffhook pid:%d\n", p->bc->pid); return -1; case AST_CONTROL_OFFHOOK: - chan_misdn_log(1, p->bc->port, " --> *\toffhook pid:%d\n", p->bc ? p->bc->pid : -1); + chan_misdn_log(1, p->bc->port, " --> *\toffhook pid:%d\n", p->bc->pid); return -1; case AST_CONTROL_FLASH: - chan_misdn_log(1, p->bc->port, " --> *\tflash pid:%d\n", p->bc ? p->bc->pid : -1); + chan_misdn_log(1, p->bc->port, " --> *\tflash pid:%d\n", p->bc->pid); break; case AST_CONTROL_PROGRESS: - chan_misdn_log(1, p->bc->port, " --> * IND :\tprogress pid:%d\n", p->bc ? p->bc->pid : -1); - misdn_lib_send_event( p->bc, EVENT_PROGRESS); + chan_misdn_log(1, p->bc->port, " --> * IND :\tprogress pid:%d\n", p->bc->pid); + misdn_lib_send_event(p->bc, EVENT_PROGRESS); break; case AST_CONTROL_PROCEEDING: - chan_misdn_log(1, p->bc->port, " --> * IND :\tproceeding pid:%d\n", p->bc ? p->bc->pid : -1); - misdn_lib_send_event( p->bc, EVENT_PROCEEDING); + chan_misdn_log(1, p->bc->port, " --> * IND :\tproceeding pid:%d\n", p->bc->pid); + misdn_lib_send_event(p->bc, EVENT_PROCEEDING); break; case AST_CONTROL_CONGESTION: - chan_misdn_log(1, p->bc->port, " --> * IND :\tcongestion pid:%d\n", p->bc ? p->bc->pid : -1); + chan_misdn_log(1, p->bc->port, " --> * IND :\tcongestion pid:%d\n", p->bc->pid); p->bc->out_cause = AST_CAUSE_SWITCH_CONGESTION; start_bc_tones(p); - misdn_lib_send_event( p->bc, EVENT_DISCONNECT); + misdn_lib_send_event(p->bc, EVENT_DISCONNECT); if (p->bc->nt) { hanguptone_indicate(p); } break; case -1 : - chan_misdn_log(1, p->bc->port, " --> * IND :\t-1! (stop indication) pid:%d\n", p->bc ? p->bc->pid : -1); + chan_misdn_log(1, p->bc->port, " --> * IND :\t-1! (stop indication) pid:%d\n", p->bc->pid); stop_indicate(p); @@ -2780,17 +6765,26 @@ } break; case AST_CONTROL_HOLD: - ast_moh_start(ast, data, p->mohinterpret); - chan_misdn_log(1, p->bc->port, " --> *\tHOLD pid:%d\n", p->bc ? p->bc->pid : -1); + ast_moh_start(ast, data, p->mohinterpret); + chan_misdn_log(1, p->bc->port, " --> *\tHOLD pid:%d\n", p->bc->pid); break; case AST_CONTROL_UNHOLD: ast_moh_stop(ast); - chan_misdn_log(1, p->bc->port, " --> *\tUNHOLD pid:%d\n", p->bc ? p->bc->pid : -1); + chan_misdn_log(1, p->bc->port, " --> *\tUNHOLD pid:%d\n", p->bc->pid); break; + case AST_CONTROL_CONNECTED_LINE: + chan_misdn_log(1, p->bc->port, "* IND :\tconnected line update pid:%d\n", p->bc->pid); + misdn_update_connected_line(ast, p->bc, p->originator); + break; + case AST_CONTROL_REDIRECTING: + chan_misdn_log(1, p->bc->port, "* IND :\tredirecting info update pid:%d\n", p->bc->pid); + misdn_update_redirecting(ast, p->bc, p->originator); + break; default: - chan_misdn_log(1, p->bc->port, " --> * Unknown Indication:%d pid:%d\n", cond, p->bc ? p->bc->pid : -1); + chan_misdn_log(1, p->bc->port, " --> * Unknown Indication:%d pid:%d\n", cond, p->bc->pid); + break; } - + return 0; } @@ -2815,8 +6809,10 @@ if (bc) { const char *tmp; + ast_channel_lock(ast); - if ((tmp = pbx_builtin_getvar_helper(ast, "MISDN_USERUSER"))) { + tmp = pbx_builtin_getvar_helper(ast, "MISDN_USERUSER"); + if (tmp) { ast_log(LOG_NOTICE, "MISDN_USERUSER: %s\n", tmp); strcpy(bc->uu, tmp); bc->uulen = strlen(bc->uu); @@ -2827,16 +6823,16 @@ MISDN_ASTERISK_TECH_PVT(ast) = NULL; p->ast = NULL; - if (ast->_state == AST_STATE_RESERVED || - p->state == MISDN_NOTHING || - p->state == MISDN_HOLDED || - p->state == MISDN_HOLD_DISCONNECT ) { + if (ast->_state == AST_STATE_RESERVED || + p->state == MISDN_NOTHING || + p->state == MISDN_HOLDED || + p->state == MISDN_HOLD_DISCONNECT) { - CLEAN_CH: +CLEAN_CH: /* between request and call */ ast_debug(1, "State Reserved (or nothing) => chanIsAvail\n"); MISDN_ASTERISK_TECH_PVT(ast) = NULL; - + ast_mutex_lock(&release_lock); cl_dequeue_chan(&cl_te, p); close(p->pipe[0]); @@ -2872,20 +6868,27 @@ if ((varcause = pbx_builtin_getvar_helper(ast, "HANGUPCAUSE")) || (varcause = pbx_builtin_getvar_helper(ast, "PRI_CAUSE"))) { int tmpcause = atoi(varcause); + bc->out_cause = tmpcause ? tmpcause : AST_CAUSE_NORMAL_CLEARING; } ast_channel_unlock(ast); - chan_misdn_log(1, bc->port, "* IND : HANGUP\tpid:%d ctx:%s dad:%s oad:%s State:%s\n", p->bc ? p->bc->pid : -1, ast->context, ast->exten, ast->cid.cid_num, misdn_get_ch_state(p)); + chan_misdn_log(1, bc->port, + "* IND : HANGUP\tpid:%d context:%s dialed:%s caller:\"%s\" <%s> State:%s\n", + p->bc ? p->bc->pid : -1, + ast->context, + ast->exten, + ast->cid.cid_name ? ast->cid.cid_name : "", + ast->cid.cid_num ? ast->cid.cid_num : "", + misdn_get_ch_state(p)); chan_misdn_log(3, bc->port, " --> l3id:%x\n", p->l3id); chan_misdn_log(3, bc->port, " --> cause:%d\n", bc->cause); chan_misdn_log(2, bc->port, " --> out_cause:%d\n", bc->out_cause); - chan_misdn_log(2, bc->port, " --> state:%s\n", misdn_get_ch_state(p)); switch (p->state) { case MISDN_INCOMING_SETUP: p->state = MISDN_CLEANING; - /* This is the only place in misdn_hangup, where we + /* This is the only place in misdn_hangup, where we * can call release_chan, else it might create lot's of trouble * */ ast_log(LOG_NOTICE, "release channel, in INCOMING_SETUP state.. no other events happened\n"); @@ -2970,7 +6973,7 @@ if (bc->need_release) { misdn_lib_send_event(bc, EVENT_RELEASE); } - p->state = MISDN_CLEANING; + p->state = MISDN_CLEANING; } else { if (bc->need_disconnect) { misdn_lib_send_event(bc, EVENT_DISCONNECT); @@ -2980,6 +6983,13 @@ p->state = MISDN_CLEANING; +#if defined(AST_MISDN_ENHANCEMENTS) + if (p->peer) { + ao2_ref(p->peer, -1); + p->peer = NULL; + } +#endif /* AST_MISDN_ENHANCEMENTS */ + chan_misdn_log(3, bc->port, " --> Channel: %s hanguped new state:%s\n", ast->name, misdn_get_ch_state(p)); return 0; @@ -2989,7 +6999,7 @@ static struct ast_frame *process_ast_dsp(struct chan_list *tmp, struct ast_frame *frame) { struct ast_frame *f,*f2; - + if (tmp->trans) { f2 = ast_translate(tmp->trans, frame, 0); f = ast_dsp_process(tmp->ast, tmp->dsp, f2); @@ -3000,9 +7010,9 @@ if (!f || (f->frametype != AST_FRAME_DTMF)) return frame; - + ast_debug(1, "Detected inband DTMF digit: %c\n", f->subclass); - + if (tmp->faxdetect && (f->subclass == 'f')) { /* Fax tone -- Handle and return NULL */ if (!tmp->faxhandled) { @@ -3035,7 +7045,7 @@ ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast->name, context); } } else { - ast_log(LOG_NOTICE, "Fax detected, but no fax extension ctx:%s exten:%s\n", context, ast->exten); + ast_log(LOG_NOTICE, "Fax detected but no fax extension, context:%s exten:%s\n", context, ast->exten); } } else { ast_debug(1, "Already in a fax extension, not redirecting\n"); @@ -3044,12 +7054,14 @@ case 2: ast_verb(3, "Not redirecting %s to fax extension, nojump is set.\n", ast->name); break; + default: + break; } } else { ast_debug(1, "Fax already handled\n"); } } - + if (tmp->ast_dsp && (f->subclass != 'f')) { chan_misdn_log(2, tmp->bc->port, " --> * SEND: DTMF (AST_DSP) :%c\n", f->subclass); } @@ -3082,7 +7094,8 @@ FD_ZERO(&rrfs); FD_SET(tmp->pipe[0], &rrfs); - if (!(t = select(FD_SETSIZE, &rrfs, NULL, NULL, &tv))) { + t = select(FD_SETSIZE, &rrfs, NULL, NULL, &tv); + if (!t) { chan_misdn_log(3, tmp->bc->port, "read Select Timed out\n"); len = 160; } @@ -3100,7 +7113,6 @@ chan_misdn_log(2, tmp->bc->port, "misdn_read: Pipe closed, hanging up\n"); return NULL; } - } else { return NULL; } @@ -3151,7 +7163,7 @@ { struct chan_list *ch; int i = 0; - + if (!ast || !(ch = MISDN_ASTERISK_TECH_PVT(ast))) { return -1; } @@ -3160,12 +7172,12 @@ chan_misdn_log(7, 0, "misdn_write: Returning because holded\n"); return 0; } - - if (!ch->bc ) { + + if (!ch->bc) { ast_log(LOG_WARNING, "private but no bc\n"); return -1; } - + if (ch->notxtone) { chan_misdn_log(7, ch->bc->port, "misdn_write: Returning because notxtone\n"); return 0; @@ -3176,16 +7188,16 @@ chan_misdn_log(4, ch->bc->port, "misdn_write: * prods us\n"); return 0; } - + if (!(frame->subclass & prefformat)) { chan_misdn_log(-1, ch->bc->port, "Got Unsupported Frame with Format:%d\n", frame->subclass); return 0; } - - if (!frame->samples ) { + + if (!frame->samples) { chan_misdn_log(4, ch->bc->port, "misdn_write: zero write\n"); - + if (!strcmp(frame->src,"ast_prod")) { chan_misdn_log(1, ch->bc->port, "misdn_write: state (%s) prodded.\n", misdn_get_ch_state(ch)); @@ -3203,10 +7215,11 @@ chan_misdn_log(8, ch->bc->port, "misdn_write: no addr for bc dropping:%d\n", frame->samples); return 0; } - + #ifdef MISDN_DEBUG { - int i, max = 5 > frame->samples ? frame->samples : 5; + int i; + int max = 5 > frame->samples ? frame->samples : 5; ast_debug(1, "write2mISDN %p %d bytes: ", p, frame->samples); @@ -3222,9 +7235,11 @@ break; default: if (!ch->dropped_frame_cnt) { - chan_misdn_log(5, ch->bc->port, "BC not active (nor bridged) dropping: %d frames addr:%x exten:%s cid:%s ch->state:%s bc_state:%d l3id:%x\n", frame->samples, ch->bc->addr, ast->exten, ast->cid.cid_num, misdn_get_ch_state( ch), ch->bc->bc_state, ch->bc->l3_id); + chan_misdn_log(5, ch->bc->port, + "BC not active (nor bridged) dropping: %d frames addr:%x exten:%s cid:%s ch->state:%s bc_state:%d l3id:%x\n", + frame->samples, ch->bc->addr, ast->exten, ast->cid.cid_num, misdn_get_ch_state(ch), ch->bc->bc_state, ch->bc->l3_id); } - + if (++ch->dropped_frame_cnt > 100) { ch->dropped_frame_cnt = 0; chan_misdn_log(5, ch->bc->port, "BC not active (nor bridged) dropping: %d frames addr:%x dropped > 100 frames!\n", frame->samples, ch->bc->addr); @@ -3241,24 +7256,20 @@ cb_log(0, ch->bc->port, "Misdn Jitterbuffer Overflow.\n"); } } - + } else { - /*transmit without jitterbuffer*/ + /* transmit without jitterbuffer */ i = misdn_lib_tx2misdn_frm(ch->bc, frame->data.ptr, frame->samples); } return 0; } - - - -static enum ast_bridge_result misdn_bridge (struct ast_channel *c0, - struct ast_channel *c1, int flags, - struct ast_frame **fo, - struct ast_channel **rc, - int timeoutms) - +static enum ast_bridge_result misdn_bridge(struct ast_channel *c0, + struct ast_channel *c1, int flags, + struct ast_frame **fo, + struct ast_channel **rc, + int timeoutms) { struct chan_list *ch1, *ch2; struct ast_channel *carr[2], *who; @@ -3266,13 +7277,13 @@ struct ast_frame *f; int p1_b, p2_b; int bridging; - + ch1 = get_chan_by_ast(c0); ch2 = get_chan_by_ast(c1); carr[0] = c0; carr[1] = c1; - + if (!(ch1 && ch2)) { return -1; } @@ -3294,13 +7305,17 @@ ast_verb(3, "Native bridging %s and %s\n", c0->name, c1->name); - chan_misdn_log(1, ch1->bc->port, "* Making Native Bridge between %s and %s\n", ch1->bc->oad, ch2->bc->oad); - - if (! (flags & AST_BRIDGE_DTMF_CHANNEL_0) ) { + chan_misdn_log(1, ch1->bc->port, "* Making Native Bridge between \"%s\" <%s> and \"%s\" <%s>\n", + ch1->bc->caller.name, + ch1->bc->caller.number, + ch2->bc->caller.name, + ch2->bc->caller.number); + + if (!(flags & AST_BRIDGE_DTMF_CHANNEL_0)) { ch1->ignore_dtmf = 1; } - if (! (flags & AST_BRIDGE_DTMF_CHANNEL_1) ) { + if (!(flags & AST_BRIDGE_DTMF_CHANNEL_1)) { ch2->ignore_dtmf = 1; } @@ -3317,33 +7332,34 @@ if (!f || f->frametype == AST_FRAME_CONTROL) { /* got hangup .. */ - if (!f) + if (!f) { chan_misdn_log(4, ch1->bc->port, "Read Null Frame\n"); - else + } else { chan_misdn_log(4, ch1->bc->port, "Read Frame Control class:%d\n", f->subclass); + } *fo = f; *rc = who; break; } - - if ( f->frametype == AST_FRAME_DTMF ) { + + if (f->frametype == AST_FRAME_DTMF) { chan_misdn_log(1, 0, "Read DTMF %d from %s\n", f->subclass, who->exten); *fo = f; *rc = who; break; } - + #if 0 if (f->frametype == AST_FRAME_VOICE) { chan_misdn_log(1, ch1->bc->port, "I SEND: Splitting conference with Number:%d\n", ch1->bc->pid +1); - + continue; } #endif - ast_write(who == c0 ? c1 : c0, f); + ast_write((who == c0) ? c1 : c0, f); } chan_misdn_log(1, ch1->bc->port, "I SEND: Splitting conference with Number:%d\n", ch1->bc->pid + 1); @@ -3371,11 +7387,11 @@ chan_misdn_log(1, cl->bc->port, "Not sending Dialtone, because config wants it\n"); return 0; } - + chan_misdn_log(3, cl->bc->port, " --> Dial\n"); cl->ts = ast_get_indication_tone(ast->zone, "dial"); - + if (cl->ts) { cl->notxtone = 0; cl->norxtone = 0; @@ -3429,7 +7445,7 @@ cl->notxtone = 1; cl->norxtone = 1; - + return 0; } @@ -3438,59 +7454,121 @@ { struct chan_list *cl; - if (!(cl = ast_calloc(1, sizeof(*cl)))) { + cl = ast_calloc(1, sizeof(*cl)); + if (!cl) { chan_misdn_log(-1, 0, "misdn_request: malloc failed!"); return NULL; } - + cl->originator = orig; cl->need_queue_hangup = 1; cl->need_hangup = 1; cl->need_busy = 1; cl->overlap_dial_task = -1; +#if defined(AST_MISDN_ENHANCEMENTS) + cl->record_id = -1; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ return cl; } static struct ast_channel *misdn_request(const char *type, int format, void *data, int *cause) { - struct ast_channel *tmp = NULL; + struct ast_channel *ast; char group[BUFFERSIZE + 1] = ""; - char buf[128]; - char *buf2 = ast_strdupa(data), *ext = NULL, *port_str; - char *tokb = NULL, *p = NULL; - int channel = 0, port = 0; + char dial_str[128]; + char *dest_cp; + char *p = NULL; + int channel = 0; + int port = 0; struct misdn_bchannel *newbc = NULL; int dec = 0; +#if defined(AST_MISDN_ENHANCEMENTS) + int cc_retry_call = 0; /* TRUE if this is a call completion retry call */ + long record_id = -1; + struct misdn_cc_record *cc_record; + const char *err_msg; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + struct chan_list *cl; - struct chan_list *cl = init_chan_list(ORG_AST); + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(intf); /* interface token */ + AST_APP_ARG(ext); /* extension token */ + AST_APP_ARG(opts); /* options token */ + ); - snprintf(buf, sizeof(buf), "%s/%s", misdn_type, (char*)data); + snprintf(dial_str, sizeof(dial_str), "%s/%s", misdn_type, (char *) data); - port_str = strtok_r(buf2, "/", &tokb); + /* + * data is ---v + * Dial(mISDN/g:group_name[/extension[/options]]) + * Dial(mISDN/port[:preselected_channel][/extension[/options]]) + * Dial(mISDN/cc/cc-record-id) + * + * The dial extension could be empty if you are using MISDN_KEYPAD + * to control ISDN provider features. + */ + dest_cp = ast_strdupa(data); + AST_NONSTANDARD_APP_ARGS(args, dest_cp, '/'); + if (!args.ext) { + args.ext = ""; + } - ext = strtok_r(NULL, "/", &tokb); - - if (port_str) { - if (port_str[0] == 'g' && port_str[1] == ':' ) { + if (!ast_strlen_zero(args.intf)) { + if (args.intf[0] == 'g' && args.intf[1] == ':') { /* We make a group call lets checkout which ports are in my group */ - port_str += 2; - ast_copy_string(group, port_str, sizeof(group)); + args.intf += 2; + ast_copy_string(group, args.intf, sizeof(group)); chan_misdn_log(2, 0, " --> Group Call group: %s\n", group); - } else if ((p = strchr(port_str, ':'))) { +#if defined(AST_MISDN_ENHANCEMENTS) + } else if (strcmp(args.intf, "cc") == 0) { + cc_retry_call = 1; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + } else if ((p = strchr(args.intf, ':'))) { /* we have a preselected channel */ - *p = 0; - channel = atoi(++p); - port = atoi(port_str); + *p++ = 0; + channel = atoi(p); + port = atoi(args.intf); chan_misdn_log(2, port, " --> Call on preselected Channel (%d).\n", channel); } else { - port = atoi(port_str); + port = atoi(args.intf); } } else { - ast_log(LOG_WARNING, " --> ! IND : CALL dad:%s WITHOUT PORT/Group, check extensions.conf\n", ext); + ast_log(LOG_WARNING, " --> ! IND : Dial(%s) WITHOUT Port or Group, check extensions.conf\n", dial_str); return NULL; } +#if defined(AST_MISDN_ENHANCEMENTS) + if (cc_retry_call) { + if (ast_strlen_zero(args.ext)) { + ast_log(LOG_WARNING, " --> ! IND : Dial(%s) WITHOUT cc-record-id, check extensions.conf\n", dial_str); + return NULL; + } + if (!isdigit(*args.ext)) { + ast_log(LOG_WARNING, " --> ! IND : Dial(%s) cc-record-id must be a number.\n", dial_str); + return NULL; + } + record_id = atol(args.ext); + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_id(record_id); + if (!cc_record) { + AST_LIST_UNLOCK(&misdn_cc_records_db); + err_msg = misdn_cc_record_not_found; + ast_log(LOG_WARNING, " --> ! IND : Dial(%s) %s.\n", dial_str, err_msg); + return NULL; + } + if (!cc_record->activated) { + AST_LIST_UNLOCK(&misdn_cc_records_db); + err_msg = "Call completion has not been activated"; + ast_log(LOG_WARNING, " --> ! IND : Dial(%s) %s.\n", dial_str, err_msg); + return NULL; + } + port = cc_record->port; + AST_LIST_UNLOCK(&misdn_cc_records_db); + } +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + if (misdn_cfg_is_group_method(group, METHOD_STANDARD_DEC)) { chan_misdn_log(4, port, " --> STARTING STANDARD DEC...\n"); dec = 1; @@ -3524,9 +7602,10 @@ if (port >= port_start) { next_chan = 1; } - + if (port <= port_start && next_chan) { - int maxbchans=misdn_lib_get_maxchans(port); + int maxbchans = misdn_lib_get_maxchans(port); + if (++robin_channel >= maxbchans) { robin_channel = 1; } @@ -3538,13 +7617,14 @@ if (!strcasecmp(cfg_group, group)) { int port_up; int check; + misdn_cfg_get(port, MISDN_CFG_PMP_L1_CHECK, &check, sizeof(check)); port_up = misdn_lib_port_up(port, check); if (check && !port_up) { chan_misdn_log(1, port, "L1 is not Up on this Port\n"); } - + if (check && port_up < 0) { ast_log(LOG_WARNING, "This port (%d) is blocked\n", port); } @@ -3564,37 +7644,38 @@ } } } while (!newbc && robin_channel != rr->channel); - } else { + } else { for (port = misdn_cfg_get_next_port(0); port > 0; - port = misdn_cfg_get_next_port(port)) { - + port = misdn_cfg_get_next_port(port)) { misdn_cfg_get(port, MISDN_CFG_GROUPNAME, cfg_group, sizeof(cfg_group)); chan_misdn_log(3, port, "Group [%s] Port [%d]\n", group, port); if (!strcasecmp(cfg_group, group)) { int port_up; int check; + misdn_cfg_get(port, MISDN_CFG_PMP_L1_CHECK, &check, sizeof(check)); port_up = misdn_lib_port_up(port, check); chan_misdn_log(4, port, "portup:%d\n", port_up); if (port_up > 0) { - if ((newbc = misdn_lib_get_free_bc(port, 0, 0, dec))) { + newbc = misdn_lib_get_free_bc(port, 0, 0, dec); + if (newbc) { break; } } } } } - + /* Group dial failed ?*/ if (!newbc) { - ast_log(LOG_WARNING, - "Could not Dial out on group '%s'.\n" - "\tEither the L2 and L1 on all of these ports where DOWN (see 'show application misdn_check_l2l1')\n" - "\tOr there was no free channel on none of the ports\n\n" - , group); + ast_log(LOG_WARNING, + "Could not Dial out on group '%s'.\n" + "\tEither the L2 and L1 on all of these ports where DOWN (see 'show application misdn_check_l2l1')\n" + "\tOr there was no free channel on none of the ports\n\n", + group); return NULL; } } else { @@ -3603,42 +7684,48 @@ chan_misdn_log(1, port, " --> preselected_channel: %d\n", channel); } newbc = misdn_lib_get_free_bc(port, channel, 0, dec); - if (!newbc) { - ast_log(LOG_WARNING, "Could not create channel on port:%d with extensions:%s\n", port, ext); + ast_log(LOG_WARNING, "Could not create channel on port:%d for Dial(%s)\n", port, dial_str); return NULL; } } - /* create ast_channel and link all the objects together */ + cl = init_chan_list(ORG_AST); + if (!cl) { + ast_log(LOG_ERROR, "Could not create call record for Dial(%s)\n", dial_str); + return NULL; + } cl->bc = newbc; - - tmp = misdn_new(cl, AST_STATE_RESERVED, ext, NULL, format, port, channel); - if (!tmp) { - ast_log(LOG_ERROR, "Could not create Asterisk object\n"); + + ast = misdn_new(cl, AST_STATE_RESERVED, args.ext, NULL, format, port, channel); + if (!ast) { + ast_log(LOG_ERROR, "Could not create Asterisk channel for Dial(%s)\n", dial_str); return NULL; } + cl->ast = ast; - cl->ast = tmp; - +#if defined(AST_MISDN_ENHANCEMENTS) + cl->record_id = record_id; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + /* register chan in local list */ cl_queue_chan(&cl_te, cl); - + /* fill in the config into the objects */ - read_config(cl, ORG_AST); + read_config(cl); /* important */ cl->need_hangup = 0; - - return tmp; + + return ast; } static int misdn_send_text(struct ast_channel *chan, const char *text) { struct chan_list *tmp = chan->tech_pvt; - + if (tmp && tmp->bc) { ast_copy_string(tmp->bc->display, text, sizeof(tmp->bc->display)); misdn_lib_send_event(tmp->bc, EVENT_INFORMATION); @@ -3646,19 +7733,19 @@ ast_log(LOG_WARNING, "No chan_list but send_text request?\n"); return -1; } - + return 0; } static struct ast_channel_tech misdn_tech = { - .type = "mISDN", + .type = misdn_type, .description = "Channel driver for mISDN Support (Bri/Pri)", .capabilities = AST_FORMAT_ALAW , .requester = misdn_request, .send_digit_begin = misdn_digit_begin, .send_digit_end = misdn_digit_end, .call = misdn_call, - .bridge = misdn_bridge, + .bridge = misdn_bridge, .hangup = misdn_hangup, .answer = misdn_answer, .read = misdn_read, @@ -3670,7 +7757,7 @@ }; static struct ast_channel_tech misdn_tech_wo_bridge = { - .type = "mISDN", + .type = misdn_type, .description = "Channel driver for mISDN Support (Bri/Pri)", .capabilities = AST_FORMAT_ALAW , .requester = misdn_request, @@ -3690,16 +7777,17 @@ static int glob_channel = 0; -static void update_name(struct ast_channel *tmp, int port, int c) +static void update_name(struct ast_channel *tmp, int port, int c) { int chan_offset = 0; int tmp_port = misdn_cfg_get_next_port(0); char newname[255]; + for (; tmp_port > 0; tmp_port = misdn_cfg_get_next_port(tmp_port)) { if (tmp_port == port) { break; } - chan_offset += misdn_lib_port_is_pri(tmp_port) ? 30 : 2; + chan_offset += misdn_lib_port_is_pri(tmp_port) ? 30 : 2; } if (c < 0) { c = 0; @@ -3708,7 +7796,9 @@ snprintf(newname, sizeof(newname), "%s/%d-", misdn_type, chan_offset + c); if (strncmp(tmp->name, newname, strlen(newname))) { snprintf(newname, sizeof(newname), "%s/%d-u%d", misdn_type, chan_offset + c, glob_channel++); + ast_channel_lock(tmp); ast_change_name(tmp, newname); + ast_channel_unlock(tmp); chan_misdn_log(3, port, " --> updating channel name to [%s]\n", tmp->name); } } @@ -3737,7 +7827,7 @@ tmp = ast_channel_alloc(1, state, cid_num, cid_name, "", exten, "", 0, "%s/%s%d-u%d", misdn_type, c ? "" : "tmp", chan_offset + c, glob_channel++); if (tmp) { - chan_misdn_log(2, 0, " --> * NEW CHANNEL dad:%s oad:%s\n", exten, callerid); + chan_misdn_log(2, 0, " --> * NEW CHANNEL dialed:%s caller:%s\n", exten, callerid); tmp->nativeformats = prefformat; @@ -3745,11 +7835,10 @@ tmp->rawreadformat = format; tmp->writeformat = format; tmp->rawwriteformat = format; - + tmp->tech_pvt = chlist; misdn_cfg_get(0, MISDN_GEN_BRIDGING, &bridging, sizeof(bridging)); - tmp->tech = bridging ? &misdn_tech : &misdn_tech_wo_bridge; tmp->writeformat = format; @@ -3779,20 +7868,25 @@ } else { chan_misdn_log(-1, 0, "Unable to allocate channel structure\n"); } - + return tmp; } static struct chan_list *find_chan_by_bc(struct chan_list *list, struct misdn_bchannel *bc) { struct chan_list *help = list; + for (; help; help = help->next) { if (help->bc == bc) { return help; } } - chan_misdn_log(6, bc->port, "$$$ find_chan: No channel found for oad:%s dad:%s\n", bc->oad, bc->dad); + chan_misdn_log(6, bc->port, + "$$$ find_chan_by_bc: No channel found for dialed:%s caller:\"%s\" <%s>\n", + bc->dialed.number, + bc->caller.name, + bc->caller.number); return NULL; } @@ -3800,13 +7894,14 @@ static struct chan_list *find_chan_by_pid(struct chan_list *list, int pid) { struct chan_list *help = list; + for (; help; help = help->next) { if (help->bc && (help->bc->pid == pid)) { return help; } } - chan_misdn_log(6, 0, "$$$ find_chan: No channel found for pid:%d\n", pid); + chan_misdn_log(6, 0, "$$$ find_chan_by_pid: No channel found for pid:%d\n", pid); return NULL; } @@ -3819,21 +7914,29 @@ return NULL; } - chan_misdn_log(6, bc->port, "$$$ find_holded: channel:%d oad:%s dad:%s\n", bc->channel, bc->oad, bc->dad); + chan_misdn_log(6, bc->port, "$$$ find_holded: channel:%d dialed:%s caller:\"%s\" <%s>\n", + bc->channel, + bc->dialed.number, + bc->caller.name, + bc->caller.number); for (; help; help = help->next) { chan_misdn_log(4, bc->port, "$$$ find_holded: --> holded:%d channel:%d\n", help->state == MISDN_HOLDED, help->hold_info.channel); - if ((help->state == MISDN_HOLDED) && + if ((help->state == MISDN_HOLDED) && (help->hold_info.port == bc->port)) { return help; } } - chan_misdn_log(6, bc->port, "$$$ find_chan: No channel found for oad:%s dad:%s\n", bc->oad, bc->dad); + chan_misdn_log(6, bc->port, + "$$$ find_holded: No channel found for dialed:%s caller:\"%s\" <%s>\n", + bc->dialed.number, + bc->caller.name, + bc->caller.number); return NULL; } -static struct chan_list *find_holded_l3(struct chan_list *list, unsigned long l3_id, int w) +static struct chan_list *find_holded_l3(struct chan_list *list, unsigned long l3_id, int w) { struct chan_list *help = list; @@ -3850,20 +7953,20 @@ static void cl_queue_chan(struct chan_list **list, struct chan_list *chan) { chan_misdn_log(4, chan->bc ? chan->bc->port : 0, "* Queuing chan %p\n", chan); - + ast_mutex_lock(&cl_te_lock); if (!*list) { *list = chan; } else { struct chan_list *help = *list; - for (; help->next; help = help->next); + for (; help->next; help = help->next); help->next = chan; } chan->next = NULL; ast_mutex_unlock(&cl_te_lock); } -static void cl_dequeue_chan(struct chan_list **list, struct chan_list *chan) +static void cl_dequeue_chan(struct chan_list **list, struct chan_list *chan) { struct chan_list *help; @@ -3879,13 +7982,13 @@ ast_mutex_unlock(&cl_te_lock); return; } - + if (*list == chan) { *list = (*list)->next; ast_mutex_unlock(&cl_te_lock); return; } - + for (help = *list; help->next; help = help->next) { if (help->next == chan) { help->next = help->next->next; @@ -3893,7 +7996,7 @@ return; } } - + ast_mutex_unlock(&cl_te_lock); } @@ -3902,7 +8005,7 @@ static int pbx_start_chan(struct chan_list *ch) { - int ret = ast_pbx_start(ch->ast); + int ret = ast_pbx_start(ch->ast); ch->need_hangup = (ret >= 0) ? 0 : 1; @@ -3911,12 +8014,14 @@ static void hangup_chan(struct chan_list *ch) { - int port = ch ? (ch->bc ? ch->bc->port : 0) : 0; + int port; + if (!ch) { cb_log(1, 0, "Cannot hangup chan, no ch\n"); return; } + port = ch->bc ? ch->bc->port : 0; cb_log(5, port, "hangup_chan called\n"); if (ch->need_hangup) { @@ -3948,25 +8053,24 @@ } /** Isdn asks us to release channel, pendant to misdn_hangup **/ -static void release_chan(struct misdn_bchannel *bc) { - struct ast_channel *ast = NULL; +static void release_chan(struct misdn_bchannel *bc) +{ struct chan_list *ch; + struct ast_channel *ast; ast_mutex_lock(&release_lock); - if (!(ch = find_chan_by_bc(cl_te, bc))) { + + ch = find_chan_by_bc(cl_te, bc); + if (!ch) { chan_misdn_log(1, bc->port, "release_chan: Ch not found!\n"); ast_mutex_unlock(&release_lock); return; } - if (ch->ast) { - ast = ch->ast; - } - chan_misdn_log(5, bc->port, "release_chan: bc with l3id: %x\n", bc->l3_id); /* releasing jitterbuffer */ - if (ch->jb ) { + if (ch->jb) { misdn_jb_destroy(ch->jb); ch->jb = NULL; } else { @@ -3984,35 +8088,39 @@ } if (ch->originator == ORG_AST) { - misdn_out_calls[bc->port]--; + --misdn_out_calls[bc->port]; } else { - misdn_in_calls[bc->port]--; + --misdn_in_calls[bc->port]; } - if (ch) { - close(ch->pipe[0]); - close(ch->pipe[1]); + close(ch->pipe[0]); + close(ch->pipe[1]); - if (ast && MISDN_ASTERISK_TECH_PVT(ast)) { - chan_misdn_log(1, bc->port, "* RELEASING CHANNEL pid:%d ctx:%s dad:%s oad:%s state: %s\n", bc ? bc->pid : -1, ast->context, ast->exten, ast->cid.cid_num, misdn_get_ch_state(ch)); - chan_misdn_log(3, bc->port, " --> * State Down\n"); - MISDN_ASTERISK_TECH_PVT(ast) = NULL; + ast = ch->ast; + if (ast && MISDN_ASTERISK_TECH_PVT(ast)) { + chan_misdn_log(1, bc->port, + "* RELEASING CHANNEL pid:%d context:%s dialed:%s caller:\"%s\" <%s> state: %s\n", + bc->pid, + ast->context, + ast->exten, + ast->cid.cid_name ? ast->cid.cid_name : "", + ast->cid.cid_num ? ast->cid.cid_num : "", + misdn_get_ch_state(ch)); + chan_misdn_log(3, bc->port, " --> * State Down\n"); + MISDN_ASTERISK_TECH_PVT(ast) = NULL; - if (ast->_state != AST_STATE_RESERVED) { - chan_misdn_log(3, bc->port, " --> Setting AST State to down\n"); - ast_setstate(ast, AST_STATE_DOWN); - } + if (ast->_state != AST_STATE_RESERVED) { + chan_misdn_log(3, bc->port, " --> Setting AST State to down\n"); + ast_setstate(ast, AST_STATE_DOWN); } + } - ch->state = MISDN_CLEANING; - cl_dequeue_chan(&cl_te, ch); + ch->state = MISDN_CLEANING; + cl_dequeue_chan(&cl_te, ch); - ast_free(ch); - } else { - /* chan is already cleaned, so exiting */ - } + ast_free(ch); + ast_mutex_unlock(&release_lock); -/*** release end **/ } static void misdn_transfer_bc(struct chan_list *tmp_ch, struct chan_list *holded_chan) @@ -4040,25 +8148,28 @@ if (!ch->noautorespond_on_setup) { if (bc->nt) { - int ret; - ret = misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE ); + misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE); } else { - int ret; - if ( misdn_lib_is_ptp(bc->port)) { - ret = misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE ); + if (misdn_lib_is_ptp(bc->port)) { + misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE); } else { - ret = misdn_lib_send_event(bc, EVENT_PROCEEDING ); + misdn_lib_send_event(bc, EVENT_PROCEEDING); } } } else { ch->state = MISDN_INCOMING_SETUP; } - chan_misdn_log(1, bc->port, "* Starting Ast ctx:%s dad:%s oad:%s with 's' extension\n", ast->context, ast->exten, ast->cid.cid_num); - - strncpy(ast->exten, "s", 2); - - if (!ast_canmatch_extension(ast, ast->context, ast->exten, 1, bc->oad) || pbx_start_chan(ch) < 0) { + chan_misdn_log(1, bc->port, + "* Starting Ast context:%s dialed:%s caller:\"%s\" <%s> with 's' extension\n", + ast->context, + ast->exten, + ast->cid.cid_name ? ast->cid.cid_name : "", + ast->cid.cid_num ? ast->cid.cid_num : ""); + + strcpy(ast->exten, "s"); + + if (!ast_canmatch_extension(ast, ast->context, ast->exten, 1, bc->caller.number) || pbx_start_chan(ch) < 0) { ast = NULL; bc->out_cause = AST_CAUSE_UNALLOCATED; hangup_chan(ch); @@ -4066,9 +8177,9 @@ misdn_lib_send_event(bc, bc->nt ? EVENT_RELEASE_COMPLETE : EVENT_DISCONNECT); } - - - while (!ast_strlen_zero(predial) ) { + + + while (!ast_strlen_zero(predial)) { fr.frametype = AST_FRAME_DTMF; fr.subclass = *predial; fr.src = NULL; @@ -4079,7 +8190,7 @@ fr.offset = 0; fr.delivery = ast_tv(0,0); - if (ch->ast && MISDN_ASTERISK_PVT(ch->ast) && MISDN_ASTERISK_TECH_PVT(ch->ast)) { + if (ch->ast && MISDN_ASTERISK_TECH_PVT(ch->ast)) { ast_queue_frame(ch->ast, &fr); } predial++; @@ -4119,7 +8230,7 @@ * chan_misdn_log(1, bc ? bc->port : 0, " --> * SEND: Queue Congestion pid:%d\n", bc ? bc->pid : -1); ch->state = MISDN_BUSY; - + ast_queue_control(ast, AST_CONTROL_CONGESTION); */ break; @@ -4134,11 +8245,11 @@ } chan_misdn_log(1, bc ? bc->port : 0, " --> * SEND: Queue Busy pid:%d\n", bc ? bc->pid : -1); - + ast_queue_control(ast, AST_CONTROL_BUSY); - + ch->need_busy = 0; - + break; } } @@ -4185,6 +8296,7 @@ void export_ch(struct ast_channel *chan, struct misdn_bchannel *bc, struct chan_list *ch) { char tmp[32]; + chan_misdn_log(3, bc->port, " --> EXPORT_PID: pid:%d\n", bc->pid); snprintf(tmp, sizeof(tmp), "%d", bc->pid); pbx_builtin_setvar_helper(chan, "_MISDN_PID", tmp); @@ -4211,7 +8323,7 @@ int add_in_calls(int port) { int max_in_calls; - + misdn_cfg_get(port, MISDN_CFG_MAX_IN, &max_in_calls, sizeof(max_in_calls)); misdn_in_calls[port]++; @@ -4219,14 +8331,14 @@ ast_log(LOG_NOTICE, "Marking Incoming Call on port[%d]\n", port); return misdn_in_calls[port] - max_in_calls; } - + return 0; } int add_out_calls(int port) { int max_out_calls; - + misdn_cfg_get(port, MISDN_CFG_MAX_OUT, &max_out_calls, sizeof(max_out_calls)); if (max_out_calls >= 0 && max_out_calls <= misdn_out_calls[port]) { @@ -4235,11 +8347,12 @@ } misdn_out_calls[port]++; - + return 0; } -static void start_pbx(struct chan_list *ch, struct misdn_bchannel *bc, struct ast_channel *chan) { +static void start_pbx(struct chan_list *ch, struct misdn_bchannel *bc, struct ast_channel *chan) +{ if (pbx_start_chan(ch) < 0) { hangup_chan(ch); chan_misdn_log(-1, bc->port, "ast_pbx_start returned <0 in SETUP\n"); @@ -4252,36 +8365,967 @@ } } -static void wait_for_digits(struct chan_list *ch, struct misdn_bchannel *bc, struct ast_channel *chan) { +static void wait_for_digits(struct chan_list *ch, struct misdn_bchannel *bc, struct ast_channel *chan) +{ ch->state = MISDN_WAITING4DIGS; - misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE ); - if (bc->nt && !bc->dad[0]) { + misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE); + if (bc->nt && !bc->dialed.number[0]) { dialtone_indicate(ch); } } +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Handle the FACILITY CCBSStatusRequest message. + * + * \param port Logical port number. + * \param facility Facility ie contents. + * + * \return Nothing + */ +static void misdn_cc_handle_ccbs_status_request(int port, const struct FacParm *facility) +{ + struct misdn_cc_record *cc_record; + struct misdn_bchannel dummy; + switch (facility->u.CCBSStatusRequest.ComponentType) { + case FacComponent_Invoke: + /* Build message */ + misdn_make_dummy(&dummy, port, 0, misdn_lib_port_is_nt(port), 0); + dummy.fac_out.Function = Fac_CCBSStatusRequest; + dummy.fac_out.u.CCBSStatusRequest.InvokeID = facility->u.CCBSStatusRequest.InvokeID; + dummy.fac_out.u.CCBSStatusRequest.ComponentType = FacComponent_Result; + + /* Answer User-A free question */ + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_reference(port, facility->u.CCBSStatusRequest.Component.Invoke.CCBSReference); + if (cc_record) { + dummy.fac_out.u.CCBSStatusRequest.Component.Result.Free = cc_record->party_a_free; + } else { + /* No record so say User-A is free */ + dummy.fac_out.u.CCBSStatusRequest.Component.Result.Free = 1; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + + /* Send message */ + print_facility(&dummy.fac_out, &dummy); + misdn_lib_send_event(&dummy, EVENT_FACILITY); + break; + + default: + chan_misdn_log(0, port, " --> not yet handled: facility type:0x%04X\n", facility->Function); + break; + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Start a PBX to notify that User-B is available. + * + * \param record_id Call completion record ID + * \param notify Dialplan location to start processing. + * + * \return Nothing + */ +static void misdn_cc_pbx_notify(long record_id, const struct misdn_cc_notify *notify) +{ + struct ast_channel *chan; + char id_str[32]; + + static unsigned short sequence = 0; + + /* Create a channel to notify with */ + snprintf(id_str, sizeof(id_str), "%ld", record_id); + chan = ast_channel_alloc(0, AST_STATE_DOWN, id_str, NULL, NULL, + notify->exten, notify->context, 0, + "mISDN-CC/%ld-%X", record_id, (unsigned) ++sequence); + if (!chan) { + ast_log(LOG_ERROR, "Unable to allocate channel!\n"); + return; + } + chan->priority = notify->priority; + if (chan->cid.cid_dnid) { + ast_free(chan->cid.cid_dnid); + } + chan->cid.cid_dnid = ast_strdup(notify->exten); + + if (ast_pbx_start(chan)) { + ast_log(LOG_WARNING, "Unable to start pbx channel %s!\n", chan->name); + ast_channel_release(chan); + } else { + ast_verb(1, "Started pbx for call completion notify channel %s\n", chan->name); + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Handle the FACILITY CCBS_T_RemoteUserFree message. + * + * \param bc B channel control structure message came in on + * + * \return Nothing + */ +static void misdn_cc_handle_T_remote_user_free(struct misdn_bchannel *bc) +{ + struct misdn_cc_record *cc_record; + struct misdn_cc_notify notify; + long record_id; + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_bc(bc); + if (cc_record) { + if (cc_record->party_a_free) { + notify = cc_record->remote_user_free; + } else { + /* Send CCBS_T_Suspend message */ + bc->fac_out.Function = Fac_CCBS_T_Suspend; + bc->fac_out.u.CCBS_T_Suspend.InvokeID = ++misdn_invoke_id; + print_facility(&bc->fac_out, bc); + misdn_lib_send_event(bc, EVENT_FACILITY); + + notify = cc_record->b_free; + } + record_id = cc_record->record_id; + AST_LIST_UNLOCK(&misdn_cc_records_db); + if (notify.context[0]) { + /* Party A is free or B-Free notify has been setup. */ + misdn_cc_pbx_notify(record_id, ¬ify); + } + } else { + AST_LIST_UNLOCK(&misdn_cc_records_db); + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Handle the FACILITY CCBSRemoteUserFree message. + * + * \param port Logical port number. + * \param facility Facility ie contents. + * + * \return Nothing + */ +static void misdn_cc_handle_remote_user_free(int port, const struct FacParm *facility) +{ + struct misdn_cc_record *cc_record; + struct misdn_cc_notify notify; + long record_id; + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_reference(port, facility->u.CCBSRemoteUserFree.CCBSReference); + if (cc_record) { + notify = cc_record->remote_user_free; + record_id = cc_record->record_id; + AST_LIST_UNLOCK(&misdn_cc_records_db); + misdn_cc_pbx_notify(record_id, ¬ify); + } else { + AST_LIST_UNLOCK(&misdn_cc_records_db); + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Handle the FACILITY CCBSBFree message. + * + * \param port Logical port number. + * \param facility Facility ie contents. + * + * \return Nothing + */ +static void misdn_cc_handle_b_free(int port, const struct FacParm *facility) +{ + struct misdn_cc_record *cc_record; + struct misdn_cc_notify notify; + long record_id; + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_reference(port, facility->u.CCBSBFree.CCBSReference); + if (cc_record && cc_record->b_free.context[0]) { + /* B-Free notify has been setup. */ + notify = cc_record->b_free; + record_id = cc_record->record_id; + AST_LIST_UNLOCK(&misdn_cc_records_db); + misdn_cc_pbx_notify(record_id, ¬ify); + } else { + AST_LIST_UNLOCK(&misdn_cc_records_db); + } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +/*! + * \internal + * \brief Handle the incoming facility ie contents + * + * \param event Message type facility ie came in on + * \param bc B channel control structure message came in on + * \param ch Associated channel call record + * + * \return Nothing + */ +static void misdn_facility_ie_handler(enum event_e event, struct misdn_bchannel *bc, struct chan_list *ch) +{ +#if defined(AST_MISDN_ENHANCEMENTS) + const char *diagnostic_msg; + struct misdn_cc_record *cc_record; + char buf[32]; + struct misdn_party_id party_id; + long new_record_id; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + + print_facility(&bc->fac_in, bc); + switch (bc->fac_in.Function) { +#if defined(AST_MISDN_ENHANCEMENTS) + case Fac_ActivationDiversion: + switch (bc->fac_in.u.ActivationDiversion.ComponentType) { + case FacComponent_Result: + /* Positive ACK to activation */ + /* We don't handle this yet */ + break; + default: + chan_misdn_log(0, bc->port," --> not yet handled: facility type:0x%04X\n", + bc->fac_in.Function); + break; + } + break; + case Fac_DeactivationDiversion: + switch (bc->fac_in.u.DeactivationDiversion.ComponentType) { + case FacComponent_Result: + /* Positive ACK to deactivation */ + /* We don't handle this yet */ + break; + default: + chan_misdn_log(0, bc->port," --> not yet handled: facility type:0x%04X\n", + bc->fac_in.Function); + break; + } + break; + case Fac_ActivationStatusNotificationDiv: + /* Sent to other MSN numbers on the line when a user activates call forwarding. */ + /* Sent in the first call control message of an outgoing call from the served user. */ + /* We do not have anything to do for this message. */ + break; + case Fac_DeactivationStatusNotificationDiv: + /* Sent to other MSN numbers on the line when a user deactivates call forwarding. */ + /* We do not have anything to do for this message. */ + break; +#if 0 /* We don't handle this yet */ + case Fac_InterrogationDiversion: + /* We don't handle this yet */ + break; + case Fac_InterrogateServedUserNumbers: + /* We don't handle this yet */ + break; +#endif /* We don't handle this yet */ + case Fac_DiversionInformation: + /* Sent to the served user when a call is forwarded. */ + /* We do not have anything to do for this message. */ + break; + case Fac_CallDeflection: + if (ch && ch->ast) { + switch (bc->fac_in.u.CallDeflection.ComponentType) { + case FacComponent_Invoke: + ast_copy_string(bc->redirecting.from.number, bc->dialed.number, + sizeof(bc->redirecting.from.number)); + bc->redirecting.from.name[0] = 0; + bc->redirecting.from.number_plan = bc->dialed.number_plan; + bc->redirecting.from.number_type = bc->dialed.number_type; + bc->redirecting.from.screening = 0;/* Unscreened */ + if (bc->fac_in.u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUserPresent) { + bc->redirecting.from.presentation = + bc->fac_in.u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUser + ? 0 /* Allowed */ : 1 /* Restricted */; + } else { + bc->redirecting.from.presentation = 0;/* Allowed */ + } + + /* Add configured prefix to the call deflection number */ + memset(&party_id, 0, sizeof(party_id)); + misdn_PartyNumber_extract(&party_id, + &bc->fac_in.u.CallDeflection.Component.Invoke.Deflection.Party); + misdn_add_number_prefix(bc->port, party_id.number_type, + party_id.number, sizeof(party_id.number)); + //party_id.presentation = 0;/* Allowed */ + //party_id.screening = 0;/* Unscreened */ + bc->redirecting.to = party_id; + + ++bc->redirecting.count; + bc->redirecting.reason = mISDN_REDIRECTING_REASON_DEFLECTION; + + misdn_copy_redirecting_to_ast(ch->ast, &bc->redirecting); + ast_string_field_set(ch->ast, call_forward, bc->redirecting.to.number); + + /* Send back positive ACK */ + bc->fac_out.Function = Fac_CallDeflection; + bc->fac_out.u.CallDeflection.InvokeID = bc->fac_in.u.CallDeflection.InvokeID; + bc->fac_out.u.CallDeflection.ComponentType = FacComponent_Result; + print_facility(&bc->fac_out, bc); + misdn_lib_send_event(bc, EVENT_DISCONNECT); + + /* This line is BUSY to further attempts by this dialing attempt. */ + ast_queue_control(ch->ast, AST_CONTROL_BUSY); + break; + + case FacComponent_Result: + /* Positive ACK to call deflection */ + /* + * Sent in DISCONNECT or FACILITY message depending upon network option. + * It is in the FACILITY message if the call is still offered to the user + * while trying to alert the deflected to party. + */ + /* Ignore the ACK */ + break; + + default: + break; + } + } + break; +#if 0 /* We don't handle this yet */ + case Fac_CallRerouteing: + /* Private-Public ISDN interworking message */ + /* We don't handle this yet */ + break; +#endif /* We don't handle this yet */ + case Fac_DivertingLegInformation1: + /* Private-Public ISDN interworking message */ + bc->div_leg_3_rx_wanted = 0; + if (ch && ch->ast) { + bc->redirecting.reason = + diversion_reason_to_misdn(bc->fac_in.u.DivertingLegInformation1.DiversionReason); + if (bc->fac_in.u.DivertingLegInformation1.DivertedToPresent) { + misdn_PresentedNumberUnscreened_extract(&bc->redirecting.to, + &bc->fac_in.u.DivertingLegInformation1.DivertedTo); + + /* Add configured prefix to redirecting.to.number */ + misdn_add_number_prefix(bc->port, bc->redirecting.to.number_type, + bc->redirecting.to.number, sizeof(bc->redirecting.to.number)); + } else { + bc->redirecting.to.number[0] = '\0'; + bc->redirecting.to.number_plan = NUMPLAN_ISDN; + bc->redirecting.to.number_type = NUMTYPE_UNKNOWN; + bc->redirecting.to.presentation = 1;/* restricted */ + bc->redirecting.to.screening = 0;/* unscreened */ + } + misdn_copy_redirecting_to_ast(ch->ast, &bc->redirecting); + bc->div_leg_3_rx_wanted = 1; + } + break; + case Fac_DivertingLegInformation2: + /* Private-Public ISDN interworking message */ + switch (event) { + case EVENT_SETUP: + /* Comes in on a SETUP with redirecting.from information */ + bc->div_leg_3_tx_pending = 1; + if (ch && ch->ast) { + /* + * Setup the redirecting.to informtion so we can identify + * if the user wants to manually supply the COLR for this + * redirected to number if further redirects could happen. + * + * All the user needs to do is set the REDIRECTING(to-pres) + * to the COLR and REDIRECTING(to-num) = ${EXTEN} to be safe + * after determining that the incoming call was redirected by + * checking if there is a REDIRECTING(from-num). + */ + ast_copy_string(bc->redirecting.to.number, bc->dialed.number, + sizeof(bc->redirecting.to.number)); + bc->redirecting.to.number_plan = bc->dialed.number_plan; + bc->redirecting.to.number_type = bc->dialed.number_type; + bc->redirecting.to.presentation = 1;/* restricted */ + bc->redirecting.to.screening = 0;/* unscreened */ + + bc->redirecting.reason = + diversion_reason_to_misdn(bc->fac_in.u.DivertingLegInformation2.DiversionReason); + bc->redirecting.count = bc->fac_in.u.DivertingLegInformation2.DiversionCounter; + if (bc->fac_in.u.DivertingLegInformation2.DivertingPresent) { + /* This information is redundant if there was a redirecting ie in the SETUP. */ + misdn_PresentedNumberUnscreened_extract(&bc->redirecting.from, + &bc->fac_in.u.DivertingLegInformation2.Diverting); + + /* Add configured prefix to redirecting.from.number */ + misdn_add_number_prefix(bc->port, bc->redirecting.from.number_type, + bc->redirecting.from.number, sizeof(bc->redirecting.from.number)); + } +#if 0 + if (bc->fac_in.u.DivertingLegInformation2.OriginalCalledPresent) { + /* We have no place to put the OriginalCalled number */ + } +#endif + misdn_copy_redirecting_to_ast(ch->ast, &bc->redirecting); + } + break; + default: + chan_misdn_log(0, bc->port," --> Expected in a SETUP message: facility type:0x%04X\n", + bc->fac_in.Function); + break; + } + break; + case Fac_DivertingLegInformation3: + /* Private-Public ISDN interworking message */ + if (bc->div_leg_3_rx_wanted) { + bc->div_leg_3_rx_wanted = 0; + + if (ch && ch->ast) { + ch->ast->redirecting.to.number_presentation = + bc->fac_in.u.DivertingLegInformation3.PresentationAllowedIndicator + ? AST_PRES_ALLOWED | AST_PRES_USER_NUMBER_UNSCREENED + : AST_PRES_RESTRICTED | AST_PRES_USER_NUMBER_UNSCREENED; + ast_channel_queue_redirecting_update(ch->ast, &ch->ast->redirecting); + } + } + break; + +#else /* !defined(AST_MISDN_ENHANCEMENTS) */ + + case Fac_CD: + if (ch && ch->ast) { + ast_copy_string(bc->redirecting.from.number, bc->dialed.number, + sizeof(bc->redirecting.from.number)); + bc->redirecting.from.name[0] = 0; + bc->redirecting.from.number_plan = bc->dialed.number_plan; + bc->redirecting.from.number_type = bc->dialed.number_type; + bc->redirecting.from.screening = 0;/* Unscreened */ + bc->redirecting.from.presentation = + bc->fac_in.u.CDeflection.PresentationAllowed + ? 0 /* Allowed */ : 1 /* Restricted */; + + ast_copy_string(bc->redirecting.to.number, + (char *) bc->fac_in.u.CDeflection.DeflectedToNumber, + sizeof(bc->redirecting.to.number)); + bc->redirecting.to.name[0] = 0; + bc->redirecting.to.number_plan = NUMPLAN_UNKNOWN; + bc->redirecting.to.number_type = NUMTYPE_UNKNOWN; + bc->redirecting.to.presentation = 0;/* Allowed */ + bc->redirecting.to.screening = 0;/* Unscreened */ + + ++bc->redirecting.count; + bc->redirecting.reason = mISDN_REDIRECTING_REASON_DEFLECTION; + + misdn_copy_redirecting_to_ast(ch->ast, &bc->redirecting); + ast_string_field_set(ch->ast, call_forward, bc->redirecting.to.number); + + misdn_lib_send_event(bc, EVENT_DISCONNECT); + + /* This line is BUSY to further attempts by this dialing attempt. */ + ast_queue_control(ch->ast, AST_CONTROL_BUSY); + } + break; +#endif /* !defined(AST_MISDN_ENHANCEMENTS) */ + case Fac_AOCDCurrency: + if (ch && ch->ast) { + bc->AOCDtype = Fac_AOCDCurrency; + memcpy(&bc->AOCD.currency, &bc->fac_in.u.AOCDcur, sizeof(bc->AOCD.currency)); + bc->AOCD_need_export = 1; + export_aoc_vars(ch->originator, ch->ast, bc); + } + break; + case Fac_AOCDChargingUnit: + if (ch && ch->ast) { + bc->AOCDtype = Fac_AOCDChargingUnit; + memcpy(&bc->AOCD.chargingUnit, &bc->fac_in.u.AOCDchu, sizeof(bc->AOCD.chargingUnit)); + bc->AOCD_need_export = 1; + export_aoc_vars(ch->originator, ch->ast, bc); + } + break; +#if defined(AST_MISDN_ENHANCEMENTS) + case Fac_ERROR: + diagnostic_msg = misdn_to_str_error_code(bc->fac_in.u.ERROR.errorValue); + chan_misdn_log(1, bc->port, " --> Facility error code: %s\n", diagnostic_msg); + switch (event) { + case EVENT_DISCONNECT: + case EVENT_RELEASE: + case EVENT_RELEASE_COMPLETE: + /* Possible call failure as a result of Fac_CCBSCall/Fac_CCBS_T_Call */ + if (ch && ch->peer) { + misdn_cc_set_peer_var(ch->peer, MISDN_ERROR_MSG, diagnostic_msg); + } + break; + default: + break; + } + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_invoke(bc->port, bc->fac_in.u.ERROR.invokeId); + if (cc_record) { + cc_record->outstanding_message = 0; + cc_record->error_code = bc->fac_in.u.ERROR.errorValue; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + break; + case Fac_REJECT: + diagnostic_msg = misdn_to_str_reject_code(bc->fac_in.u.REJECT.Code); + chan_misdn_log(1, bc->port, " --> Facility reject code: %s\n", diagnostic_msg); + switch (event) { + case EVENT_DISCONNECT: + case EVENT_RELEASE: + case EVENT_RELEASE_COMPLETE: + /* Possible call failure as a result of Fac_CCBSCall/Fac_CCBS_T_Call */ + if (ch && ch->peer) { + misdn_cc_set_peer_var(ch->peer, MISDN_ERROR_MSG, diagnostic_msg); + } + break; + default: + break; + } + if (bc->fac_in.u.REJECT.InvokeIDPresent) { + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_invoke(bc->port, bc->fac_in.u.REJECT.InvokeID); + if (cc_record) { + cc_record->outstanding_message = 0; + cc_record->reject_code = bc->fac_in.u.REJECT.Code; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + } + break; + case Fac_RESULT: + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_invoke(bc->port, bc->fac_in.u.RESULT.InvokeID); + if (cc_record) { + cc_record->outstanding_message = 0; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + break; +#if 0 /* We don't handle this yet */ + case Fac_EctExecute: + /* We don't handle this yet */ + break; + case Fac_ExplicitEctExecute: + /* We don't handle this yet */ + break; + case Fac_EctLinkIdRequest: + /* We don't handle this yet */ + break; +#endif /* We don't handle this yet */ + case Fac_SubaddressTransfer: + /* We do not have anything to do for this message since we do not handle subaddreses. */ + break; + case Fac_RequestSubaddress: + /* We do not have anything to do for this message since we do not handle subaddreses. */ + break; + case Fac_EctInform: + /* Private-Public ISDN interworking message */ + if (ch && ch->ast && bc->fac_in.u.EctInform.RedirectionPresent) { + /* Add configured prefix to the redirection number */ + memset(&party_id, 0, sizeof(party_id)); + misdn_PresentedNumberUnscreened_extract(&party_id, + &bc->fac_in.u.EctInform.Redirection); + misdn_add_number_prefix(bc->port, party_id.number_type, + party_id.number, sizeof(party_id.number)); + + /* + * It would be preferable to update the connected line information + * only when the message callStatus is active. However, the + * optional redirection number may not be present in the active + * message if an alerting message were received earlier. + * + * The consequences if we wind up sending two updates is benign. + * The other end will think that it got transferred twice. + */ + misdn_queue_connected_line_update(ch->ast, &party_id, + (bc->fac_in.u.EctInform.Status == 0 /* alerting */) + ? AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER_ALERTING + : AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER); + } + break; +#if 0 /* We don't handle this yet */ + case Fac_EctLoopTest: + /* The use of this message is unclear on how it works to detect loops. */ + /* We don't handle this yet */ + break; +#endif /* We don't handle this yet */ + case Fac_CallInfoRetain: + switch (event) { + case EVENT_ALERTING: + case EVENT_DISCONNECT: + /* CCBS/CCNR is available */ + if (ch && ch->peer) { + AST_LIST_LOCK(&misdn_cc_records_db); + if (ch->record_id == -1) { + cc_record = misdn_cc_new(); + } else { + /* + * We are doing a call-completion attempt + * or the switch is sending us extra call-completion + * availability indications (erroneously?). + * + * Assume that the network request retention option + * is not on and that the current call-completion + * request is disabled. + */ + cc_record = misdn_cc_find_by_id(ch->record_id); + if (cc_record) { + if (cc_record->ptp && cc_record->mode.ptp.bc) { + /* + * What? We are getting mixed messages from the + * switch. We are currently setup for + * point-to-point. Now we are switching to + * point-to-multipoint. + * + * Close the call-completion signaling link + */ + cc_record->mode.ptp.bc->fac_out.Function = Fac_None; + cc_record->mode.ptp.bc->out_cause = AST_CAUSE_NORMAL_CLEARING; + misdn_lib_send_event(cc_record->mode.ptp.bc, EVENT_RELEASE_COMPLETE); + } + + /* + * Resetup the existing record for a possible new + * call-completion request. + */ + new_record_id = misdn_cc_record_id_new(); + if (new_record_id < 0) { + /* Looks like we must keep the old id anyway. */ + } else { + cc_record->record_id = new_record_id; + ch->record_id = new_record_id; + } + cc_record->ptp = 0; + cc_record->port = bc->port; + memset(&cc_record->mode, 0, sizeof(cc_record->mode)); + cc_record->mode.ptmp.linkage_id = bc->fac_in.u.CallInfoRetain.CallLinkageID; + cc_record->invoke_id = ++misdn_invoke_id; + cc_record->activated = 0; + cc_record->outstanding_message = 0; + cc_record->activation_requested = 0; + cc_record->error_code = FacError_None; + cc_record->reject_code = FacReject_None; + memset(&cc_record->remote_user_free, 0, sizeof(cc_record->remote_user_free)); + memset(&cc_record->b_free, 0, sizeof(cc_record->b_free)); + cc_record->time_created = time(NULL); + + cc_record = NULL; + } else { + /* + * Where did the record go? We will have to recapture + * the call setup information. Unfortunately, some + * setup information may have been changed. + */ + ch->record_id = -1; + cc_record = misdn_cc_new(); + } + } + if (cc_record) { + ch->record_id = cc_record->record_id; + cc_record->ptp = 0; + cc_record->port = bc->port; + cc_record->mode.ptmp.linkage_id = bc->fac_in.u.CallInfoRetain.CallLinkageID; + + /* Record call information for possible call-completion attempt. */ + cc_record->redial.caller = bc->caller; + cc_record->redial.dialed = bc->dialed; + cc_record->redial.setup_bc_hlc_llc = bc->setup_bc_hlc_llc; + cc_record->redial.capability = bc->capability; + cc_record->redial.hdlc = bc->hdlc; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + + /* Set MISDN_CC_RECORD_ID in original channel */ + if (ch->record_id != -1) { + snprintf(buf, sizeof(buf), "%ld", ch->record_id); + } else { + buf[0] = 0; + } + misdn_cc_set_peer_var(ch->peer, MISDN_CC_RECORD_ID, buf); + } + break; + default: + chan_misdn_log(0, bc->port, + " --> Expected in a DISCONNECT or ALERTING message: facility type:0x%04X\n", + bc->fac_in.Function); + break; + } + break; + case Fac_CCBS_T_Call: + case Fac_CCBSCall: + switch (event) { + case EVENT_SETUP: + /* + * This is a call completion retry call. + * If we had anything to do we would do it here. + */ + break; + default: + chan_misdn_log(0, bc->port, " --> Expected in a SETUP message: facility type:0x%04X\n", + bc->fac_in.Function); + break; + } + break; + case Fac_CCBSDeactivate: + switch (bc->fac_in.u.CCBSDeactivate.ComponentType) { + case FacComponent_Result: + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_invoke(bc->port, bc->fac_in.u.CCBSDeactivate.InvokeID); + if (cc_record) { + cc_record->outstanding_message = 0; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + break; + + default: + chan_misdn_log(0, bc->port, " --> not yet handled: facility type:0x%04X\n", + bc->fac_in.Function); + break; + } + break; + case Fac_CCBSErase: + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_reference(bc->port, bc->fac_in.u.CCBSErase.CCBSReference); + if (cc_record) { + misdn_cc_delete(cc_record); + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + break; + case Fac_CCBSRemoteUserFree: + misdn_cc_handle_remote_user_free(bc->port, &bc->fac_in); + break; + case Fac_CCBSBFree: + misdn_cc_handle_b_free(bc->port, &bc->fac_in); + break; + case Fac_CCBSStatusRequest: + misdn_cc_handle_ccbs_status_request(bc->port, &bc->fac_in); + break; + case Fac_EraseCallLinkageID: + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_linkage(bc->port, + bc->fac_in.u.EraseCallLinkageID.CallLinkageID); + if (cc_record && !cc_record->activation_requested) { + /* + * The T-RETENTION timer expired before we requested + * call completion activation. Call completion is no + * longer available. + */ + misdn_cc_delete(cc_record); + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + break; + case Fac_CCBSStopAlerting: + /* We do not have anything to do for this message. */ + break; + case Fac_CCBSRequest: + case Fac_CCNRRequest: + switch (bc->fac_in.u.CCBSRequest.ComponentType) { + case FacComponent_Result: + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_invoke(bc->port, bc->fac_in.u.CCBSRequest.InvokeID); + if (cc_record && !cc_record->ptp) { + cc_record->outstanding_message = 0; + cc_record->activated = 1; + cc_record->mode.ptmp.recall_mode = bc->fac_in.u.CCBSRequest.Component.Result.RecallMode; + cc_record->mode.ptmp.reference_id = bc->fac_in.u.CCBSRequest.Component.Result.CCBSReference; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + break; + + default: + chan_misdn_log(0, bc->port, " --> not yet handled: facility type:0x%04X\n", + bc->fac_in.Function); + break; + } + break; +#if 0 /* We don't handle this yet */ + case Fac_CCBSInterrogate: + case Fac_CCNRInterrogate: + /* We don't handle this yet */ + break; + case Fac_StatusRequest: + /* We don't handle this yet */ + break; +#endif /* We don't handle this yet */ +#if 0 /* We don't handle this yet */ + case Fac_CCBS_T_Suspend: + case Fac_CCBS_T_Resume: + /* We don't handle this yet */ + break; +#endif /* We don't handle this yet */ + case Fac_CCBS_T_RemoteUserFree: + misdn_cc_handle_T_remote_user_free(bc); + break; + case Fac_CCBS_T_Available: + switch (event) { + case EVENT_ALERTING: + case EVENT_DISCONNECT: + /* CCBS-T/CCNR-T is available */ + if (ch && ch->peer) { + int set_id = 1; + + AST_LIST_LOCK(&misdn_cc_records_db); + if (ch->record_id == -1) { + cc_record = misdn_cc_new(); + } else { + /* + * We are doing a call-completion attempt + * or the switch is sending us extra call-completion + * availability indications (erroneously?). + */ + cc_record = misdn_cc_find_by_id(ch->record_id); + if (cc_record) { + if (cc_record->ptp && cc_record->mode.ptp.retention_enabled) { + /* + * Call-completion is still activated. + * The user does not have to request it again. + */ + chan_misdn_log(1, bc->port, " --> Call-completion request retention option is enabled\n"); + + set_id = 0; + } else { + if (cc_record->ptp && cc_record->mode.ptp.bc) { + /* + * The network request retention option + * is not on and the current call-completion + * request is to be disabled. + * + * We should get here only if EVENT_DISCONNECT + * + * Close the call-completion signaling link + */ + cc_record->mode.ptp.bc->fac_out.Function = Fac_None; + cc_record->mode.ptp.bc->out_cause = AST_CAUSE_NORMAL_CLEARING; + misdn_lib_send_event(cc_record->mode.ptp.bc, EVENT_RELEASE_COMPLETE); + } + + /* + * Resetup the existing record for a possible new + * call-completion request. + */ + new_record_id = misdn_cc_record_id_new(); + if (new_record_id < 0) { + /* Looks like we must keep the old id anyway. */ + } else { + cc_record->record_id = new_record_id; + ch->record_id = new_record_id; + } + cc_record->ptp = 1; + cc_record->port = bc->port; + memset(&cc_record->mode, 0, sizeof(cc_record->mode)); + cc_record->invoke_id = ++misdn_invoke_id; + cc_record->activated = 0; + cc_record->outstanding_message = 0; + cc_record->activation_requested = 0; + cc_record->error_code = FacError_None; + cc_record->reject_code = FacReject_None; + memset(&cc_record->remote_user_free, 0, sizeof(cc_record->remote_user_free)); + memset(&cc_record->b_free, 0, sizeof(cc_record->b_free)); + cc_record->time_created = time(NULL); + } + cc_record = NULL; + } else { + /* + * Where did the record go? We will have to recapture + * the call setup information. Unfortunately, some + * setup information may have been changed. + */ + ch->record_id = -1; + cc_record = misdn_cc_new(); + } + } + if (cc_record) { + ch->record_id = cc_record->record_id; + cc_record->ptp = 1; + cc_record->port = bc->port; + + /* Record call information for possible call-completion attempt. */ + cc_record->redial.caller = bc->caller; + cc_record->redial.dialed = bc->dialed; + cc_record->redial.setup_bc_hlc_llc = bc->setup_bc_hlc_llc; + cc_record->redial.capability = bc->capability; + cc_record->redial.hdlc = bc->hdlc; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + + /* Set MISDN_CC_RECORD_ID in original channel */ + if (ch->record_id != -1 && set_id) { + snprintf(buf, sizeof(buf), "%ld", ch->record_id); + } else { + buf[0] = 0; + } + misdn_cc_set_peer_var(ch->peer, MISDN_CC_RECORD_ID, buf); + } + break; + default: + chan_misdn_log(0, bc->port, + " --> Expected in a DISCONNECT or ALERTING message: facility type:0x%04X\n", + bc->fac_in.Function); + break; + } + break; + case Fac_CCBS_T_Request: + case Fac_CCNR_T_Request: + switch (bc->fac_in.u.CCBS_T_Request.ComponentType) { + case FacComponent_Result: + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_invoke(bc->port, bc->fac_in.u.CCBS_T_Request.InvokeID); + if (cc_record && cc_record->ptp) { + cc_record->outstanding_message = 0; + cc_record->activated = 1; + cc_record->mode.ptp.retention_enabled = + cc_record->mode.ptp.requested_retention + ? bc->fac_in.u.CCBS_T_Request.Component.Result.RetentionSupported + ? 1 : 0 + : 0; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + break; + + case FacComponent_Invoke: + /* We cannot be User-B in ptp mode. */ + default: + chan_misdn_log(0, bc->port, " --> not yet handled: facility type:0x%04X\n", + bc->fac_in.Function); + break; + } + break; + +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + case Fac_None: + break; + default: + chan_misdn_log(0, bc->port, " --> not yet handled: facility type:0x%04X\n", + bc->fac_in.Function); + break; + } +} + /************************************************************/ /* Receive Events from isdn_lib here */ /************************************************************/ static enum event_response_e cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) { +#if defined(AST_MISDN_ENHANCEMENTS) + struct misdn_cc_record *cc_record; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ struct chan_list *ch = find_chan_by_bc(cl_te, bc); - - if (event != EVENT_BCHAN_DATA && event != EVENT_TONE_GENERATE) { /* Debug Only Non-Bchan */ + + if (event != EVENT_BCHAN_DATA && event != EVENT_TONE_GENERATE) { int debuglevel = 1; - if ( event == EVENT_CLEANUP && !user_data) { + + /* Debug Only Non-Bchan */ + if (event == EVENT_CLEANUP && !user_data) { debuglevel = 5; } - chan_misdn_log(debuglevel, bc->port, "I IND :%s oad:%s dad:%s pid:%d state:%s\n", manager_isdn_get_info(event), bc->oad, bc->dad, bc->pid, ch ? misdn_get_ch_state(ch) : "none"); + chan_misdn_log(debuglevel, bc->port, + "I IND :%s caller:\"%s\" <%s> dialed:%s pid:%d state:%s\n", + manager_isdn_get_info(event), + bc->caller.name, + bc->caller.number, + bc->dialed.number, + bc->pid, + ch ? misdn_get_ch_state(ch) : "none"); if (debuglevel == 1) { misdn_lib_log_ies(bc); chan_misdn_log(4, bc->port, " --> bc_state:%s\n", bc_state2str(bc->bc_state)); } } - + if (!ch) { switch(event) { case EVENT_SETUP: @@ -4290,6 +9334,7 @@ case EVENT_RETRIEVE: case EVENT_NEW_BC: case EVENT_FACILITY: + case EVENT_REGISTER: break; case EVENT_RELEASE_COMPLETE: chan_misdn_log(1, bc->port, " --> no Ch, so we've already released.\n"); @@ -4303,7 +9348,7 @@ return -1; } } - + if (ch) { switch (event) { case EVENT_TONE_GENERATE: @@ -4318,16 +9363,17 @@ } break; default: - if (!ch->ast || !MISDN_ASTERISK_PVT(ch->ast) || !MISDN_ASTERISK_TECH_PVT(ch->ast)) { + if (!ch->ast || !MISDN_ASTERISK_TECH_PVT(ch->ast)) { if (event != EVENT_BCHAN_DATA) { ast_log(LOG_NOTICE, "No Ast or No private Pointer in Event (%d:%s)\n", event, manager_isdn_get_info(event)); } return -1; } + break; } } - - + + switch (event) { case EVENT_PORT_ALARM: { @@ -4335,17 +9381,17 @@ misdn_cfg_get(bc->port, MISDN_CFG_ALARM_BLOCK, &boa, sizeof(boa)); if (boa) { cb_log(1, bc->port, " --> blocking\n"); - misdn_lib_port_block(bc->port); + misdn_lib_port_block(bc->port); } } break; case EVENT_BCHAN_ACTIVATED: break; - + case EVENT_NEW_CHANNEL: update_name(ch->ast,bc->port,bc->channel); break; - + case EVENT_NEW_L3ID: ch->l3id=bc->l3_id; ch->addr=bc->addr; @@ -4355,17 +9401,17 @@ if (!ch) { ch = find_holded(cl_te,bc); } - + if (!ch) { ast_log(LOG_WARNING, "NEW_BC without chan_list?\n"); break; } if (bc) { - ch->bc = (struct misdn_bchannel *)user_data; + ch->bc = (struct misdn_bchannel *) user_data; } break; - + case EVENT_DTMF_TONE: { /* sending INFOS as DTMF-Frames :) */ @@ -4381,56 +9427,54 @@ fr.mallocd = 0; fr.offset = 0; fr.delivery = ast_tv(0,0); - + if (!ch->ignore_dtmf) { chan_misdn_log(2, bc->port, " --> DTMF:%c\n", bc->dtmf); ast_queue_frame(ch->ast, &fr); } else { chan_misdn_log(2, bc->port, " --> Ignoring DTMF:%c due to bridge flags\n", bc->dtmf); } + break; } - break; case EVENT_STATUS: break; - + case EVENT_INFORMATION: if (ch->state != MISDN_CONNECTED) { stop_indicate(ch); } - + if (!ch->ast) { break; } - if (ch->state == MISDN_WAITING4DIGS ) { + if (ch->state == MISDN_WAITING4DIGS) { /* Ok, incomplete Setup, waiting till extension exists */ if (ast_strlen_zero(bc->info_dad) && ! ast_strlen_zero(bc->keypad)) { chan_misdn_log(1, bc->port, " --> using keypad as info\n"); ast_copy_string(bc->info_dad, bc->keypad, sizeof(bc->info_dad)); } - strncat(bc->dad, bc->info_dad, sizeof(bc->dad) - strlen(bc->dad) - 1); - ast_copy_string(ch->ast->exten, bc->dad, sizeof(ch->ast->exten)); + strncat(bc->dialed.number, bc->info_dad, sizeof(bc->dialed.number) - strlen(bc->dialed.number) - 1); + ast_copy_string(ch->ast->exten, bc->dialed.number, sizeof(ch->ast->exten)); /* Check for Pickup Request first */ if (!strcmp(ch->ast->exten, ast_pickup_ext())) { if (ast_pickup_call(ch->ast)) { hangup_chan(ch); } else { - struct ast_channel *chan = ch->ast; ch->state = MISDN_CALLING_ACKNOWLEDGE; - ast_setstate(chan, AST_STATE_DOWN); hangup_chan(ch); ch->ast = NULL; break; } } - - if (!ast_canmatch_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) { - if (ast_exists_extension(ch->ast, ch->context, "i", 1, bc->oad)) { + + if (!ast_canmatch_extension(ch->ast, ch->context, bc->dialed.number, 1, bc->caller.number)) { + if (ast_exists_extension(ch->ast, ch->context, "i", 1, bc->caller.number)) { ast_log(LOG_WARNING, "Extension '%s@%s' can never match. Jumping to 'i' extension. port:%d\n", - bc->dad, ch->context, bc->port); + bc->dialed.number, ch->context, bc->port); strcpy(ch->ast->exten, "i"); ch->state = MISDN_DIALING; @@ -4441,7 +9485,7 @@ ast_log(LOG_WARNING, "Extension '%s@%s' can never match. Disconnecting. port:%d\n" "\tMaybe you want to add an 'i' extension to catch this case.\n", - bc->dad, ch->context, bc->port); + bc->dialed.number, ch->context, bc->port); if (bc->nt) { hanguptone_indicate(ch); @@ -4458,13 +9502,13 @@ ch->overlap_tv = ast_tvnow(); ast_mutex_unlock(&ch->overlap_tv_lock); if (ch->overlap_dial_task == -1) { - ch->overlap_dial_task = + ch->overlap_dial_task = misdn_tasks_add_variable(ch->overlap_dial, misdn_overlap_dial_task, ch); } break; } - if (ast_exists_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) { + if (ast_exists_extension(ch->ast, ch->context, bc->dialed.number, 1, bc->caller.number)) { ch->state = MISDN_DIALING; start_pbx(ch, bc, ch->ast); } @@ -4485,13 +9529,13 @@ fr.delivery = ast_tv(0,0); misdn_cfg_get(0, MISDN_GEN_APPEND_DIGITS2EXTEN, &digits, sizeof(digits)); - if (ch->state != MISDN_CONNECTED ) { + if (ch->state != MISDN_CONNECTED) { if (digits) { - strncat(bc->dad, bc->info_dad, sizeof(bc->dad) - strlen(bc->dad) - 1); - ast_copy_string(ch->ast->exten, bc->dad, sizeof(ch->ast->exten)); + strncat(bc->dialed.number, bc->info_dad, sizeof(bc->dialed.number) - strlen(bc->dialed.number) - 1); + ast_copy_string(ch->ast->exten, bc->dialed.number, sizeof(ch->ast->exten)); ast_cdr_update(ch->ast); } - + ast_queue_frame(ch->ast, &fr); } } @@ -4499,10 +9543,9 @@ case EVENT_SETUP: { struct chan_list *ch = find_chan_by_bc(cl_te, bc); - int msn_valid = misdn_cfg_is_msn_valid(bc->port, bc->dad); + int msn_valid = misdn_cfg_is_msn_valid(bc->port, bc->dialed.number); struct ast_channel *chan; int exceed; - int pres, screen; int ai; int im; @@ -4533,7 +9576,6 @@ print_bearer(bc); ch = init_chan_list(ORG_MISDN); - if (!ch) { chan_misdn_log(-1, bc->port, "cb_events: malloc for chan_list failed!\n"); return 0; @@ -4542,13 +9584,11 @@ ch->bc = bc; ch->l3id = bc->l3_id; ch->addr = bc->addr; - ch->originator = ORG_MISDN; - chan = misdn_new(ch, AST_STATE_RESERVED, bc->dad, bc->oad, AST_FORMAT_ALAW, bc->port, bc->channel); - + chan = misdn_new(ch, AST_STATE_RESERVED, bc->dialed.number, bc->caller.number, AST_FORMAT_ALAW, bc->port, bc->channel); if (!chan) { misdn_lib_send_event(bc,EVENT_RELEASE_COMPLETE); - ast_log(LOG_ERROR, "cb_events: misdn_new failed !\n"); + ast_log(LOG_ERROR, "cb_events: misdn_new failed !\n"); return 0; } @@ -4560,50 +9600,34 @@ pbx_builtin_setvar_helper(chan, "MAX_OVERFLOW", tmp); } - read_config(ch, ORG_MISDN); + read_config(ch); export_ch(chan, bc, ch); ch->ast->rings = 1; ast_setstate(ch->ast, AST_STATE_RINGING); - switch (bc->pres) { - case 1: - pres = AST_PRES_RESTRICTED; - chan_misdn_log(2, bc->port, " --> PRES: Restricted (1)\n"); - break; - case 2: - pres = AST_PRES_UNAVAILABLE; - chan_misdn_log(2, bc->port, " --> PRES: Unavailable (2)\n"); - break; - default: - pres = AST_PRES_ALLOWED; - chan_misdn_log(2, bc->port, " --> PRES: Allowed (%d)\n", bc->pres); - break; - } + /* Update asterisk channel caller information */ + chan_misdn_log(2, bc->port, " --> TON: %s(%d)\n", misdn_to_str_ton(bc->caller.number_type), bc->caller.number_type); + chan_misdn_log(2, bc->port, " --> PLAN: %s(%d)\n", misdn_to_str_plan(bc->caller.number_plan), bc->caller.number_plan); + chan->cid.cid_ton = misdn_to_ast_ton(bc->caller.number_type) + | misdn_to_ast_plan(bc->caller.number_plan); - switch (bc->screen) { - default: - case 0: - screen = AST_PRES_USER_NUMBER_UNSCREENED; - chan_misdn_log(2, bc->port, " --> SCREEN: Unscreened (%d)\n", bc->screen); - break; - case 1: - screen = AST_PRES_USER_NUMBER_PASSED_SCREEN; - chan_misdn_log(2, bc->port, " --> SCREEN: Passed screen (1)\n"); - break; - case 2: - screen = AST_PRES_USER_NUMBER_FAILED_SCREEN; - chan_misdn_log(2, bc->port, " --> SCREEN: failed screen (2)\n"); - break; - case 3: - screen = AST_PRES_NETWORK_NUMBER; - chan_misdn_log(2, bc->port, " --> SCREEN: Network Number (3)\n"); - break; + chan_misdn_log(2, bc->port, " --> PRES: %s(%d)\n", misdn_to_str_pres(bc->caller.presentation), bc->caller.presentation); + chan_misdn_log(2, bc->port, " --> SCREEN: %s(%d)\n", misdn_to_str_screen(bc->caller.screening), bc->caller.screening); + chan->cid.cid_pres = misdn_to_ast_pres(bc->caller.presentation) + | misdn_to_ast_screen(bc->caller.screening); + + ast_set_callerid(chan, bc->caller.number, NULL, bc->caller.number); + + if (!ast_strlen_zero(bc->redirecting.from.number)) { + /* Add configured prefix to redirecting.from.number */ + misdn_add_number_prefix(bc->port, bc->redirecting.from.number_type, bc->redirecting.from.number, sizeof(bc->redirecting.from.number)); + + /* Update asterisk channel redirecting information */ + misdn_copy_redirecting_to_ast(chan, &bc->redirecting); } - chan->cid.cid_pres = pres | screen; - pbx_builtin_setvar_helper(chan, "TRANSFERCAPABILITY", ast_transfercapability2str(bc->capability)); chan->transfercapability = bc->capability; @@ -4613,6 +9637,7 @@ break; default: pbx_builtin_setvar_helper(chan, "CALLTYPE", "SPEECH"); + break; } /** queue new chan **/ @@ -4632,7 +9657,7 @@ break; } } - } /* end for */ + } if (i == ARRAY_LEN(allowed_bearers_array)) { /* We did not find the bearer capability */ chan_misdn_log(0, bc->port, "Bearer capability not allowed: %s(%d)\n", @@ -4645,11 +9670,15 @@ } } + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, ch); + } + /* Check for Pickup Request first */ if (!strcmp(chan->exten, ast_pickup_ext())) { if (!ch->noautorespond_on_setup) { - int ret;/** Sending SETUP_ACK**/ - ret = misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE ); + /* Sending SETUP_ACK */ + misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE); } else { ch->state = MISDN_INCOMING_SETUP; } @@ -4657,7 +9686,6 @@ hangup_chan(ch); } else { ch->state = MISDN_CALLING_ACKNOWLEDGE; - ast_setstate(chan, AST_STATE_DOWN); hangup_chan(ch); ch->ast = NULL; break; @@ -4674,19 +9702,19 @@ break; } - /* check if we should jump into s when we have no dad */ + /* check if we should jump into s when we have no dialed.number */ misdn_cfg_get(bc->port, MISDN_CFG_IMMEDIATE, &im, sizeof(im)); - if (im && ast_strlen_zero(bc->dad)) { + if (im && ast_strlen_zero(bc->dialed.number)) { do_immediate_setup(bc, ch, chan); break; } chan_misdn_log(5, bc->port, "CONTEXT:%s\n", ch->context); - if (!ast_canmatch_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) { - if (ast_exists_extension(ch->ast, ch->context, "i", 1, bc->oad)) { + if (!ast_canmatch_extension(ch->ast, ch->context, bc->dialed.number, 1, bc->caller.number)) { + if (ast_exists_extension(ch->ast, ch->context, "i", 1, bc->caller.number)) { ast_log(LOG_WARNING, "Extension '%s@%s' can never match. Jumping to 'i' extension. port:%d\n", - bc->dad, ch->context, bc->port); + bc->dialed.number, ch->context, bc->port); strcpy(ch->ast->exten, "i"); misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE); ch->state = MISDN_DIALING; @@ -4697,7 +9725,7 @@ ast_log(LOG_WARNING, "Extension '%s@%s' can never match. Disconnecting. port:%d\n" "\tMaybe you want to add an 'i' extension to catch this case.\n", - bc->dad, ch->context, bc->port); + bc->dialed.number, ch->context, bc->port); if (bc->nt) { hanguptone_indicate(ch); } @@ -4706,17 +9734,16 @@ bc->out_cause = AST_CAUSE_UNALLOCATED; misdn_lib_send_event(bc, bc->nt ? EVENT_RELEASE_COMPLETE : EVENT_RELEASE); - break; } - /* Whatever happens, when sending_complete is set or we are PTMP TE, we will definitely - * jump into the dialplan, when the dialed extension does not exist, the 's' extension + /* Whatever happens, when sending_complete is set or we are PTMP TE, we will definitely + * jump into the dialplan, when the dialed extension does not exist, the 's' extension * will be used by Asterisk automatically. */ if (bc->sending_complete || (!bc->nt && !misdn_lib_is_ptp(bc->port))) { if (!ch->noautorespond_on_setup) { ch->state=MISDN_DIALING; - misdn_lib_send_event(bc, EVENT_PROCEEDING ); + misdn_lib_send_event(bc, EVENT_PROCEEDING); } else { ch->state = MISDN_INCOMING_SETUP; } @@ -4726,17 +9753,17 @@ /* - * When we are NT and overlapdial is set and if + * When we are NT and overlapdial is set and if * the number is empty, we wait for the ISDN timeout * instead of our own timer. */ - if (ch->overlap_dial && bc->nt && !bc->dad[0] ) { + if (ch->overlap_dial && bc->nt && !bc->dialed.number[0]) { wait_for_digits(ch, bc, chan); break; } - /* - * If overlapdial we will definitely send a SETUP_ACKNOWLEDGE and wait for more + /* + * If overlapdial we will definitely send a SETUP_ACKNOWLEDGE and wait for more * Infos with a Interdigit Timeout. * */ if (ch->overlap_dial) { @@ -4746,16 +9773,16 @@ wait_for_digits(ch, bc, chan); if (ch->overlap_dial_task == -1) { - ch->overlap_dial_task = + ch->overlap_dial_task = misdn_tasks_add_variable(ch->overlap_dial, misdn_overlap_dial_task, ch); } break; } - /* If the extension does not exist and we're not TE_PTMP we wait for more digits + /* If the extension does not exist and we're not TE_PTMP we wait for more digits * without interdigit timeout. * */ - if (!ast_exists_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) { + if (!ast_exists_extension(ch->ast, ch->context, bc->dialed.number, 1, bc->caller.number)) { wait_for_digits(ch, bc, chan); break; } @@ -4763,29 +9790,50 @@ /* * If the extension exists let's just jump into it. * */ - if (ast_exists_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) { + if (ast_exists_extension(ch->ast, ch->context, bc->dialed.number, 1, bc->caller.number)) { misdn_lib_send_event(bc, bc->need_more_infos ? EVENT_SETUP_ACKNOWLEDGE : EVENT_PROCEEDING); ch->state = MISDN_DIALING; start_pbx(ch, bc, chan); break; } + break; } +#if defined(AST_MISDN_ENHANCEMENTS) + case EVENT_REGISTER: + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, ch); + } + /* + * Shut down this connection immediately. + * The current design of chan_misdn data structures + * does not allow the proper handling of inbound call records + * without an assigned B channel. Therefore, we cannot + * be the CCBS User-B party in a point-to-point setup. + */ + bc->fac_out.Function = Fac_None; + bc->out_cause = AST_CAUSE_NORMAL_CLEARING; + misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE); break; - +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ case EVENT_SETUP_ACKNOWLEDGE: ch->state = MISDN_CALLING_ACKNOWLEDGE; - if (bc->channel) + if (bc->channel) { update_name(ch->ast,bc->port,bc->channel); - + } + + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, ch); + } + if (!ast_strlen_zero(bc->infos_pending)) { /* TX Pending Infos */ - strncat(bc->dad, bc->infos_pending, sizeof(bc->dad) - strlen(bc->dad) - 1); + strncat(bc->dialed.number, bc->infos_pending, sizeof(bc->dialed.number) - strlen(bc->dialed.number) - 1); if (!ch->ast) { break; } - ast_copy_string(ch->ast->exten, bc->dad, sizeof(ch->ast->exten)); + ast_copy_string(ch->ast->exten, bc->dialed.number, sizeof(ch->ast->exten)); ast_copy_string(bc->info_dad, bc->infos_pending, sizeof(bc->info_dad)); ast_copy_string(bc->infos_pending, "", sizeof(bc->infos_pending)); @@ -4794,12 +9842,16 @@ break; case EVENT_PROCEEDING: if (misdn_cap_is_speech(bc->capability) && - misdn_inband_avail(bc) ) { + misdn_inband_avail(bc)) { start_bc_tones(ch); } ch->state = MISDN_PROCEEDING; - + + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, ch); + } + if (!ch->ast) { break; } @@ -4811,12 +9863,16 @@ update_name(ch->ast, bc->port, bc->channel); } - if (!bc->nt ) { + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, ch); + } + + if (!bc->nt) { if (misdn_cap_is_speech(bc->capability) && misdn_inband_avail(bc)) { start_bc_tones(ch); } - + ch->state = MISDN_PROGRESS; if (!ch->ast) { @@ -4827,16 +9883,20 @@ break; case EVENT_ALERTING: ch->state = MISDN_ALERTING; - + if (!ch->ast) { break; } + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, ch); + } + ast_queue_control(ch->ast, AST_CONTROL_RINGING); ast_setstate(ch->ast, AST_STATE_RINGING); - + cb_log(7, bc->port, " --> Set State Ringing\n"); - + if (misdn_cap_is_speech(bc->capability) && misdn_inband_avail(bc)) { cb_log(1, bc->port, "Starting Tones, we have inband Data\n"); start_bc_tones(ch); @@ -4850,36 +9910,73 @@ } break; case EVENT_CONNECT: - { - struct ast_channel *bridged; + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, ch); + } +#if defined(AST_MISDN_ENHANCEMENTS) + if (bc->div_leg_3_rx_wanted) { + bc->div_leg_3_rx_wanted = 0; - /*we answer when we've got our very new L3 ID from the NT stack */ - misdn_lib_send_event(bc, EVENT_CONNECT_ACKNOWLEDGE); - - if (!ch->ast) { - break; + if (ch->ast) { + ch->ast->redirecting.to.number_presentation = + AST_PRES_RESTRICTED | AST_PRES_USER_NUMBER_UNSCREENED; + ast_channel_queue_redirecting_update(ch->ast, &ch->ast->redirecting); } + } +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ - bridged = ast_bridged_channel(ch->ast); - stop_indicate(ch); + /* we answer when we've got our very new L3 ID from the NT stack */ + misdn_lib_send_event(bc, EVENT_CONNECT_ACKNOWLEDGE); - if (bridged && !strcasecmp(bridged->tech->type, "mISDN")) { - struct chan_list *bridged_ch = MISDN_ASTERISK_TECH_PVT(bridged); + if (!ch->ast) { + break; + } - chan_misdn_log(1, bc->port, " --> copying cpndialplan:%d and cad:%s to the A-Channel\n", bc->cpnnumplan, bc->cad); - if (bridged_ch) { - bridged_ch->bc->cpnnumplan = bc->cpnnumplan; - ast_copy_string(bridged_ch->bc->cad, bc->cad, sizeof(bridged_ch->bc->cad)); + stop_indicate(ch); + +#if defined(AST_MISDN_ENHANCEMENTS) + if (ch->record_id != -1) { + /* + * We will delete the associated call completion + * record since we now have a completed call. + * We will not wait/depend on the network to tell + * us to delete it. + */ + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_id(ch->record_id); + if (cc_record) { + if (cc_record->ptp && cc_record->mode.ptp.bc) { + /* Close the call-completion signaling link */ + cc_record->mode.ptp.bc->fac_out.Function = Fac_None; + cc_record->mode.ptp.bc->out_cause = AST_CAUSE_NORMAL_CLEARING; + misdn_lib_send_event(cc_record->mode.ptp.bc, EVENT_RELEASE_COMPLETE); } + misdn_cc_delete(cc_record); } + AST_LIST_UNLOCK(&misdn_cc_records_db); + ch->record_id = -1; + if (ch->peer) { + misdn_cc_set_peer_var(ch->peer, MISDN_CC_RECORD_ID, ""); + + ao2_ref(ch->peer, -1); + ch->peer = NULL; + } } +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + + /* Add configured prefix to connected.number */ + misdn_add_number_prefix(bc->port, bc->connected.number_type, bc->connected.number, sizeof(bc->connected.number)); + + /* Update the connected line information on the other channel */ + misdn_queue_connected_line_update(ch->ast, &bc->connected, AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER); + ch->l3id = bc->l3_id; ch->addr = bc->addr; start_bc_tones(ch); - + ch->state = MISDN_CONNECTED; - + ast_queue_control(ch->ast, AST_CONTROL_ANSWER); break; case EVENT_CONNECT_ACKNOWLEDGE: @@ -4895,6 +9992,10 @@ if (ch) { struct chan_list *holded_ch = find_holded(cl_te, bc); + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, ch); + } + chan_misdn_log(3, bc->port, " --> org:%d nt:%d, inbandavail:%d state:%d\n", ch->originator, bc->nt, misdn_inband_avail(bc), ch->state); if (ch->originator == ORG_AST && !bc->nt && misdn_inband_avail(bc) && ch->state != MISDN_CONNECTED) { /* If there's inband information available (e.g. a @@ -4903,7 +10004,7 @@ alternative number, then play it instead of immediately releasing the call */ chan_misdn_log(1, bc->port, " --> Inband Info Avail, not sending RELEASE\n"); - + ch->state = MISDN_DISCONNECTED; start_bc_tones(ch); @@ -4941,6 +10042,10 @@ } break; case EVENT_RELEASE: + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, ch); + } + bc->need_disconnect = 0; bc->need_release = 0; @@ -4948,22 +10053,41 @@ release_chan(bc); break; case EVENT_RELEASE_COMPLETE: + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, ch); + } + bc->need_disconnect = 0; bc->need_release = 0; bc->need_release_complete = 0; - stop_bc_tones(ch); - hangup_chan(ch); - - if (ch) + if (ch) { + stop_bc_tones(ch); + hangup_chan(ch); ch->state = MISDN_CLEANING; +#if defined(AST_MISDN_ENHANCEMENTS) + } else { + /* + * A call-completion signaling link established with + * REGISTER does not have a struct chan_list record + * associated with it. + */ + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_bc(bc); + if (cc_record) { + /* The call-completion signaling link is closed. */ + misdn_cc_delete(cc_record); + } + AST_LIST_UNLOCK(&misdn_cc_records_db); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + } release_chan(bc); break; case EVENT_BCHAN_ERROR: case EVENT_CLEANUP: stop_bc_tones(ch); - + switch (ch->state) { case MISDN_CALLING: bc->cause = AST_CAUSE_DESTINATION_OUT_OF_ORDER; @@ -4971,7 +10095,7 @@ default: break; } - + hangup_chan(ch); release_chan(bc); break; @@ -4997,30 +10121,30 @@ ast->generatordata = NULL; generate = ast->generator->generate; - if (tone_len < 0 || tone_len > 512 ) { + if (tone_len < 0 || tone_len > 512) { ast_log(LOG_NOTICE, "TONE_GEN: len was %d, set to 128\n", tone_len); tone_len = 128; } res = generate(ast, tmp, tone_len, tone_len); ast->generatordata = tmp; - + if (res) { ast_log(LOG_WARNING, "Auto-deactivating generator\n"); ast_deactivate_generator(ast); } else { bc->tone_cnt = 0; } + break; } - break; - case EVENT_BCHAN_DATA: if (ch->bc->AOCD_need_export) { export_aoc_vars(ch->originator, ch->ast, ch->bc); } if (!misdn_cap_is_speech(ch->bc->capability)) { struct ast_frame frame; - /*In Data Modes we queue frames*/ + + /* In Data Modes we queue frames */ frame.frametype = AST_FRAME_VOICE; /* we have no data frames yet */ frame.subclass = AST_FORMAT_ALAW; frame.datalen = bc->bframe_len; @@ -5031,8 +10155,9 @@ frame.src = NULL; frame.data.ptr = bc->bframe; - if (ch->ast) + if (ch->ast) { ast_queue_frame(ch->ast, &frame); + } } else { fd_set wrfs; struct timeval tv = { 0, 0 }; @@ -5042,17 +10167,16 @@ FD_SET(ch->pipe[1], &wrfs); t = select(FD_SETSIZE, NULL, &wrfs, NULL, &tv); - if (!t) { chan_misdn_log(9, bc->port, "Select Timed out\n"); break; } - + if (t < 0) { chan_misdn_log(-1, bc->port, "Select Error (err=%s)\n", strerror(errno)); break; } - + if (FD_ISSET(ch->pipe[1], &wrfs)) { chan_misdn_log(9, bc->port, "writing %d bytes to asterisk\n", bc->bframe_len); if (write(ch->pipe[1], bc->bframe, bc->bframe_len) <= 0) { @@ -5102,11 +10226,12 @@ misdn_lib_send_event(bc, EVENT_RELEASE); } break; - case MISDN_CLEANING: + case MISDN_CLEANING: chan_misdn_log(1, bc->port, " --> in state cleaning .. so ignoring, the stack should clean it for us\n"); break; default: misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE); + break; } break; @@ -5140,13 +10265,13 @@ if (hold_ast) { ast_moh_stop(hold_ast); } - + if (misdn_lib_send_event(bc, EVENT_RETRIEVE_ACKNOWLEDGE) < 0) { chan_misdn_log(4, bc->port, " --> RETRIEVE_ACK failed\n"); misdn_lib_send_event(bc, EVENT_RETRIEVE_REJECT); } + break; } - break; case EVENT_HOLD: { int hold_allowed; @@ -5164,7 +10289,7 @@ chan_misdn_log(2, bc->port, "Bridge Partner is of type: %s\n", bridged->tech->type); ch->state = MISDN_HOLDED; ch->l3id = bc->l3_id; - + misdn_lib_send_event(bc, EVENT_HOLD_ACKNOWLEDGE); /* XXX This should queue an AST_CONTROL_HOLD frame on this channel @@ -5179,61 +10304,80 @@ misdn_lib_send_event(bc, EVENT_HOLD_REJECT); chan_misdn_log(0, bc->port, "We aren't bridged to anybody\n"); } - } break; - case EVENT_FACILITY: - print_facility(&(bc->fac_in), bc); - - switch (bc->fac_in.Function) { -#ifdef HAVE_MISDN_FAC_RESULT - case Fac_RESULT: + } + case EVENT_NOTIFY: + if (bc->redirecting.to_changed) { + /* Add configured prefix to redirecting.to.number */ + misdn_add_number_prefix(bc->port, bc->redirecting.to.number_type, + bc->redirecting.to.number, sizeof(bc->redirecting.to.number)); + } + switch (bc->notify_description_code) { + case mISDN_NOTIFY_CODE_DIVERSION_ACTIVATED: + /* Ignore for now. */ + bc->redirecting.to_changed = 0; break; -#endif - case Fac_CD: - if (ch) { - struct ast_channel *bridged = ast_bridged_channel(ch->ast); - struct chan_list *ch_br; - if (bridged && MISDN_ASTERISK_TECH_PVT(bridged)) { - ch_br = MISDN_ASTERISK_TECH_PVT(bridged); - /*ch->state = MISDN_FACILITY_DEFLECTED;*/ - if (ch_br->bc) { - if (ast_exists_extension(bridged, ch->context, (char *)bc->fac_in.u.CDeflection.DeflectedToNumber, 1, bc->oad)) { - ch_br->state = MISDN_DIALING; - if (pbx_start_chan(ch_br) < 0) { - chan_misdn_log(-1, ch_br->bc->port, "ast_pbx_start returned < 0 in misdn_overlap_dial_task\n"); - } - } + case mISDN_NOTIFY_CODE_CALL_IS_DIVERTING: + if (bc->redirecting.to_changed) { + bc->redirecting.to_changed = 0; + if (ch && ch->ast) { + switch (ch->state) { + case MISDN_ALERTING: + /* Call is deflecting after we have seen an ALERTING message */ + bc->redirecting.reason = mISDN_REDIRECTING_REASON_NO_REPLY; + break; + default: + /* Call is deflecting for call forwarding unconditional or busy reason. */ + bc->redirecting.reason = mISDN_REDIRECTING_REASON_UNKNOWN; + break; } + misdn_copy_redirecting_to_ast(ch->ast, &bc->redirecting); + ast_channel_queue_redirecting_update(ch->ast, &ch->ast->redirecting); } - misdn_lib_send_event(bc, EVENT_DISCONNECT); - } + } break; - case Fac_AOCDCurrency: - if (ch) { - bc->AOCDtype = Fac_AOCDCurrency; - memcpy(&(bc->AOCD.currency), &(bc->fac_in.u.AOCDcur), sizeof(bc->AOCD.currency)); - bc->AOCD_need_export = 1; - export_aoc_vars(ch->originator, ch->ast, bc); + case mISDN_NOTIFY_CODE_CALL_TRANSFER_ALERTING: + /* + * It would be preferable to update the connected line information + * only when the message callStatus is active. However, the + * optional redirection number may not be present in the active + * message if an alerting message were received earlier. + * + * The consequences if we wind up sending two updates is benign. + * The other end will think that it got transferred twice. + */ + if (bc->redirecting.to_changed) { + bc->redirecting.to_changed = 0; + if (ch && ch->ast) { + misdn_queue_connected_line_update(ch->ast, &bc->redirecting.to, + AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER_ALERTING); + } } break; - case Fac_AOCDChargingUnit: - if (ch) { - bc->AOCDtype = Fac_AOCDChargingUnit; - memcpy(&(bc->AOCD.chargingUnit), &(bc->fac_in.u.AOCDchu), sizeof(bc->AOCD.chargingUnit)); - bc->AOCD_need_export = 1; - export_aoc_vars(ch->originator, ch->ast, bc); + case mISDN_NOTIFY_CODE_CALL_TRANSFER_ACTIVE: + if (bc->redirecting.to_changed) { + bc->redirecting.to_changed = 0; + if (ch && ch->ast) { + misdn_queue_connected_line_update(ch->ast, &bc->redirecting.to, + AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER); + } } break; - case Fac_None: -#ifdef HAVE_MISDN_FAC_ERROR - case Fac_ERROR: -#endif + default: + bc->redirecting.to_changed = 0; + chan_misdn_log(0, bc->port," --> not yet handled: notify code:0x%02X\n", + bc->notify_description_code); break; - default: - chan_misdn_log(0, bc->port," --> not yet handled: facility type:%d\n", bc->fac_in.Function); } - break; + case EVENT_FACILITY: + if (bc->fac_in.Function == Fac_None) { + /* This is a FACILITY message so we MUST have a facility ie */ + chan_misdn_log(0, bc->port," --> Missing facility ie or unknown facility ie contents.\n"); + } else { + misdn_facility_ie_handler(event, bc, ch); + } + break; case EVENT_RESTART: if (!bc->dummy) { stop_bc_tones(ch); @@ -5244,12 +10388,140 @@ chan_misdn_log(1, 0, "Got Unknown Event\n"); break; } - + return RESPONSE_OK; } /** TE STUFF END **/ +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Get call completion record information. + * + * \param chan Asterisk channel to operate upon. (Not used) + * \param function_name Name of the function that called us. + * \param function_args Argument string passed to function (Could be NULL) + * \param buf Buffer to put returned string. + * \param size Size of the supplied buffer including the null terminator. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int misdn_cc_read(struct ast_channel *chan, const char *function_name, + char *function_args, char *buf, size_t size) +{ + char *parse; + struct misdn_cc_record *cc_record; + + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(cc_id); /* Call completion record ID value. */ + AST_APP_ARG(get_name); /* Name of what to get */ + AST_APP_ARG(other); /* Any extraneous garbage arguments */ + ); + + /* Ensure that the buffer is empty */ + *buf = 0; + + if (ast_strlen_zero(function_args)) { + ast_log(LOG_ERROR, "Function '%s' requires arguments.\n", function_name); + return -1; + } + + parse = ast_strdupa(function_args); + AST_STANDARD_APP_ARGS(args, parse); + + if (!args.argc || ast_strlen_zero(args.cc_id)) { + ast_log(LOG_ERROR, "Function '%s' missing call completion record ID.\n", + function_name); + return -1; + } + if (!isdigit(*args.cc_id)) { + ast_log(LOG_ERROR, "Function '%s' call completion record ID must be numeric.\n", + function_name); + return -1; + } + + if (ast_strlen_zero(args.get_name)) { + ast_log(LOG_ERROR, "Function '%s' missing what-to-get parameter.\n", + function_name); + return -1; + } + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_id(atoi(args.cc_id)); + if (cc_record) { + if (!strcasecmp("a-all", args.get_name)) { + snprintf(buf, size, "\"%s\" <%s>", cc_record->redial.caller.name, + cc_record->redial.caller.number); + } else if (!strcasecmp("a-name", args.get_name)) { + ast_copy_string(buf, cc_record->redial.caller.name, size); + } else if (!strncasecmp("a-num", args.get_name, 5)) { + ast_copy_string(buf, cc_record->redial.caller.number, size); + } else if (!strcasecmp("a-ton", args.get_name)) { + snprintf(buf, size, "%d", + misdn_to_ast_plan(cc_record->redial.caller.number_plan) + | misdn_to_ast_ton(cc_record->redial.caller.number_type)); + } else if (!strncasecmp("a-pres", args.get_name, 6)) { + ast_copy_string(buf, ast_named_caller_presentation( + misdn_to_ast_pres(cc_record->redial.caller.presentation) + | misdn_to_ast_screen(cc_record->redial.caller.screening)), size); + } else if (!strcasecmp("a-busy", args.get_name)) { + ast_copy_string(buf, cc_record->party_a_free ? "no" : "yes", size); + } else if (!strncasecmp("b-num", args.get_name, 5)) { + ast_copy_string(buf, cc_record->redial.dialed.number, size); + } else if (!strcasecmp("b-ton", args.get_name)) { + snprintf(buf, size, "%d", + misdn_to_ast_plan(cc_record->redial.dialed.number_plan) + | misdn_to_ast_ton(cc_record->redial.dialed.number_type)); + } else if (!strcasecmp("port", args.get_name)) { + snprintf(buf, size, "%d", cc_record->port); + } else if (!strcasecmp("available-notify-priority", args.get_name)) { + snprintf(buf, size, "%d", cc_record->remote_user_free.priority); + } else if (!strcasecmp("available-notify-exten", args.get_name)) { + ast_copy_string(buf, cc_record->remote_user_free.exten, size); + } else if (!strcasecmp("available-notify-context", args.get_name)) { + ast_copy_string(buf, cc_record->remote_user_free.context, size); + } else if (!strcasecmp("busy-notify-priority", args.get_name)) { + snprintf(buf, size, "%d", cc_record->b_free.priority); + } else if (!strcasecmp("busy-notify-exten", args.get_name)) { + ast_copy_string(buf, cc_record->b_free.exten, size); + } else if (!strcasecmp("busy-notify-context", args.get_name)) { + ast_copy_string(buf, cc_record->b_free.context, size); + } else { + AST_LIST_UNLOCK(&misdn_cc_records_db); + ast_log(LOG_ERROR, "Function '%s': Unknown what-to-get '%s'.\n", function_name, args.get_name); + return -1; + } + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + + return 0; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static struct ast_custom_function misdn_cc_function = { + .name = "mISDN_CC", + .synopsis = "Get call completion record information.", + .syntax = "mISDN_CC(${MISDN_CC_RECORD_ID},)", + .desc = + "mISDN_CC(${MISDN_CC_RECORD_ID},)\n" + "The following can be retrieved:\n" + "\"a-num\", \"a-name\", \"a-all\", \"a-ton\", \"a-pres\", \"a-busy\",\n" + "\"b-num\", \"b-ton\", \"port\",\n" + " User-A is available for call completion:\n" + " \"available-notify-priority\",\n" + " \"available-notify-exten\",\n" + " \"available-notify-context\",\n" + " User-A is busy:\n" + " \"busy-notify-priority\",\n" + " \"busy-notify-exten\",\n" + " \"busy-notify-context\"\n", + .read = misdn_cc_read, +}; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + /****************************************** * * Asterisk Channel Endpoint END @@ -5265,24 +10537,28 @@ ast_log(LOG_VERBOSE, "-- Unregistering mISDN Channel Driver --\n"); misdn_tasks_destroy(); - + if (!g_config_initialized) { return 0; } - + ast_cli_unregister_multiple(chan_misdn_clis, sizeof(chan_misdn_clis) / sizeof(struct ast_cli_entry)); /* ast_unregister_application("misdn_crypt"); */ ast_unregister_application("misdn_set_opt"); ast_unregister_application("misdn_facility"); ast_unregister_application("misdn_check_l2l1"); +#if defined(AST_MISDN_ENHANCEMENTS) + ast_unregister_application(misdn_command_name); + ast_custom_function_unregister(&misdn_cc_function); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ ast_channel_unregister(&misdn_tech); free_robin_list(); misdn_cfg_destroy(); misdn_lib_destroy(); - + if (misdn_debug) { ast_free(misdn_debug); } @@ -5290,7 +10566,11 @@ ast_free(misdn_debug_only); } ast_free(misdn_ports); - + +#if defined(AST_MISDN_ENHANCEMENTS) + misdn_cc_destroy(); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + return 0; } @@ -5308,18 +10588,22 @@ }; max_ports = misdn_lib_maxports_get(); - + if (max_ports <= 0) { ast_log(LOG_ERROR, "Unable to initialize mISDN\n"); return AST_MODULE_LOAD_DECLINE; } - + if (misdn_cfg_init(max_ports, 0)) { ast_log(LOG_ERROR, "Unable to initialize misdn_config.\n"); return AST_MODULE_LOAD_DECLINE; } g_config_initialized = 1; - + +#if defined(AST_MISDN_ENHANCEMENTS) + misdn_cc_init(); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + misdn_debug = ast_malloc(sizeof(int) * (max_ports + 1)); if (!misdn_debug) { ast_log(LOG_ERROR, "Out of memory for misdn_debug\n"); @@ -5366,7 +10650,7 @@ misdn_cfg_get(0, MISDN_GEN_NTDEBUGFLAGS, &ntflags, sizeof(ntflags)); misdn_cfg_get(0, MISDN_GEN_NTDEBUGFILE, &ntfile, sizeof(ntfile)); - misdn_cfg_get( 0, MISDN_GEN_NTKEEPCALLS, &ntkc, sizeof(ntkc)); + misdn_cfg_get(0, MISDN_GEN_NTKEEPCALLS, &ntkc, sizeof(ntkc)); misdn_lib_nt_keepcalls(ntkc); misdn_lib_nt_debug_init(ntflags, ntfile); @@ -5376,7 +10660,7 @@ unload_module(); return AST_MODULE_LOAD_DECLINE; } - + ast_cli_register_multiple(chan_misdn_clis, sizeof(chan_misdn_clis) / sizeof(struct ast_cli_entry)); ast_register_application("misdn_set_opt", misdn_set_opt_exec, "misdn_set_opt", @@ -5407,45 +10691,71 @@ " vt - Tx gain control, optarg is gain\n" ); - + ast_register_application("misdn_facility", misdn_facility_exec, "misdn_facility", - "misdn_facility(||..)\n" - "Sends the Facility Message FACILITY_TYPE with \n" - "the given Arguments to the current ISDN Channel\n" - "Supported Facilities are:\n" - "\n" - "type=calldeflect args=Nr where to deflect\n" + "misdn_facility(||..)\n" + "Sends the Facility Message FACILITY_TYPE with \n" + "the given Arguments to the current ISDN Channel\n" + "Supported Facilities are:\n" + "\n" + "type=calldeflect args=Nr where to deflect\n" ); ast_register_application("misdn_check_l2l1", misdn_check_l2l1, "misdn_check_l2l1", - "misdn_check_l2l1(||g:,timeout)" - "Checks if the L2 and L1 are up on either the given or\n" - "on the ports in the group with \n" - "If the L1/L2 are down, check_l2l1 gets up the L1/L2 and waits\n" - "for seconds that this happens. Otherwise, nothing happens\n" - "\n" - "This application, ensures the L1/L2 state of the Ports in a group\n" - "it is intended to make the pmp_l1_check option redundant and to\n" - "fix a buggy switch config from your provider\n" - "\n" - "a sample dialplan would look like:\n\n" - "exten => _X.,1,misdn_check_l2l1(g:out|2)\n" - "exten => _X.,n,dial(mISDN/g:out/${EXTEN})\n" - "\n" + "misdn_check_l2l1(||g:,timeout)\n" + "Checks if the L2 and L1 are up on either the given or\n" + "on the ports in the group with \n" + "If the L1/L2 are down, check_l2l1 gets up the L1/L2 and waits\n" + "for seconds that this happens. Otherwise, nothing happens\n" + "\n" + "This application, ensures the L1/L2 state of the Ports in a group\n" + "it is intended to make the pmp_l1_check option redundant and to\n" + "fix a buggy switch config from your provider\n" + "\n" + "a sample dialplan would look like:\n\n" + "exten => _X.,1,misdn_check_l2l1(g:out|2)\n" + "exten => _X.,n,dial(mISDN/g:out/${EXTEN})\n" ); +#if defined(AST_MISDN_ENHANCEMENTS) + ast_register_application(misdn_command_name, misdn_command_exec, misdn_command_name, + "misdn_command([,])\n" + "The following commands are defined:\n" + "cc-initialize\n" + " Setup mISDN support for call completion\n" + " Must call before doing any Dial() involving call completion.\n" + "ccnr-request,${MISDN_CC_RECORD_ID},,,\n" + " Request Call Completion No Reply activation\n" + "ccbs-request,${MISDN_CC_RECORD_ID},,,\n" + " Request Call Completion Busy Subscriber activation\n" + "cc-b-free,${MISDN_CC_RECORD_ID},,,\n" + " Set the dialplan location to notify when User-B is available but User-A is busy.\n" + " Setting this dialplan location is optional.\n" + "cc-a-busy,${MISDN_CC_RECORD_ID},\n" + " Set the busy status of call completion User-A\n" + "cc-deactivate,${MISDN_CC_RECORD_ID}\n" + " Deactivate the identified call completion request\n" + "\n" + "MISDN_CC_RECORD_ID is set when Dial() returns and call completion is possible\n" + "MISDN_CC_STATUS is set to ACTIVATED or ERROR after the call completion\n" + "activation request.\n" + "MISDN_ERROR_MSG is set to a descriptive message on error.\n" + ); + ast_custom_function_register(&misdn_cc_function); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + misdn_cfg_get(0, MISDN_GEN_TRACEFILE, global_tracefile, sizeof(global_tracefile)); /* start the l1 watchers */ - + for (port = misdn_cfg_get_next_port(0); port >= 0; port = misdn_cfg_get_next_port(port)) { int l1timeout; misdn_cfg_get(port, MISDN_CFG_L1_TIMEOUT, &l1timeout, sizeof(l1timeout)); if (l1timeout) { chan_misdn_log(4, 0, "Adding L1watcher task: port:%d timeout:%ds\n", port, l1timeout); - misdn_tasks_add(l1timeout * 1000, misdn_l1_task, &misdn_ports[port]); + misdn_tasks_add(l1timeout * 1000, misdn_l1_task, &misdn_ports[port]); } } @@ -5465,23 +10775,646 @@ /*** SOME APPS ;)***/ -static int misdn_facility_exec(struct ast_channel *chan, void *data) +#if defined(AST_MISDN_ENHANCEMENTS) +/*! +* \brief misdn_command arguments container. +*/ +AST_DEFINE_APP_ARGS_TYPE(misdn_command_args, + AST_APP_ARG(name); /* Subcommand name */ + AST_APP_ARG(arg)[10 + 1]; /* Subcommand arguments */ +); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static void misdn_cc_caller_destroy(void *obj) { + /* oh snap! */ +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +static struct misdn_cc_caller *misdn_cc_caller_alloc(struct ast_channel *chan) +{ + struct misdn_cc_caller *cc_caller; + + if (!(cc_caller = ao2_alloc(sizeof(*cc_caller), misdn_cc_caller_destroy))) { + return NULL; + } + + cc_caller->chan = chan; + + return cc_caller; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief misdn_command(cc-initialize) subcommand handler + * + * \param chan Asterisk channel to operate upon. + * \param subcommand Arguments for the subcommand + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int misdn_command_cc_initialize(struct ast_channel *chan, struct misdn_command_args *subcommand) +{ + struct misdn_cc_caller *cc_caller; + struct ast_datastore *datastore; + + if (!(cc_caller = misdn_cc_caller_alloc(chan))) { + return -1; + } + + if (!(datastore = ast_datastore_alloc(&misdn_cc_ds_info, NULL))) { + ao2_ref(cc_caller, -1); + return -1; + } + + ast_channel_lock(chan); + + /* Inherit reference */ + datastore->data = cc_caller; + cc_caller = NULL; + + datastore->inheritance = DATASTORE_INHERIT_FOREVER; + + ast_channel_datastore_add(chan, datastore); + + ast_channel_unlock(chan); + + return 0; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief misdn_command(cc-deactivate) subcommand handler + * + * \details + * misdn_command(cc-deactivate,${MISDN_CC_RECORD_ID}) + * Deactivate a call completion service instance. + * + * \param chan Asterisk channel to operate upon. + * \param subcommand Arguments for the subcommand + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int misdn_command_cc_deactivate(struct ast_channel *chan, struct misdn_command_args *subcommand) +{ + long record_id; + const char *error_str; + struct misdn_cc_record *cc_record; + struct misdn_bchannel *bc; + struct misdn_bchannel dummy; + + static const char cmd_help[] = "%s(%s,${MISDN_CC_RECORD_ID})\n"; + + if (ast_strlen_zero(subcommand->arg[0]) || !isdigit(*subcommand->arg[0])) { + ast_log(LOG_WARNING, cmd_help, misdn_command_name, subcommand->name); + return -1; + } + record_id = atol(subcommand->arg[0]); + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_id(record_id); + if (cc_record && 0 <= cc_record->port) { + if (cc_record->ptp) { + if (cc_record->mode.ptp.bc) { + /* Close the call-completion signaling link */ + bc = cc_record->mode.ptp.bc; + bc->fac_out.Function = Fac_None; + bc->out_cause = AST_CAUSE_NORMAL_CLEARING; + misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE); + } + misdn_cc_delete(cc_record); + } else if (cc_record->activated) { + cc_record->error_code = FacError_None; + cc_record->reject_code = FacReject_None; + cc_record->invoke_id = ++misdn_invoke_id; + cc_record->outstanding_message = 1; + + /* Build message */ + misdn_make_dummy(&dummy, cc_record->port, 0, misdn_lib_port_is_nt(cc_record->port), 0); + dummy.fac_out.Function = Fac_CCBSDeactivate; + dummy.fac_out.u.CCBSDeactivate.InvokeID = cc_record->invoke_id; + dummy.fac_out.u.CCBSDeactivate.ComponentType = FacComponent_Invoke; + dummy.fac_out.u.CCBSDeactivate.Component.Invoke.CCBSReference = cc_record->mode.ptmp.reference_id; + + /* Send message */ + print_facility(&dummy.fac_out, &dummy); + misdn_lib_send_event(&dummy, EVENT_FACILITY); + } + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + + /* Wait for the response to the call completion deactivation request. */ + misdn_cc_response_wait(chan, MISDN_CC_REQUEST_WAIT_MAX, record_id); + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_id(record_id); + if (cc_record) { + if (cc_record->port < 0) { + /* The network did not tell us that call completion was available. */ + error_str = NULL; + } else if (cc_record->outstanding_message) { + cc_record->outstanding_message = 0; + error_str = misdn_no_response_from_network; + } else if (cc_record->reject_code != FacReject_None) { + error_str = misdn_to_str_reject_code(cc_record->reject_code); + } else if (cc_record->reject_code != FacError_None) { + error_str = misdn_to_str_error_code(cc_record->error_code); + } else { + error_str = NULL; + } + + misdn_cc_delete(cc_record); + } else { + error_str = NULL; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + if (error_str) { + ast_verb(1, "%s(%s) diagnostic '%s' on channel %s\n", + misdn_command_name, subcommand->name, error_str, chan->name); + pbx_builtin_setvar_helper(chan, MISDN_ERROR_MSG, error_str); + } + + return 0; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief misdn_command(cc-a-busy) subcommand handler + * + * \details + * misdn_command(cc-a-busy,${MISDN_CC_RECORD_ID},) + * Set the status of User-A for a call completion service instance. + * + * \param chan Asterisk channel to operate upon. + * \param subcommand Arguments for the subcommand + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int misdn_command_cc_a_busy(struct ast_channel *chan, struct misdn_command_args *subcommand) +{ + long record_id; + int party_a_free; + struct misdn_cc_record *cc_record; + struct misdn_bchannel *bc; + + static const char cmd_help[] = "%s(%s,${MISDN_CC_RECORD_ID},)\n"; + + if (ast_strlen_zero(subcommand->arg[0]) || !isdigit(*subcommand->arg[0])) { + ast_log(LOG_WARNING, cmd_help, misdn_command_name, subcommand->name); + return -1; + } + record_id = atol(subcommand->arg[0]); + + if (ast_true(subcommand->arg[1])) { + party_a_free = 0; + } else if (ast_false(subcommand->arg[1])) { + party_a_free = 1; + } else { + ast_log(LOG_WARNING, cmd_help, misdn_command_name, subcommand->name); + return -1; + } + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_id(record_id); + if (cc_record && cc_record->party_a_free != party_a_free) { + /* User-A's status has changed */ + cc_record->party_a_free = party_a_free; + + if (cc_record->ptp && cc_record->mode.ptp.bc) { + cc_record->error_code = FacError_None; + cc_record->reject_code = FacReject_None; + + /* Build message */ + bc = cc_record->mode.ptp.bc; + if (cc_record->party_a_free) { + bc->fac_out.Function = Fac_CCBS_T_Resume; + bc->fac_out.u.CCBS_T_Resume.InvokeID = ++misdn_invoke_id; + } else { + bc->fac_out.Function = Fac_CCBS_T_Suspend; + bc->fac_out.u.CCBS_T_Suspend.InvokeID = ++misdn_invoke_id; + } + + /* Send message */ + print_facility(&bc->fac_out, bc); + misdn_lib_send_event(bc, EVENT_FACILITY); + } + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + + return 0; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief misdn_command(cc-b-free) subcommand handler + * + * \details + * misdn_command(cc-b-free,${MISDN_CC_RECORD_ID},,,) + * Set the dialplan location to notify when User-B is free and User-A is busy. + * + * \param chan Asterisk channel to operate upon. + * \param subcommand Arguments for the subcommand + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int misdn_command_cc_b_free(struct ast_channel *chan, struct misdn_command_args *subcommand) +{ + unsigned index; + long record_id; + int priority; + char *context; + char *exten; + struct misdn_cc_record *cc_record; + + static const char cmd_help[] = "%s(%s,${MISDN_CC_RECORD_ID},,,)\n"; + + /* Check that all arguments are present */ + for (index = 0; index < 4; ++index) { + if (ast_strlen_zero(subcommand->arg[index])) { + ast_log(LOG_WARNING, cmd_help, misdn_command_name, subcommand->name); + return -1; + } + } + + /* These must be numeric */ + if (!isdigit(*subcommand->arg[0]) || !isdigit(*subcommand->arg[3])) { + ast_log(LOG_WARNING, cmd_help, misdn_command_name, subcommand->name); + return -1; + } + + record_id = atol(subcommand->arg[0]); + context = subcommand->arg[1]; + exten = subcommand->arg[2]; + priority = atoi(subcommand->arg[3]); + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_id(record_id); + if (cc_record) { + /* Save User-B free information */ + ast_copy_string(cc_record->b_free.context, context, sizeof(cc_record->b_free.context)); + ast_copy_string(cc_record->b_free.exten, exten, sizeof(cc_record->b_free.exten)); + cc_record->b_free.priority = priority; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + + return 0; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +struct misdn_cc_request { + enum FacFunction ptmp; + enum FacFunction ptp; +}; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief misdn_command(ccbs-request/ccnr-request) subcommand handler helper + * + * \details + * misdn_command(ccbs-request,${MISDN_CC_RECORD_ID},,,) + * misdn_command(ccnr-request,${MISDN_CC_RECORD_ID},,,) + * Set the dialplan location to notify when User-B is free and User-A is free. + * + * \param chan Asterisk channel to operate upon. + * \param subcommand Arguments for the subcommand + * \param request Which call-completion request message to generate. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int misdn_command_cc_request(struct ast_channel *chan, struct misdn_command_args *subcommand, const struct misdn_cc_request *request) +{ + unsigned index; + int request_retention; + long record_id; + int priority; + char *context; + char *exten; + const char *error_str; + struct misdn_cc_record *cc_record; + struct misdn_bchannel *bc; + struct misdn_bchannel dummy; + struct misdn_party_id id; + + static const char cmd_help[] = "%s(%s,${MISDN_CC_RECORD_ID},,,)\n"; + + /* Check that all arguments are present */ + for (index = 0; index < 4; ++index) { + if (ast_strlen_zero(subcommand->arg[index])) { + ast_log(LOG_WARNING, cmd_help, misdn_command_name, subcommand->name); + return -1; + } + } + + /* These must be numeric */ + if (!isdigit(*subcommand->arg[0]) || !isdigit(*subcommand->arg[3])) { + ast_log(LOG_WARNING, cmd_help, misdn_command_name, subcommand->name); + return -1; + } + + record_id = atol(subcommand->arg[0]); + context = subcommand->arg[1]; + exten = subcommand->arg[2]; + priority = atoi(subcommand->arg[3]); + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_id(record_id); + if (cc_record) { + /* Save User-B free information */ + ast_copy_string(cc_record->remote_user_free.context, context, + sizeof(cc_record->remote_user_free.context)); + ast_copy_string(cc_record->remote_user_free.exten, exten, + sizeof(cc_record->remote_user_free.exten)); + cc_record->remote_user_free.priority = priority; + + if (0 <= cc_record->port) { + if (cc_record->ptp) { + if (!cc_record->mode.ptp.bc) { + bc = misdn_lib_get_register_bc(cc_record->port); + if (bc) { + cc_record->mode.ptp.bc = bc; + cc_record->error_code = FacError_None; + cc_record->reject_code = FacReject_None; + cc_record->invoke_id = ++misdn_invoke_id; + cc_record->outstanding_message = 1; + cc_record->activation_requested = 1; + + misdn_cfg_get(bc->port, MISDN_CFG_CC_REQUEST_RETENTION, + &request_retention, sizeof(request_retention)); + cc_record->mode.ptp.requested_retention = request_retention ? 1 : 0; + + /* Build message */ + bc->fac_out.Function = request->ptp; + bc->fac_out.u.CCBS_T_Request.InvokeID = cc_record->invoke_id; + bc->fac_out.u.CCBS_T_Request.ComponentType = FacComponent_Invoke; + bc->fac_out.u.CCBS_T_Request.Component.Invoke.Q931ie = + cc_record->redial.setup_bc_hlc_llc; + memset(&id, 0, sizeof(id)); + id.number_plan = cc_record->redial.dialed.number_plan; + id.number_type = cc_record->redial.dialed.number_type; + ast_copy_string(id.number, cc_record->redial.dialed.number, + sizeof(id.number)); + misdn_Address_fill( + &bc->fac_out.u.CCBS_T_Request.Component.Invoke.Destination, + &id); + misdn_Address_fill( + &bc->fac_out.u.CCBS_T_Request.Component.Invoke.Originating, + &cc_record->redial.caller); + bc->fac_out.u.CCBS_T_Request.Component.Invoke.PresentationAllowedIndicatorPresent = 1; + bc->fac_out.u.CCBS_T_Request.Component.Invoke.PresentationAllowedIndicator = + (cc_record->redial.caller.presentation != 0) ? 0 : 1; + bc->fac_out.u.CCBS_T_Request.Component.Invoke.RetentionSupported = + request_retention ? 1 : 0; + + /* Send message */ + print_facility(&bc->fac_out, bc); + misdn_lib_send_event(bc, EVENT_REGISTER); + } + } + } else { + cc_record->error_code = FacError_None; + cc_record->reject_code = FacReject_None; + cc_record->invoke_id = ++misdn_invoke_id; + cc_record->outstanding_message = 1; + cc_record->activation_requested = 1; + + /* Build message */ + misdn_make_dummy(&dummy, cc_record->port, 0, + misdn_lib_port_is_nt(cc_record->port), 0); + dummy.fac_out.Function = request->ptmp; + dummy.fac_out.u.CCBSRequest.InvokeID = cc_record->invoke_id; + dummy.fac_out.u.CCBSRequest.ComponentType = FacComponent_Invoke; + dummy.fac_out.u.CCBSRequest.Component.Invoke.CallLinkageID = + cc_record->mode.ptmp.linkage_id; + + /* Send message */ + print_facility(&dummy.fac_out, &dummy); + misdn_lib_send_event(&dummy, EVENT_FACILITY); + } + } + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + + /* Wait for the response to the call completion request. */ + misdn_cc_response_wait(chan, MISDN_CC_REQUEST_WAIT_MAX, record_id); + + AST_LIST_LOCK(&misdn_cc_records_db); + cc_record = misdn_cc_find_by_id(record_id); + if (cc_record) { + if (!cc_record->activated) { + if (cc_record->port < 0) { + /* The network did not tell us that call completion was available. */ + error_str = "No port number"; + } else if (cc_record->outstanding_message) { + cc_record->outstanding_message = 0; + error_str = misdn_no_response_from_network; + } else if (cc_record->reject_code != FacReject_None) { + error_str = misdn_to_str_reject_code(cc_record->reject_code); + } else if (cc_record->error_code != FacError_None) { + error_str = misdn_to_str_error_code(cc_record->error_code); + } else if (cc_record->ptp) { + if (cc_record->mode.ptp.bc) { + error_str = "Call-completion already requested"; + } else { + error_str = "Could not allocate call-completion signaling link"; + } + } else { + /* Should never happen. */ + error_str = "Unexpected error"; + } + + /* No need to keep the call completion record. */ + if (cc_record->ptp && cc_record->mode.ptp.bc) { + /* Close the call-completion signaling link */ + bc = cc_record->mode.ptp.bc; + bc->fac_out.Function = Fac_None; + bc->out_cause = AST_CAUSE_NORMAL_CLEARING; + misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE); + } + misdn_cc_delete(cc_record); + } else { + error_str = NULL; + } + } else { + error_str = misdn_cc_record_not_found; + } + AST_LIST_UNLOCK(&misdn_cc_records_db); + if (error_str) { + ast_verb(1, "%s(%s) diagnostic '%s' on channel %s\n", + misdn_command_name, subcommand->name, error_str, chan->name); + pbx_builtin_setvar_helper(chan, MISDN_ERROR_MSG, error_str); + pbx_builtin_setvar_helper(chan, MISDN_CC_STATUS, "ERROR"); + } else { + pbx_builtin_setvar_helper(chan, MISDN_CC_STATUS, "ACTIVATED"); + } + + return 0; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief misdn_command(ccbs-request) subcommand handler + * + * \details + * misdn_command(ccbs-request,${MISDN_CC_RECORD_ID},,,) + * Set the dialplan location to notify when User-B is free and User-A is free. + * + * \param chan Asterisk channel to operate upon. + * \param subcommand Arguments for the subcommand + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int misdn_command_ccbs_request(struct ast_channel *chan, struct misdn_command_args *subcommand) +{ + static const struct misdn_cc_request request = { + .ptmp = Fac_CCBSRequest, + .ptp = Fac_CCBS_T_Request + }; + + return misdn_command_cc_request(chan, subcommand, &request); +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief misdn_command(ccnr-request) subcommand handler + * + * \details + * misdn_command(ccnr-request,${MISDN_CC_RECORD_ID},,,) + * Set the dialplan location to notify when User-B is free and User-A is free. + * + * \param chan Asterisk channel to operate upon. + * \param subcommand Arguments for the subcommand + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int misdn_command_ccnr_request(struct ast_channel *chan, struct misdn_command_args *subcommand) +{ + static const struct misdn_cc_request request = { + .ptmp = Fac_CCNRRequest, + .ptp = Fac_CCNR_T_Request + }; + + return misdn_command_cc_request(chan, subcommand, &request); +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +struct misdn_command_table { + /*! \brief subcommand name */ + const char *name; + + /*! \brief subcommand handler */ + int (*func)(struct ast_channel *chan, struct misdn_command_args *subcommand); + + /*! \brief TRUE if the subcommand can only be executed on mISDN channels */ + int misdn_only; +}; +static const struct misdn_command_table misdn_commands[] = { +/* *INDENT-OFF* */ + /* subcommand-name subcommand-handler mISDN only */ + { "cc-initialize", misdn_command_cc_initialize, 0 }, + { "cc-deactivate", misdn_command_cc_deactivate, 0 }, + { "cc-a-busy", misdn_command_cc_a_busy, 0 }, + { "cc-b-free", misdn_command_cc_b_free, 0 }, + { "ccbs-request", misdn_command_ccbs_request, 0 }, + { "ccnr-request", misdn_command_ccnr_request, 0 }, +/* *INDENT-ON* */ +}; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief misdn_command() dialplan application. + * + * \param chan Asterisk channel to operate upon. + * \param data Application options string. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int misdn_command_exec(struct ast_channel *chan, const char *data) +{ + char *parse; + unsigned index; + struct misdn_command_args subcommand; + + if (ast_strlen_zero((char *) data)) { + ast_log(LOG_ERROR, "%s requires arguments\n", misdn_command_name); + return -1; + } + + ast_log(LOG_DEBUG, "%s(%s)\n", misdn_command_name, (char *) data); + + parse = ast_strdupa(data); + AST_STANDARD_APP_ARGS(subcommand, parse); + if (!subcommand.argc || ast_strlen_zero(subcommand.name)) { + ast_log(LOG_ERROR, "%s requires a subcommand\n", misdn_command_name); + return -1; + } + + for (index = 0; index < ARRAY_LEN(misdn_commands); ++index) { + if (strcasecmp(misdn_commands[index].name, subcommand.name) == 0) { + strcpy(subcommand.name, misdn_commands[index].name); + if (misdn_commands[index].misdn_only + && strcasecmp(chan->tech->type, misdn_type) != 0) { + ast_log(LOG_WARNING, + "%s(%s) only makes sense with %s channels!\n", + misdn_command_name, subcommand.name, misdn_type); + return -1; + } + return misdn_commands[index].func(chan, &subcommand); + } + } + + ast_log(LOG_WARNING, "%s(%s) subcommand is unknown\n", misdn_command_name, + subcommand.name); + return -1; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +static int misdn_facility_exec(struct ast_channel *chan, const char *data) +{ struct chan_list *ch = MISDN_ASTERISK_TECH_PVT(chan); char *parse; + unsigned max_len; + AST_DECLARE_APP_ARGS(args, AST_APP_ARG(facility_type); AST_APP_ARG(arg)[99]; ); chan_misdn_log(0, 0, "TYPE: %s\n", chan->tech->type); - - if (strcasecmp(chan->tech->type, "mISDN")) { - ast_log(LOG_WARNING, "misdn_facility makes only sense with chan_misdn channels!\n"); + + if (strcasecmp(chan->tech->type, misdn_type)) { + ast_log(LOG_WARNING, "misdn_facility only makes sense with %s channels!\n", misdn_type); return -1; } - - if (ast_strlen_zero((char *)data)) { + + if (ast_strlen_zero((char *) data)) { ast_log(LOG_WARNING, "misdn_facility requires arguments: facility_type[,]\n"); return -1; } @@ -5499,12 +11432,41 @@ ast_log(LOG_WARNING, "Facility: Call Deflection requires an argument: Number\n"); } - if (strlen(args.arg[0]) >= sizeof(ch->bc->fac_out.u.CDeflection.DeflectedToNumber)) { - ast_log(LOG_WARNING, "Facility: Number argument too long (up to %d digits are allowed). Ignoring.\n", (int)sizeof(ch->bc->fac_out.u.CDeflection.DeflectedToNumber)); +#if defined(AST_MISDN_ENHANCEMENTS) + max_len = sizeof(ch->bc->fac_out.u.CallDeflection.Component.Invoke.Deflection.Party.Number) - 1; + if (max_len < strlen(args.arg[0])) { + ast_log(LOG_WARNING, + "Facility: Number argument too long (up to %u digits are allowed). Ignoring.\n", + max_len); return 0; } + ch->bc->fac_out.Function = Fac_CallDeflection; + ch->bc->fac_out.u.CallDeflection.InvokeID = ++misdn_invoke_id; + ch->bc->fac_out.u.CallDeflection.ComponentType = FacComponent_Invoke; + ch->bc->fac_out.u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUserPresent = 1; + ch->bc->fac_out.u.CallDeflection.Component.Invoke.PresentationAllowedToDivertedToUser = 0; + ch->bc->fac_out.u.CallDeflection.Component.Invoke.Deflection.Party.Type = 0;/* unknown */ + ch->bc->fac_out.u.CallDeflection.Component.Invoke.Deflection.Party.LengthOfNumber = strlen(args.arg[0]); + strcpy((char *) ch->bc->fac_out.u.CallDeflection.Component.Invoke.Deflection.Party.Number, args.arg[0]); + ch->bc->fac_out.u.CallDeflection.Component.Invoke.Deflection.Subaddress.Length = 0; + +#else /* !defined(AST_MISDN_ENHANCEMENTS) */ + + max_len = sizeof(ch->bc->fac_out.u.CDeflection.DeflectedToNumber) - 1; + if (max_len < strlen(args.arg[0])) { + ast_log(LOG_WARNING, + "Facility: Number argument too long (up to %u digits are allowed). Ignoring.\n", + max_len); + return 0; + } ch->bc->fac_out.Function = Fac_CD; - ast_copy_string((char *)ch->bc->fac_out.u.CDeflection.DeflectedToNumber, args.arg[0], sizeof(ch->bc->fac_out.u.CDeflection.DeflectedToNumber)); + ch->bc->fac_out.u.CDeflection.PresentationAllowed = 0; + //ch->bc->fac_out.u.CDeflection.DeflectedToSubaddress[0] = 0; + strcpy((char *) ch->bc->fac_out.u.CDeflection.DeflectedToNumber, args.arg[0]); +#endif /* !defined(AST_MISDN_ENHANCEMENTS) */ + + /* Send message */ + print_facility(&ch->bc->fac_out, ch->bc); misdn_lib_send_event(ch->bc, EVENT_FACILITY); } else { chan_misdn_log(1, ch->bc->port, "Unknown Facility: %s\n", args.facility_type); @@ -5513,7 +11475,7 @@ return 0; } -static int misdn_check_l2l1(struct ast_channel *chan, void *data) +static int misdn_check_l2l1(struct ast_channel *chan, const char *data) { char *parse; char group[BUFFERSIZE + 1]; @@ -5524,11 +11486,11 @@ int port_up; AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(grouppar); - AST_APP_ARG(timeout); + AST_APP_ARG(grouppar); + AST_APP_ARG(timeout); ); - if (ast_strlen_zero((char *)data)) { + if (ast_strlen_zero((char *) data)) { ast_log(LOG_WARNING, "misdn_check_l2l1 Requires arguments\n"); return -1; } @@ -5545,13 +11507,13 @@ timeout = atoi(args.timeout); port_str = args.grouppar; - if (port_str[0] == 'g' && port_str[1] == ':' ) { + if (port_str[0] == 'g' && port_str[1] == ':') { /* We make a group call lets checkout which ports are in my group */ port_str += 2; ast_copy_string(group, port_str, sizeof(group)); chan_misdn_log(2, 0, "Checking Ports in group: %s\n", group); - for ( port = misdn_cfg_get_next_port(port); + for (port = misdn_cfg_get_next_port(port); port > 0; port = misdn_cfg_get_next_port(port)) { char cfg_group[BUFFERSIZE + 1]; @@ -5562,7 +11524,6 @@ if (!strcasecmp(cfg_group, group)) { port_up = misdn_lib_port_up(port, 1); - if (!port_up) { chan_misdn_log(2, 0, " --> port '%d'\n", port); misdn_lib_get_port_up(port); @@ -5570,10 +11531,9 @@ } } } - } else { port = atoi(port_str); - chan_misdn_log(2, 0, "Checking Port: %d\n",port); + chan_misdn_log(2, 0, "Checking Port: %d\n", port); port_up = misdn_lib_port_up(port, 1); if (!port_up) { misdn_lib_get_port_up(port); @@ -5589,54 +11549,53 @@ return 0; } -static int misdn_set_opt_exec(struct ast_channel *chan, void *data) +static int misdn_set_opt_exec(struct ast_channel *chan, const char *data) { struct chan_list *ch = MISDN_ASTERISK_TECH_PVT(chan); - char *tok, *tokb, *parse; - int keyidx = 0; + char *tok; + char *tokb; + char *parse; + int keyidx = 0; int rxgain = 0; int txgain = 0; int change_jitter = 0; - if (strcasecmp(chan->tech->type, "mISDN")) { - ast_log(LOG_WARNING, "misdn_set_opt makes only sense with chan_misdn channels!\n"); + if (strcasecmp(chan->tech->type, misdn_type)) { + ast_log(LOG_WARNING, "misdn_set_opt makes sense only with %s channels!\n", misdn_type); return -1; } - - if (ast_strlen_zero((char *)data)) { + + if (ast_strlen_zero((char *) data)) { ast_log(LOG_WARNING, "misdn_set_opt Requires arguments\n"); return -1; } parse = ast_strdupa(data); for (tok = strtok_r(parse, ":", &tokb); - tok; - tok = strtok_r(NULL, ":", &tokb) ) { + tok; + tok = strtok_r(NULL, ":", &tokb)) { int neglect = 0; - if (tok[0] == '!' ) { + if (tok[0] == '!') { neglect = 1; tok++; } - + switch(tok[0]) { - case 'd' : ast_copy_string(ch->bc->display, ++tok, sizeof(ch->bc->display)); chan_misdn_log(1, ch->bc->port, "SETOPT: Display:%s\n", ch->bc->display); break; - case 'n': chan_misdn_log(1, ch->bc->port, "SETOPT: No DSP\n"); ch->bc->nodsp = 1; break; - case 'j': chan_misdn_log(1, ch->bc->port, "SETOPT: jitter\n"); tok++; change_jitter = 1; - switch ( tok[0] ) { + switch (tok[0]) { case 'b': ch->jb_len = atoi(++tok); chan_misdn_log(1, ch->bc->port, " --> buffer_len:%d\n", ch->jb_len); @@ -5654,6 +11613,7 @@ ch->jb_upper_threshold = 0; chan_misdn_log(1, ch->bc->port, " --> buffer_len:%d (default)\n", ch->jb_len); chan_misdn_log(1, ch->bc->port, " --> upper_threshold:%d (default)\n", ch->jb_upper_threshold); + break; } break; case 'v': @@ -5684,13 +11644,14 @@ break; } break; - case 'c': keyidx = atoi(++tok); { char keys[4096]; - char *key = NULL, *tmp = keys; + char *key = NULL; + char *tmp = keys; int i; + misdn_cfg_get(0, MISDN_GEN_CRYPT_KEYS, keys, sizeof(keys)); for (i = 0; i < keyidx; i++) { @@ -5706,7 +11667,7 @@ } case 'e': chan_misdn_log(1, ch->bc->port, "SETOPT: EchoCancel\n"); - + if (neglect) { chan_misdn_log(1, ch->bc->port, " --> disabled\n"); #ifdef MISDN_1_2 @@ -5726,11 +11687,10 @@ } #endif } - break; case 'h': chan_misdn_log(1, ch->bc->port, "SETOPT: Digital\n"); - + if (strlen(tok) > 1 && tok[1] == '1') { chan_misdn_log(1, ch->bc->port, "SETOPT: HDLC \n"); if (!ch->bc->hdlc) { @@ -5739,38 +11699,37 @@ } ch->bc->capability = INFO_CAPABILITY_DIGITAL_UNRESTRICTED; break; - case 's': chan_misdn_log(1, ch->bc->port, "SETOPT: Send DTMF\n"); ch->bc->send_dtmf = 1; break; - case 'f': chan_misdn_log(1, ch->bc->port, "SETOPT: Faxdetect\n"); ch->faxdetect = 1; misdn_cfg_get(ch->bc->port, MISDN_CFG_FAXDETECT_TIMEOUT, &ch->faxdetect_timeout, sizeof(ch->faxdetect_timeout)); break; - case 'a': chan_misdn_log(1, ch->bc->port, "SETOPT: AST_DSP (for DTMF)\n"); ch->ast_dsp = 1; break; - case 'p': chan_misdn_log(1, ch->bc->port, "SETOPT: callerpres: %s\n", &tok[1]); /* CRICH: callingpres!!! */ if (strstr(tok, "allowed")) { - ch->bc->pres = 0; + ch->bc->presentation = 0; + ch->bc->set_presentation = 1; } else if (strstr(tok, "restricted")) { - ch->bc->pres = 1; + ch->bc->presentation = 1; + ch->bc->set_presentation = 1; } else if (strstr(tok, "not_screened")) { chan_misdn_log(0, ch->bc->port, "SETOPT: callerpres: not_screened is deprecated\n"); - ch->bc->pres = 1; + ch->bc->presentation = 1; + ch->bc->set_presentation = 1; } break; case 'i' : chan_misdn_log(1, ch->bc->port, "Ignoring dtmf tones, just use them inband\n"); - ch->ignore_dtmf=1; + ch->ignore_dtmf = 1; break; default: break; @@ -5797,19 +11756,19 @@ chan_misdn_log(1, ch->bc->port, "SETOPT: with AST_DSP we deactivate mISDN_dsp\n"); ch->bc->nodsp = 1; } - + return 0; } -int chan_misdn_jb_empty ( struct misdn_bchannel *bc, char *buf, int len) +int chan_misdn_jb_empty(struct misdn_bchannel *bc, char *buf, int len) { struct chan_list *ch = find_chan_by_bc(cl_te, bc); - + if (ch && ch->jb) { return misdn_jb_empty(ch->jb, buf, len); } - + return -1; } @@ -5866,7 +11825,7 @@ void misdn_jb_destroy(struct misdn_jb *jb) { ast_mutex_destroy(&jb->mutexjb); - + ast_free(jb->ok); ast_free(jb->samples); ast_free(jb); @@ -5876,17 +11835,20 @@ error (buffer overflow). */ int misdn_jb_fill(struct misdn_jb *jb, const char *data, int len) { - int i, j, rp, wp; + int i; + int j; + int rp; + int wp; if (!jb || ! data) { return 0; } ast_mutex_lock(&jb->mutexjb); - + wp = jb->wp; rp = jb->rp; - + for (i = 0; i < len; i++) { jb->samples[wp] = data[i]; jb->ok[wp] = 1; @@ -5930,7 +11892,7 @@ jb->wp = wp; ast_mutex_unlock(&jb->mutexjb); - + return 0; } @@ -5939,14 +11901,17 @@ of data. */ int misdn_jb_empty(struct misdn_jb *jb, char *data, int len) { - int i, wp, rp, read = 0; + int i; + int wp; + int rp; + int read = 0; ast_mutex_lock(&jb->mutexjb); rp = jb->rp; wp = jb->wp; - if (jb->state_empty) { + if (jb->state_empty) { for (i = 0; i < len; i++) { if (wp == rp) { jb->rp = rp; @@ -5982,66 +11947,70 @@ return read; } - - - /*******************************************************/ /*************** JITTERBUFFER END *********************/ /*******************************************************/ - - - static void chan_misdn_log(int level, int port, char *tmpl, ...) { va_list ap; char buf[1024]; char port_buf[8]; - if (! ((0 <= port) && (port <= max_ports))) { + if (!(0 <= port && port <= max_ports)) { ast_log(LOG_WARNING, "cb_log called with out-of-range port number! (%d)\n", port); port = 0; level = -1; + } else if (!(level == -1 + || (misdn_debug_only[port] + ? (level == 1 && misdn_debug[port]) || level == misdn_debug[port] + : level <= misdn_debug[port]) + || (level <= misdn_debug[0] && !ast_strlen_zero(global_tracefile)))) { + /* + * We are not going to print anything so lets not + * go to all the work of generating a string. + */ + return; } snprintf(port_buf, sizeof(port_buf), "P[%2d] ", port); - va_start(ap, tmpl); vsnprintf(buf, sizeof(buf), tmpl, ap); va_end(ap); if (level == -1) { ast_log(LOG_WARNING, "%s", buf); - - } else if (misdn_debug_only[port] ? - (level == 1 && misdn_debug[port]) || (level == misdn_debug[port]) - : level <= misdn_debug[port]) { - + } else if (misdn_debug_only[port] + ? (level == 1 && misdn_debug[port]) || level == misdn_debug[port] + : level <= misdn_debug[port]) { ast_console_puts(port_buf); ast_console_puts(buf); } - - if ((level <= misdn_debug[0]) && !ast_strlen_zero(global_tracefile) ) { + + if (level <= misdn_debug[0] && !ast_strlen_zero(global_tracefile)) { char ctimebuf[30]; - time_t tm = time(NULL); - char *tmp = ctime_r(&tm, ctimebuf), *p; + time_t tm; + char *tmp; + char *p; + FILE *fp; - FILE *fp = fopen(global_tracefile, "a+"); - - if ((p = strchr(tmp, '\n'))) { - *p = ':'; - } - + fp = fopen(global_tracefile, "a+"); if (!fp) { ast_console_puts("Error opening Tracefile: [ "); ast_console_puts(global_tracefile); ast_console_puts(" ] "); - + ast_console_puts(strerror(errno)); ast_console_puts("\n"); - return ; + return; } - + + tm = time(NULL); + tmp = ctime_r(&tm, ctimebuf); + p = strchr(tmp, '\n'); + if (p) { + *p = ':'; + } fputs(tmp, fp); fputs(" ", fp); fputs(port_buf, fp); Index: channels/chan_skinny.c =================================================================== --- a/channels/chan_skinny.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/chan_skinny.c (.../trunk) (revision 202568) @@ -49,7 +49,7 @@ #include "asterisk/pbx.h" #include "asterisk/sched.h" #include "asterisk/io.h" -#include "asterisk/rtp.h" +#include "asterisk/rtp_engine.h" #include "asterisk/netsock.h" #include "asterisk/acl.h" #include "asterisk/callerid.h" @@ -71,6 +71,63 @@ #include "asterisk/indications.h" #include "asterisk/linkedlists.h" +/*** DOCUMENTATION + + + List SKINNY devices (text format). + + + + + + Lists Skinny devices in text format with details on current status. + Devicelist will follow as separate events, followed by a final event called + DevicelistComplete. + + + + + Show SKINNY device (text format). + + + + + The device name you want to check. + + + + Show one SKINNY device with details on current status. + + + + + List SKINNY lines (text format). + + + + + + Lists Skinny lines in text format with details on current status. + Linelist will follow as separate events, followed by a final event called + LinelistComplete. + + + + + Show SKINNY line (text format). + + + + + The line name you want to check. + + + + Show one SKINNY line with details on current status. + + + ***/ + #ifdef SKINNY_DEVMODE #define SKINNY_DEVONLY(code) \ code @@ -585,7 +642,7 @@ #define SOFTKEY_DND 0x13 #define SOFTKEY_IDIVERT 0x14 -struct soft_key_template_definition soft_key_template_default[] = { +static struct soft_key_template_definition soft_key_template_default[] = { { "\200\001", SOFTKEY_REDIAL }, { "\200\002", SOFTKEY_NEWCALL }, { "\200\003", SOFTKEY_HOLD }, @@ -963,7 +1020,7 @@ /* XXX This is the combined size of the variables above. (len, res, e) If more are added, this MUST change. (sizeof(skinny_req) - sizeof(skinny_data)) DOES NOT WORK on all systems (amd64?). */ -int skinny_header_size = 12; +static int skinny_header_size = 12; /***************************** * Asterisk specific globals * @@ -977,8 +1034,8 @@ static char ourhost[256]; static int ourport; static struct in_addr __ourip; -struct ast_hostent ahp; -struct hostent *hp; +static struct ast_hostent ahp; +static struct hostent *hp; static int skinnysock = -1; static pthread_t accept_t; static int callnums = 1; @@ -1076,7 +1133,7 @@ #define SKINNY_CX_INACTIVE 4 #if 0 -static char *skinny_cxmodes[] = { +static const char * const skinny_cxmodes[] = { "sendonly", "recvonly", "sendrecv", @@ -1111,8 +1168,8 @@ struct skinny_subchannel { ast_mutex_t lock; struct ast_channel *owner; - struct ast_rtp *rtp; - struct ast_rtp *vrtp; + struct ast_rtp_instance *rtp; + struct ast_rtp_instance *vrtp; unsigned int callid; /* time_t lastouttime; */ /* Unused */ int progress; @@ -1198,7 +1255,7 @@ int newmsgs; }; -struct skinny_line_options{ +static struct skinny_line_options{ SKINNY_LINE_OPTIONS } default_line_struct = { .callwaiting = 1, @@ -1217,7 +1274,7 @@ .prune = 0, .hookstate = SKINNY_ONHOOK, }; -struct skinny_line_options *default_line = &default_line_struct; +static struct skinny_line_options *default_line = &default_line_struct; static AST_LIST_HEAD_STATIC(lines, skinny_line); @@ -1278,7 +1335,7 @@ AST_LIST_ENTRY(skinny_device) list; }; -struct skinny_device_options{ +static struct skinny_device_options { SKINNY_DEVICE_OPTIONS } default_device_struct = { .transfer = 1, @@ -1290,7 +1347,7 @@ .capability = 0, .prune = 0, }; -struct skinny_device_options *default_device = &default_device_struct; +static struct skinny_device_options *default_device = &default_device_struct; static AST_LIST_HEAD_STATIC(devices, skinny_device); @@ -1347,7 +1404,7 @@ .fixup = skinny_fixup, .send_digit_begin = skinny_senddigit_begin, .send_digit_end = skinny_senddigit_end, - .bridge = ast_rtp_bridge, + .bridge = ast_rtp_instance_bridge, }; static int skinny_extensionstate_cb(char *context, char* exten, int state, void *data); @@ -2516,6 +2573,43 @@ return 0; } +static void update_connectedline(struct skinny_subchannel *sub, const void *data, size_t datalen) +{ + struct ast_channel *c = sub->owner; + struct skinny_line *l = sub->parent; + struct skinny_device *d = l->device; + + if (ast_strlen_zero(c->cid.cid_num) || ast_strlen_zero(c->connected.id.number)) + return; + + if (sub->owner->_state == AST_STATE_UP) { + transmit_callstate(d, l->instance, SKINNY_CONNECTED, sub->callid); + transmit_displaypromptstatus(d, "Connected", 0, l->instance, sub->callid); + if (sub->outgoing) + transmit_callinfo(d, c->connected.id.name, c->connected.id.number, l->cid_name, l->cid_num, l->instance, sub->callid, 1); + else + transmit_callinfo(d, l->cid_name, l->cid_num, c->connected.id.name, c->connected.id.number, l->instance, sub->callid, 2); + } else { + if (sub->outgoing) { + transmit_callstate(d, l->instance, SKINNY_RINGIN, sub->callid); + transmit_displaypromptstatus(d, "Ring-In", 0, l->instance, sub->callid); + transmit_callinfo(d, c->connected.id.name, c->connected.id.number, l->cid_name, l->cid_num, l->instance, sub->callid, 1); + } else { + if (!sub->ringing) { + transmit_callstate(d, l->instance, SKINNY_RINGOUT, sub->callid); + transmit_displaypromptstatus(d, "Ring-Out", 0, l->instance, sub->callid); + sub->ringing = 1; + } else { + transmit_callstate(d, l->instance, SKINNY_PROGRESS, sub->callid); + transmit_displaypromptstatus(d, "Call Progress", 0, l->instance, sub->callid); + sub->progress = 1; + } + + transmit_callinfo(d, l->cid_name, l->cid_num, c->connected.id.name, c->connected.id.number, l->instance, sub->callid, 2); + } + } +} + static void mwi_event_cb(const struct ast_event *event, void *userdata) { struct skinny_line *l = userdata; @@ -2557,46 +2651,48 @@ /* I do not believe skinny can deal with video. Anyone know differently? */ /* Yes, it can. Currently 7985 and Cisco VT Advantage do video. */ -static enum ast_rtp_get_result skinny_get_vrtp_peer(struct ast_channel *c, struct ast_rtp **rtp) +static enum ast_rtp_glue_result skinny_get_vrtp_peer(struct ast_channel *c, struct ast_rtp_instance **instance) { struct skinny_subchannel *sub = NULL; if (!(sub = c->tech_pvt) || !(sub->vrtp)) - return AST_RTP_GET_FAILED; + return AST_RTP_GLUE_RESULT_FORBID; - *rtp = sub->vrtp; + ao2_ref(sub->vrtp, +1); + *instance = sub->vrtp; - return AST_RTP_TRY_NATIVE; + return AST_RTP_GLUE_RESULT_REMOTE; } -static enum ast_rtp_get_result skinny_get_rtp_peer(struct ast_channel *c, struct ast_rtp **rtp) +static enum ast_rtp_glue_result skinny_get_rtp_peer(struct ast_channel *c, struct ast_rtp_instance **instance) { struct skinny_subchannel *sub = NULL; struct skinny_line *l; - enum ast_rtp_get_result res = AST_RTP_TRY_NATIVE; + enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_REMOTE; if (skinnydebug) ast_verb(1, "skinny_get_rtp_peer() Channel = %s\n", c->name); if (!(sub = c->tech_pvt)) - return AST_RTP_GET_FAILED; + return AST_RTP_GLUE_RESULT_FORBID; ast_mutex_lock(&sub->lock); if (!(sub->rtp)){ ast_mutex_unlock(&sub->lock); - return AST_RTP_GET_FAILED; + return AST_RTP_GLUE_RESULT_FORBID; } - - *rtp = sub->rtp; + ao2_ref(sub->rtp, +1); + *instance = sub->rtp; + l = sub->parent; if (!l->canreinvite || l->nat){ - res = AST_RTP_TRY_PARTIAL; + res = AST_RTP_GLUE_RESULT_LOCAL; if (skinnydebug) - ast_verb(1, "skinny_get_rtp_peer() Using AST_RTP_TRY_PARTIAL \n"); + ast_verb(1, "skinny_get_rtp_peer() Using AST_RTP_GLUE_RESULT_LOCAL \n"); } ast_mutex_unlock(&sub->lock); @@ -2605,15 +2701,15 @@ } -static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) +static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *trtp, int codecs, int nat_active) { struct skinny_subchannel *sub; struct skinny_line *l; struct skinny_device *d; struct skinnysession *s; struct ast_format_list fmt; - struct sockaddr_in us; - struct sockaddr_in them; + struct sockaddr_in us = { 0, }; + struct sockaddr_in them = { 0, }; struct skinny_req *req; sub = c->tech_pvt; @@ -2630,7 +2726,7 @@ s = d->session; if (rtp){ - ast_rtp_get_peer(rtp, &them); + ast_rtp_instance_get_remote_address(rtp, &them); /* Shutdown any early-media or previous media on re-invite */ if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE))) @@ -2654,7 +2750,7 @@ req->data.startmedia.conferenceId = htolel(sub->callid); req->data.startmedia.passThruPartyId = htolel(sub->callid); if (!(l->canreinvite) || (l->nat)){ - ast_rtp_get_us(rtp, &us); + ast_rtp_instance_get_local_address(rtp, &us); req->data.startmedia.remoteIp = htolel(d->ourip.s_addr); req->data.startmedia.remotePort = htolel(ntohs(us.sin_port)); } else { @@ -2675,11 +2771,11 @@ return 0; } -static struct ast_rtp_protocol skinny_rtp = { +static struct ast_rtp_glue skinny_rtp_glue = { .type = "Skinny", .get_rtp_info = skinny_get_rtp_peer, .get_vrtp_info = skinny_get_vrtp_peer, - .set_rtp_peer = skinny_set_rtp_peer, + .update_peer = skinny_set_rtp_peer, }; static char *handle_skinny_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) @@ -3010,13 +3106,6 @@ return CLI_SUCCESS; } -static char mandescr_show_devices[] = -"Description: Lists Skinny devices in text format with details on current status.\n" -"Devicelist will follow as separate events, followed by a final event called\n" -"DevicelistComplete.\n" -"Variables: \n" -" ActionID: Action ID for this transaction. Will be returned.\n"; - /*! \brief Show SKINNY devices in the manager API */ /* Inspired from chan_sip */ static int manager_skinny_show_devices(struct mansession *s, const struct message *m) @@ -3155,12 +3244,6 @@ return CLI_SUCCESS; } -static char mandescr_show_device[] = -"Description: Show one SKINNY device with details on current status.\n" -"Variables: \n" -" Device: The device name you want to check.\n" -" ActionID: Optional action ID for this AMI transaction.\n"; - static int manager_skinny_show_device(struct mansession *s, const struct message *m) { const char *a[4]; @@ -3271,13 +3354,6 @@ return CLI_SUCCESS; } -static char mandescr_show_lines[] = -"Description: Lists Skinny lines in text format with details on current status.\n" -"Linelist will follow as separate events, followed by a final event called\n" -"LinelistComplete.\n" -"Variables: \n" -" ActionID: Action ID for this transaction. Will be returned.\n"; - /*! \brief Show Skinny lines in the manager API */ /* Inspired from chan_sip */ static int manager_skinny_show_lines(struct mansession *s, const struct message *m) @@ -3463,12 +3539,6 @@ return CLI_SUCCESS; } -static char mandescr_show_line[] = -"Description: Show one SKINNY line with details on current status.\n" -"Variables: \n" -" Line: The line name you want to check.\n" -" ActionID: Optional action ID for this AMI transaction.\n"; - static int manager_skinny_show_line(struct mansession *s, const struct message *m) { const char *a[4]; @@ -3559,29 +3629,36 @@ ast_mutex_lock(&sub->lock); /* Allocate the RTP */ - sub->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); + sub->rtp = ast_rtp_instance_new(NULL, sched, &bindaddr, NULL); if (hasvideo) - sub->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); - + sub->vrtp = ast_rtp_instance_new(NULL, sched, &bindaddr, NULL); + + if (sub->rtp) { + ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_RTCP, 1); + } + if (sub->vrtp) { + ast_rtp_instance_set_prop(sub->vrtp, AST_RTP_PROPERTY_RTCP, 1); + } + if (sub->rtp && sub->owner) { - ast_channel_set_fd(sub->owner, 0, ast_rtp_fd(sub->rtp)); - ast_channel_set_fd(sub->owner, 1, ast_rtcp_fd(sub->rtp)); + ast_channel_set_fd(sub->owner, 0, ast_rtp_instance_fd(sub->rtp, 0)); + ast_channel_set_fd(sub->owner, 1, ast_rtp_instance_fd(sub->rtp, 1)); } if (hasvideo && sub->vrtp && sub->owner) { - ast_channel_set_fd(sub->owner, 2, ast_rtp_fd(sub->vrtp)); - ast_channel_set_fd(sub->owner, 3, ast_rtcp_fd(sub->vrtp)); + ast_channel_set_fd(sub->owner, 2, ast_rtp_instance_fd(sub->vrtp, 0)); + ast_channel_set_fd(sub->owner, 3, ast_rtp_instance_fd(sub->vrtp, 1)); } if (sub->rtp) { - ast_rtp_setqos(sub->rtp, qos.tos_audio, qos.cos_audio, "Skinny RTP"); - ast_rtp_setnat(sub->rtp, l->nat); + ast_rtp_instance_set_qos(sub->rtp, qos.tos_audio, qos.cos_audio, "Skinny RTP"); + ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_NAT, l->nat); } if (sub->vrtp) { - ast_rtp_setqos(sub->vrtp, qos.tos_video, qos.cos_video, "Skinny VRTP"); - ast_rtp_setnat(sub->vrtp, l->nat); + ast_rtp_instance_set_qos(sub->vrtp, qos.tos_video, qos.cos_video, "Skinny VRTP"); + ast_rtp_instance_set_prop(sub->vrtp, AST_RTP_PROPERTY_NAT, l->nat); } /* Set Frame packetization */ if (sub->rtp) - ast_rtp_codec_setpref(sub->rtp, &l->prefs); + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(sub->rtp), sub->rtp, &l->prefs); /* Create the RTP connection */ transmit_connect(d, sub); @@ -3601,6 +3678,8 @@ l->hidecallerid ? "" : l->cid_num, l->hidecallerid ? "" : l->cid_name, c->cid.cid_ani ? NULL : l->cid_num); + c->connected.id.number = ast_strdup(c->exten); + c->connected.id.name = NULL; ast_setstate(c, AST_STATE_RING); if (!sub->rtp) { start_rtp(sub); @@ -3764,7 +3843,7 @@ transmit_callstateonly(d, sub, SKINNY_RINGIN); transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGIN); transmit_displaypromptstatus(d, "Ring-In", 0, l->instance, sub->callid); - transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, l->cid_name, l->cid_num, l->instance, sub->callid, 1); + transmit_callinfo(d, ast->connected.id.name, ast->connected.id.number, l->cid_name, l->cid_num, l->instance, sub->callid, 1); transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK); transmit_ringer_mode(d, SKINNY_RING_INSIDE); @@ -3852,7 +3931,7 @@ sub->alreadygone = 0; sub->outgoing = 0; if (sub->rtp) { - ast_rtp_destroy(sub->rtp); + ast_rtp_instance_destroy(sub->rtp); sub->rtp = NULL; } ast_mutex_unlock(&sub->lock); @@ -3891,7 +3970,7 @@ /* order matters here... for some reason, transmit_callinfo must be before transmit_callstate, or you won't get keypad messages in some situations. */ - transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, l->lastnumberdialed, l->lastnumberdialed, l->instance, sub->callid, 2); + transmit_callinfo(d, ast->connected.id.name, ast->connected.id.number, l->lastnumberdialed, l->lastnumberdialed, l->instance, sub->callid, 2); transmit_callstateonly(d, sub, SKINNY_CONNECTED); transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED); transmit_dialednumber(d, l->lastnumberdialed, l->instance, sub->callid); @@ -3913,16 +3992,16 @@ switch(ast->fdno) { case 0: - f = ast_rtp_read(sub->rtp); /* RTP Audio */ + f = ast_rtp_instance_read(sub->rtp, 0); /* RTP Audio */ break; case 1: - f = ast_rtcp_read(sub->rtp); /* RTCP Control Channel */ + f = ast_rtp_instance_read(sub->rtp, 1); /* RTCP Control Channel */ break; case 2: - f = ast_rtp_read(sub->vrtp); /* RTP Video */ + f = ast_rtp_instance_read(sub->vrtp, 0); /* RTP Video */ break; case 3: - f = ast_rtcp_read(sub->vrtp); /* RTCP Control Channel for video */ + f = ast_rtp_instance_read(sub->vrtp, 1); /* RTCP Control Channel for video */ break; #if 0 case 5: @@ -3979,7 +4058,7 @@ if (sub) { ast_mutex_lock(&sub->lock); if (sub->rtp) { - res = ast_rtp_write(sub->rtp, frame); + res = ast_rtp_instance_write(sub->rtp, frame); } ast_mutex_unlock(&sub->lock); } @@ -4086,6 +4165,10 @@ return "Unhold"; case AST_CONTROL_SRCUPDATE: return "Media Source Update"; + case AST_CONTROL_CONNECTED_LINE: + return "Connected Line"; + case AST_CONTROL_REDIRECTING: + return "Redirecting"; case -1: return "Stop tone"; default: @@ -4193,7 +4276,7 @@ transmit_callstateonly(d, sub, SKINNY_RINGOUT); transmit_dialednumber(d, l->lastnumberdialed, l->instance, sub->callid); transmit_displaypromptstatus(d, "Ring Out", 0, l->instance, sub->callid); - transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, l->lastnumberdialed, l->lastnumberdialed, l->instance, sub->callid, 2); /* 2 = outgoing from phone */ + transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, S_OR(ast->connected.id.name, l->lastnumberdialed), S_OR(ast->connected.id.number, l->lastnumberdialed), l->instance, sub->callid, 2); /* 2 = outgoing from phone */ sub->ringing = 1; if (!d->earlyrtp) { break; @@ -4234,7 +4317,7 @@ } transmit_callstateonly(d, sub, SKINNY_PROGRESS); transmit_displaypromptstatus(d, "Call Progress", 0, l->instance, sub->callid); - transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, l->lastnumberdialed, l->lastnumberdialed, l->instance, sub->callid, 2); /* 2 = outgoing from phone */ + transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, S_OR(ast->connected.id.name, l->lastnumberdialed), S_OR(ast->connected.id.number, l->lastnumberdialed), l->instance, sub->callid, 2); /* 2 = outgoing from phone */ sub->progress = 1; if (!d->earlyrtp) { break; @@ -4253,8 +4336,11 @@ case AST_CONTROL_PROCEEDING: break; case AST_CONTROL_SRCUPDATE: - ast_rtp_new_source(sub->rtp); + ast_rtp_instance_new_source(sub->rtp); break; + case AST_CONTROL_CONNECTED_LINE: + update_connectedline(sub, data, datalen); + break; default: ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind); return -1; /* Tell asterisk to provide inband signalling */ @@ -4312,7 +4398,7 @@ if (skinnydebug) ast_verb(1, "skinny_new: tmp->nativeformats=%d fmt=%d\n", tmp->nativeformats, fmt); if (sub->rtp) { - ast_channel_set_fd(tmp, 0, ast_rtp_fd(sub->rtp)); + ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(sub->rtp, 0)); } if (state == AST_STATE_RING) { tmp->rings = 1; @@ -5509,8 +5595,8 @@ struct skinny_line *l; struct skinny_subchannel *sub; struct ast_format_list fmt; - struct sockaddr_in sin; - struct sockaddr_in us; + struct sockaddr_in sin = { 0, }; + struct sockaddr_in us = { 0, }; uint32_t addr; int port; int status; @@ -5537,8 +5623,8 @@ l = sub->parent; if (sub->rtp) { - ast_rtp_set_peer(sub->rtp, &sin); - ast_rtp_get_us(sub->rtp, &us); + ast_rtp_instance_set_remote_address(sub->rtp, &sin); + ast_rtp_instance_get_local_address(sub->rtp, &us); } else { ast_log(LOG_ERROR, "No RTP structure, this is very bad\n"); return 0; @@ -7289,17 +7375,13 @@ return -1; } - ast_rtp_proto_register(&skinny_rtp); + ast_rtp_glue_register(&skinny_rtp_glue); ast_cli_register_multiple(cli_skinny, ARRAY_LEN(cli_skinny)); - ast_manager_register2("SKINNYdevices", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_devices, - "List SKINNY devices (text format)", mandescr_show_devices); - ast_manager_register2("SKINNYshowdevice", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_device, - "Show SKINNY device (text format)", mandescr_show_device); - ast_manager_register2("SKINNYlines", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_lines, - "List SKINNY lines (text format)", mandescr_show_lines); - ast_manager_register2("SKINNYshowline", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_line, - "Show SKINNY line (text format)", mandescr_show_line); + ast_manager_register_xml("SKINNYdevices", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_devices); + ast_manager_register_xml("SKINNYshowdevice", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_device); + ast_manager_register_xml("SKINNYlines", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_lines); + ast_manager_register_xml("SKINNYshowline", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_line); sched = sched_context_create(); if (!sched) { @@ -7323,7 +7405,7 @@ struct skinny_subchannel *sub; struct ast_context *con; - ast_rtp_proto_unregister(&skinny_rtp); + ast_rtp_glue_unregister(&skinny_rtp_glue); ast_channel_unregister(&skinny_tech); ast_cli_unregister_multiple(cli_skinny, ARRAY_LEN(cli_skinny)); Index: channels/xpmr/xpmr.c =================================================================== --- a/channels/xpmr/xpmr.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/xpmr/xpmr.c (.../trunk) (revision 202568) @@ -157,7 +157,7 @@ TRACEJ(2,(" source len = %i\n",slen)); pd=*dest; - if(pd) free(pd); + free(pd); pd=calloc(slen+1,1); memcpy(pd,src,slen); *dest=pd; Index: channels/console_gui.c =================================================================== --- a/channels/console_gui.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/console_gui.c (.../trunk) (revision 202568) @@ -832,7 +832,7 @@ * use a translation table, below - one line per entry, * plain, shift, ctrl, ... using the first char as key. */ -static const char *us_kbd_map[] = { +static const char * const us_kbd_map[] = { "`~", "1!", "2@", "3#", "4$", "5%", "6^", "7&", "8*", "9(", "0)", "-_", "=+", "[{", "]}", "\\|", ";:", "'\"", ",<", ".>", "/?", @@ -1505,7 +1505,7 @@ } struct _s_k { const char *s; int k; }; -static struct _s_k gui_key_map[] = { +static const struct _s_k gui_key_map[] = { {"FREEZE", KEY_FREEZE}, {"PIP", KEY_PIP}, {"PICK_UP", KEY_PICK_UP }, Index: channels/chan_alsa.c =================================================================== --- a/channels/chan_alsa.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/chan_alsa.c (.../trunk) (revision 202568) @@ -782,7 +782,7 @@ { char tmp[256], *tmp2; char *mye, *myc; - char *d; + const char *d; char *res = CLI_SUCCESS; switch (cmd) { Index: channels/chan_nbs.c =================================================================== --- a/channels/chan_nbs.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/chan_nbs.c (.../trunk) (revision 202568) @@ -53,7 +53,7 @@ static int prefformat = AST_FORMAT_SLINEAR; static char context[AST_MAX_EXTENSION] = "default"; -static char type[] = "NBS"; +static const char type[] = "NBS"; /* NBS creates private structures on demand */ Index: channels/chan_mgcp.c =================================================================== --- a/channels/chan_mgcp.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/chan_mgcp.c (.../trunk) (revision 202568) @@ -52,7 +52,7 @@ #include "asterisk/pbx.h" #include "asterisk/sched.h" #include "asterisk/io.h" -#include "asterisk/rtp.h" +#include "asterisk/rtp_engine.h" #include "asterisk/acl.h" #include "asterisk/callerid.h" #include "asterisk/cli.h" @@ -119,7 +119,7 @@ #define MGCP_CX_INACTIVE 4 /*! } */ -static char *mgcp_cxmodes[] = { +static const char * const mgcp_cxmodes[] = { "sendonly", "recvonly", "sendrecv", @@ -282,7 +282,7 @@ int id; struct ast_channel *owner; struct mgcp_endpoint *parent; - struct ast_rtp *rtp; + struct ast_rtp_instance *rtp; struct sockaddr_in tmpdest; char txident[80]; /*! \todo FIXME txident is replaced by rqnt_ident in endpoint. This should be obsoleted */ @@ -408,7 +408,7 @@ static int transmit_notify_request(struct mgcp_subchannel *sub, char *tone); static int transmit_modify_request(struct mgcp_subchannel *sub); static int transmit_notify_request_with_callerid(struct mgcp_subchannel *sub, char *tone, char *callernum, char *callername); -static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp *rtp, int codecs); +static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp_instance *rtp, int codecs); static int transmit_connection_del(struct mgcp_subchannel *sub); static int transmit_audit_endpoint(struct mgcp_endpoint *p); static void start_rtp(struct mgcp_subchannel *sub); @@ -447,7 +447,7 @@ .fixup = mgcp_fixup, .send_digit_begin = mgcp_senddigit_begin, .send_digit_end = mgcp_senddigit_end, - .bridge = ast_rtp_bridge, + .bridge = ast_rtp_instance_bridge, }; static void mwi_event_cb(const struct ast_event *event, void *userdata) @@ -503,7 +503,7 @@ sub->alreadygone = 0; memset(&sub->tmpdest, 0, sizeof(sub->tmpdest)); if (sub->rtp) { - ast_rtp_destroy(sub->rtp); + ast_rtp_instance_destroy(sub->rtp); sub->rtp = NULL; } dump_cmd_queues(NULL, sub); /* SC */ @@ -915,7 +915,7 @@ transmit_modify_request(sub->next); } - transmit_notify_request_with_callerid(sub, tone, ast->cid.cid_num, ast->cid.cid_name); + transmit_notify_request_with_callerid(sub, tone, ast->connected.id.number, ast->connected.id.name); ast_setstate(ast, AST_STATE_RINGING); if (sub->next->owner && !ast_strlen_zero(sub->next->cxident) && !ast_strlen_zero(sub->next->callid)) { @@ -1003,7 +1003,7 @@ /* Reset temporary destination */ memset(&sub->tmpdest, 0, sizeof(sub->tmpdest)); if (sub->rtp) { - ast_rtp_destroy(sub->rtp); + ast_rtp_instance_destroy(sub->rtp); sub->rtp = NULL; } @@ -1098,7 +1098,7 @@ if (a->argc != 4) return CLI_SHOWUSAGE; /* split the name into parts by null */ - ename = a->argv[3]; + ename = ast_strdupa(a->argv[3]); gname = ename; while (*gname) { if (*gname == '@') { @@ -1203,7 +1203,7 @@ /* Retrieve audio/etc from channel. Assumes sub->lock is already held. */ struct ast_frame *f; - f = ast_rtp_read(sub->rtp); + f = ast_rtp_instance_read(sub->rtp, 0); /* Don't send RFC2833 if we're not supposed to */ if (f && (f->frametype == AST_FRAME_DTMF) && !(sub->parent->dtmfmode & MGCP_DTMF_RFC2833)) return &ast_null_frame; @@ -1261,7 +1261,7 @@ ast_mutex_lock(&sub->lock); if ((sub->parent->sub == sub) || !sub->parent->singlepath) { if (sub->rtp) { - res = ast_rtp_write(sub->rtp, frame); + res = ast_rtp_instance_write(sub->rtp, frame); } } ast_mutex_unlock(&sub->lock); @@ -1297,7 +1297,7 @@ res = -1; /* Let asterisk play inband indications */ } else if (p->dtmfmode & MGCP_DTMF_RFC2833) { ast_log(LOG_DEBUG, "Sending DTMF using RFC2833"); - ast_rtp_senddigit_begin(sub->rtp, digit); + ast_rtp_instance_dtmf_begin(sub->rtp, digit); } else { ast_log(LOG_ERROR, "Don't know about DTMF_MODE %d\n", p->dtmfmode); } @@ -1324,7 +1324,7 @@ tmp[2] = digit; tmp[3] = '\0'; transmit_notify_request(sub, tmp); - ast_rtp_senddigit_end(sub->rtp, digit); + ast_rtp_instance_dtmf_end(sub->rtp, digit); } else { ast_log(LOG_ERROR, "Don't know about DTMF_MODE %d\n", p->dtmfmode); } @@ -1453,7 +1453,7 @@ ast_moh_stop(ast); break; case AST_CONTROL_SRCUPDATE: - ast_rtp_new_source(sub->rtp); + ast_rtp_instance_new_source(sub->rtp); break; case -1: transmit_notify_request(sub, ""); @@ -1479,9 +1479,8 @@ if (!tmp->nativeformats) tmp->nativeformats = capability; fmt = ast_best_codec(tmp->nativeformats); - ast_string_field_build(tmp, name, "MGCP/%s@%s-%d", i->name, i->parent->name, sub->id); if (sub->rtp) - ast_channel_set_fd(tmp, 0, ast_rtp_fd(sub->rtp)); + ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(sub->rtp, 0)); if (i->dtmfmode & (MGCP_DTMF_INBAND | MGCP_DTMF_HYBRID)) { i->dsp = ast_dsp_new(); ast_dsp_set_features(i->dsp, DSP_FEATURE_DIGIT_DETECT); @@ -1874,12 +1873,12 @@ sin.sin_family = AF_INET; memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); sin.sin_port = htons(portno); - ast_rtp_set_peer(sub->rtp, &sin); + ast_rtp_instance_set_remote_address(sub->rtp, &sin); #if 0 printf("Peer RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); #endif /* Scan through the RTP payload types specified in a "m=" line: */ - ast_rtp_pt_clear(sub->rtp); + ast_rtp_codecs_payloads_clear(ast_rtp_instance_get_codecs(sub->rtp), sub->rtp); codecs = ast_strdupa(m + len); while (!ast_strlen_zero(codecs)) { if (sscanf(codecs, "%d%n", &codec, &len) != 1) { @@ -1888,7 +1887,7 @@ ast_log(LOG_WARNING, "Error in codec string '%s' at '%s'\n", m, codecs); return -1; } - ast_rtp_set_m_type(sub->rtp, codec); + ast_rtp_codecs_payloads_set_m_type(ast_rtp_instance_get_codecs(sub->rtp), sub->rtp, codec); codec_count++; codecs += len; } @@ -1901,11 +1900,11 @@ if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) != 2) continue; /* Note: should really look at the 'freq' and '#chans' params too */ - ast_rtp_set_rtpmap_type(sub->rtp, codec, "audio", mimeSubtype, 0); + ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(sub->rtp), sub->rtp, codec, "audio", mimeSubtype, 0); } /* Now gather all of the codecs that were asked for: */ - ast_rtp_get_current_formats(sub->rtp, &peercapability, &peerNonCodecCapability); + ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(sub->rtp), &peercapability, &peerNonCodecCapability); p->capability = capability & peercapability; if (mgcpdebug) { ast_verbose("Capabilities: us - %d, them - %d, combined - %d\n", @@ -1920,7 +1919,7 @@ return 0; } -static int add_header(struct mgcp_request *req, char *var, char *value) +static int add_header(struct mgcp_request *req, const char *var, const char *value) { if (req->len >= sizeof(req->data) - 4) { ast_log(LOG_WARNING, "Out of space, can't add anymore\n"); @@ -2043,7 +2042,7 @@ } -static int add_sdp(struct mgcp_request *resp, struct mgcp_subchannel *sub, struct ast_rtp *rtp) +static int add_sdp(struct mgcp_request *resp, struct mgcp_subchannel *sub, struct ast_rtp_instance *rtp) { int len; int codec; @@ -2057,7 +2056,7 @@ char m[256] = ""; char a[1024] = ""; int x; - struct sockaddr_in dest; + struct sockaddr_in dest = { 0, }; struct mgcp_endpoint *p = sub->parent; /* XXX We break with the "recommendation" and send our IP, in order that our peer doesn't have to ast_gethostbyname() us XXX */ @@ -2066,9 +2065,9 @@ ast_log(LOG_WARNING, "No way to add SDP without an RTP structure\n"); return -1; } - ast_rtp_get_us(sub->rtp, &sin); + ast_rtp_instance_get_local_address(sub->rtp, &sin); if (rtp) { - ast_rtp_get_peer(rtp, &dest); + ast_rtp_instance_get_remote_address(sub->rtp, &dest); } else { if (sub->tmpdest.sin_addr.s_addr) { dest.sin_addr = sub->tmpdest.sin_addr; @@ -2094,11 +2093,11 @@ if (mgcpdebug) { ast_verbose("Answering with capability %d\n", x); } - codec = ast_rtp_lookup_code(sub->rtp, 1, x); + codec = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(sub->rtp), 1, x); if (codec > -1) { snprintf(costr, sizeof(costr), " %d", codec); strncat(m, costr, sizeof(m) - strlen(m) - 1); - snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(1, x, 0)); + snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype2(1, x, 0)); strncat(a, costr, sizeof(a) - strlen(a) - 1); } } @@ -2108,11 +2107,11 @@ if (mgcpdebug) { ast_verbose("Answering with non-codec capability %d\n", x); } - codec = ast_rtp_lookup_code(sub->rtp, 0, x); + codec = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(sub->rtp), 0, x); if (codec > -1) { snprintf(costr, sizeof(costr), " %d", codec); strncat(m, costr, sizeof(m) - strlen(m) - 1); - snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(0, x, 0)); + snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype2(0, x, 0)); strncat(a, costr, sizeof(a) - strlen(a) - 1); if (x == AST_RTP_DTMF) { /* Indicate we support DTMF... Not sure about 16, @@ -2136,7 +2135,7 @@ return 0; } -static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp *rtp, int codecs) +static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp_instance *rtp, int codecs) { struct mgcp_request resp; char local[256]; @@ -2147,13 +2146,13 @@ if (ast_strlen_zero(sub->cxident) && rtp) { /* We don't have a CXident yet, store the destination and wait a bit */ - ast_rtp_get_peer(rtp, &sub->tmpdest); + ast_rtp_instance_get_remote_address(rtp, &sub->tmpdest); return 0; } ast_copy_string(local, "p:20", sizeof(local)); for (x = 1; x <= AST_FORMAT_AUDIO_MASK; x <<= 1) { if (p->capability & x) { - snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype(1, x, 0)); + snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype2(1, x, 0)); strncat(local, tmp, sizeof(local) - strlen(local) - 1); } } @@ -2172,7 +2171,7 @@ return send_request(p, sub, &resp, oseq); /* SC */ } -static int transmit_connect_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp *rtp) +static int transmit_connect_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp_instance *rtp) { struct mgcp_request resp; char local[256]; @@ -2183,7 +2182,7 @@ ast_copy_string(local, "p:20", sizeof(local)); for (x = 1; x <= AST_FORMAT_AUDIO_MASK; x <<= 1) { if (p->capability & x) { - snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype(1, x, 0)); + snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype2(1, x, 0)); strncat(local, tmp, sizeof(local) - strlen(local) - 1); } } @@ -2611,21 +2610,17 @@ ast_mutex_lock(&sub->lock); /* check again to be on the safe side */ if (sub->rtp) { - ast_rtp_destroy(sub->rtp); + ast_rtp_instance_destroy(sub->rtp); sub->rtp = NULL; } /* Allocate the RTP now */ - sub->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); + sub->rtp = ast_rtp_instance_new(NULL, sched, &bindaddr, NULL); if (sub->rtp && sub->owner) - ast_channel_set_fd(sub->owner, 0, ast_rtp_fd(sub->rtp)); + ast_channel_set_fd(sub->owner, 0, ast_rtp_instance_fd(sub->rtp, 0)); if (sub->rtp) { - ast_rtp_setqos(sub->rtp, qos.tos_audio, qos.cos_audio, "MGCP RTP"); - ast_rtp_setnat(sub->rtp, sub->nat); + ast_rtp_instance_set_qos(sub->rtp, qos.tos_audio, qos.cos_audio, "MGCP RTP"); + ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_NAT, sub->nat); } -#if 0 - ast_rtp_set_callback(p->rtp, rtpready); - ast_rtp_set_data(p->rtp, p); -#endif /* Make a call*ID */ snprintf(sub->callid, sizeof(sub->callid), "%08lx%s", ast_random(), sub->txident); /* Transmit the connection create */ @@ -3940,22 +3935,22 @@ return (gw_reload ? NULL : gw); } -static enum ast_rtp_get_result mgcp_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) +static enum ast_rtp_glue_result mgcp_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) { struct mgcp_subchannel *sub = NULL; if (!(sub = chan->tech_pvt) || !(sub->rtp)) - return AST_RTP_GET_FAILED; + return AST_RTP_GLUE_RESULT_FORBID; - *rtp = sub->rtp; + *instance = sub->rtp ? ao2_ref(sub->rtp, +1), sub->rtp : NULL; if (sub->parent->canreinvite) - return AST_RTP_TRY_NATIVE; + return AST_RTP_GLUE_RESULT_REMOTE; else - return AST_RTP_TRY_PARTIAL; + return AST_RTP_GLUE_RESULT_LOCAL; } -static int mgcp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) +static int mgcp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *trtp, int codecs, int nat_active) { /* XXX Is there such thing as video support with MGCP? XXX */ struct mgcp_subchannel *sub; @@ -3967,10 +3962,10 @@ return -1; } -static struct ast_rtp_protocol mgcp_rtp = { +static struct ast_rtp_glue mgcp_rtp_glue = { .type = "MGCP", .get_rtp_info = mgcp_get_rtp_peer, - .set_rtp_peer = mgcp_set_rtp_peer, + .update_peer = mgcp_set_rtp_peer, }; static void destroy_endpoint(struct mgcp_endpoint *e) @@ -3984,7 +3979,7 @@ transmit_connection_del(sub); } if (sub->rtp) { - ast_rtp_destroy(sub->rtp); + ast_rtp_instance_destroy(sub->rtp); sub->rtp = NULL; } memset(sub->magic, 0, sizeof(sub->magic)); @@ -4276,7 +4271,7 @@ return AST_MODULE_LOAD_FAILURE; } - ast_rtp_proto_register(&mgcp_rtp); + ast_rtp_glue_register(&mgcp_rtp_glue); ast_cli_register_multiple(cli_mgcp, sizeof(cli_mgcp) / sizeof(struct ast_cli_entry)); /* And start the monitor for the first time */ @@ -4379,7 +4374,7 @@ } close(mgcpsock); - ast_rtp_proto_unregister(&mgcp_rtp); + ast_rtp_glue_unregister(&mgcp_rtp_glue); ast_cli_unregister_multiple(cli_mgcp, sizeof(cli_mgcp) / sizeof(struct ast_cli_entry)); sched_context_destroy(sched); Index: channels/chan_unistim.c =================================================================== --- a/channels/chan_unistim.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/chan_unistim.c (.../trunk) (revision 202568) @@ -60,7 +60,7 @@ #include "asterisk/module.h" #include "asterisk/pbx.h" #include "asterisk/event.h" -#include "asterisk/rtp.h" +#include "asterisk/rtp_engine.h" #include "asterisk/netsock.h" #include "asterisk/acl.h" #include "asterisk/callerid.h" @@ -365,7 +365,7 @@ /*! Unistim line */ struct unistim_line *parent; /*! RTP handle */ - struct ast_rtp *rtp; + struct ast_rtp_instance *rtp; int alreadygone; char ringvolume; char ringstyle; @@ -502,7 +502,7 @@ static const unsigned char packet_rcv_discovery[] = { 0xff, 0xff, 0xff, 0xff, 0x02, 0x02, 0xff, 0xff, 0xff, 0xff, 0x9e, 0x03, 0x08 }; -static unsigned char packet_send_discovery_ack[] = +static const unsigned char packet_send_discovery_ack[] = { 0x00, 0x00, /*Initial Seq (2 bytes) */ 0x00, 0x00, 0x00, 0x01 }; static const unsigned char packet_recv_firm_version[] = @@ -711,7 +711,7 @@ .send_digit_begin = unistim_senddigit_begin, .send_digit_end = unistim_senddigit_end, .send_text = unistim_sendtext, -/* .bridge = ast_rtp_bridge, */ + .bridge = ast_rtp_instance_bridge, }; static void display_last_error(const char *sz_msg) @@ -733,8 +733,8 @@ } /* Send data to a phone without retransmit nor buffering */ -static void send_raw_client(int size, unsigned char *data, struct sockaddr_in *addr_to, - const struct sockaddr_in *addr_ourip) +static void send_raw_client(int size, const unsigned char *data, struct sockaddr_in *addr_to, + const struct sockaddr_in *addr_ourip) { #ifdef HAVE_PKTINFO struct iovec msg_iov; @@ -743,7 +743,12 @@ struct cmsghdr *ip_msg = (struct cmsghdr *) buffer; struct in_pktinfo *pki = (struct in_pktinfo *) CMSG_DATA(ip_msg); - msg_iov.iov_base = data; + /* cast this to a non-const pointer, since the sendmsg() API + * does not provide read-only and write-only flavors of the + * structures used for its arguments, but in this case we know + * the data will not be modified + */ + msg_iov.iov_base = (char *) data; msg_iov.iov_len = size; msg.msg_name = addr_to; /* optional address */ @@ -1854,7 +1859,7 @@ static void swap_subs(struct unistim_line *p, int a, int b) { /* struct ast_channel *towner; */ - struct ast_rtp *rtp; + struct ast_rtp_instance *rtp; int fds; if (unistimdebug) @@ -2027,11 +2032,11 @@ static void start_rtp(struct unistim_subchannel *sub) { BUFFSEND; - struct sockaddr_in us; - struct sockaddr_in public; - struct sockaddr_in sin; + struct sockaddr_in us = { 0, }; + struct sockaddr_in public = { 0, }; + struct sockaddr_in sin = { 0, }; int codec; - struct sockaddr_in sout; + struct sockaddr_in sout = { 0, }; /* Sanity checks */ if (!sub) { @@ -2056,30 +2061,29 @@ /* Allocate the RTP */ if (unistimdebug) ast_verb(0, "Starting RTP. Bind on %s\n", ast_inet_ntoa(sout.sin_addr)); - sub->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, sout.sin_addr); + sub->rtp = ast_rtp_instance_new(NULL, sched, &sout, NULL); if (!sub->rtp) { ast_log(LOG_WARNING, "Unable to create RTP session: %s binaddr=%s\n", strerror(errno), ast_inet_ntoa(sout.sin_addr)); ast_mutex_unlock(&sub->lock); return; } - if (sub->rtp && sub->owner) { - sub->owner->fds[0] = ast_rtp_fd(sub->rtp); - sub->owner->fds[1] = ast_rtcp_fd(sub->rtp); + ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_RTCP, 1); + if (sub->owner) { + sub->owner->fds[0] = ast_rtp_instance_fd(sub->rtp, 0); + sub->owner->fds[1] = ast_rtp_instance_fd(sub->rtp, 1); } - if (sub->rtp) { - ast_rtp_setqos(sub->rtp, qos.tos_audio, qos.cos_audio, "UNISTIM RTP"); - ast_rtp_setnat(sub->rtp, sub->parent->parent->nat); - } + ast_rtp_instance_set_qos(sub->rtp, qos.tos_audio, qos.cos_audio, "UNISTIM RTP"); + ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_NAT, sub->parent->parent->nat); /* Create the RTP connection */ - ast_rtp_get_us(sub->rtp, &us); + ast_rtp_instance_get_local_address(sub->rtp, &us); sin.sin_family = AF_INET; /* Setting up RTP for our side */ memcpy(&sin.sin_addr, &sub->parent->parent->session->sin.sin_addr, sizeof(sin.sin_addr)); sin.sin_port = htons(sub->parent->parent->rtp_port); - ast_rtp_set_peer(sub->rtp, &sin); + ast_rtp_instance_set_remote_address(sub->rtp, &sin); if (!(sub->owner->nativeformats & sub->owner->readformat)) { int fmt; fmt = ast_best_codec(sub->owner->nativeformats); @@ -2091,7 +2095,7 @@ sub->owner->readformat = fmt; sub->owner->writeformat = fmt; } - codec = ast_rtp_lookup_code(sub->rtp, 1, sub->owner->readformat); + codec = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(sub->rtp), 1, sub->owner->readformat); /* Setting up RTP of the phone */ if (public_ip.sin_family == 0) /* NAT IP override ? */ memcpy(&public, &us, sizeof(public)); /* No defined, using IP from recvmsg */ @@ -3671,16 +3675,16 @@ Sendicon(TEXT_LINE0, FAV_ICON_NONE, session); if (sub->owner) { - if (sub->owner->cid.cid_num) { - send_text(TEXT_LINE1, TEXT_NORMAL, session, sub->owner->cid.cid_num); - change_callerid(session, 0, sub->owner->cid.cid_num); + if (sub->owner->connected.id.number) { + send_text(TEXT_LINE1, TEXT_NORMAL, session, sub->owner->connected.id.number); + change_callerid(session, 0, sub->owner->connected.id.number); } else { send_text(TEXT_LINE1, TEXT_NORMAL, session, DEFAULTCALLERID); change_callerid(session, 0, DEFAULTCALLERID); } - if (sub->owner->cid.cid_name) { - send_text(TEXT_LINE0, TEXT_NORMAL, session, sub->owner->cid.cid_name); - change_callerid(session, 1, sub->owner->cid.cid_name); + if (sub->owner->connected.id.name) { + send_text(TEXT_LINE0, TEXT_NORMAL, session, sub->owner->connected.id.name); + change_callerid(session, 1, sub->owner->connected.id.name); } else { send_text(TEXT_LINE0, TEXT_NORMAL, session, DEFAULTCALLERNAME); change_callerid(session, 1, DEFAULTCALLERNAME); @@ -3723,7 +3727,7 @@ if (sub->rtp) { if (unistimdebug) ast_verb(0, "Destroying RTP session\n"); - ast_rtp_destroy(sub->rtp); + ast_rtp_instance_destroy(sub->rtp); sub->rtp = NULL; } return 0; @@ -3768,7 +3772,7 @@ if (sub->rtp) { if (unistimdebug) ast_verb(0, "Destroying RTP session\n"); - ast_rtp_destroy(sub->rtp); + ast_rtp_instance_destroy(sub->rtp); sub->rtp = NULL; } return 0; @@ -3793,7 +3797,7 @@ if (sub->rtp) { if (unistimdebug) ast_verb(0, "Destroying RTP session\n"); - ast_rtp_destroy(sub->rtp); + ast_rtp_instance_destroy(sub->rtp); sub->rtp = NULL; } else if (unistimdebug) ast_verb(0, "No RTP session to destroy\n"); @@ -3920,10 +3924,10 @@ switch (ast->fdno) { case 0: - f = ast_rtp_read(sub->rtp); /* RTP Audio */ + f = ast_rtp_instance_read(sub->rtp, 0); /* RTP Audio */ break; case 1: - f = ast_rtcp_read(sub->rtp); /* RTCP Control Channel */ + f = ast_rtp_instance_read(sub->rtp, 1); /* RTCP Control Channel */ break; default: f = &ast_null_frame; @@ -3989,7 +3993,7 @@ if (sub) { ast_mutex_lock(&sub->lock); if (sub->rtp) { - res = ast_rtp_write(sub->rtp, frame); + res = ast_rtp_instance_write(sub->rtp, frame); } ast_mutex_unlock(&sub->lock); } @@ -4433,8 +4437,8 @@ return NULL; } l = sub->parent; - tmp = ast_channel_alloc(1, state, l->cid_num, NULL, l->accountcode, l->exten, - l->context, l->amaflags, "%s-%08x", l->fullname, (int) (long) sub); + tmp = ast_channel_alloc(1, state, l->cid_num, NULL, l->accountcode, l->exten, + l->context, l->amaflags, "%s@%s-%d", l->name, l->parent->name, sub->subtype); if (unistimdebug) ast_verb(0, "unistim_new sub=%d (%p) chan=%p\n", sub->subtype, sub, tmp); if (!tmp) { @@ -4449,13 +4453,11 @@ if (unistimdebug) ast_verb(0, "Best codec = %d from nativeformats %d (line cap=%d global=%d)\n", fmt, tmp->nativeformats, l->capability, CAPABILITY); - ast_string_field_build(tmp, name, "USTM/%s@%s-%d", l->name, l->parent->name, - sub->subtype); if ((sub->rtp) && (sub->subtype == 0)) { if (unistimdebug) ast_verb(0, "New unistim channel with a previous rtp handle ?\n"); - tmp->fds[0] = ast_rtp_fd(sub->rtp); - tmp->fds[1] = ast_rtcp_fd(sub->rtp); + tmp->fds[0] = ast_rtp_instance_fd(sub->rtp, 0); + tmp->fds[1] = ast_rtp_instance_fd(sub->rtp, 1); } if (sub->rtp) ast_jb_configure(tmp, &global_jbconf); @@ -5525,51 +5527,19 @@ return 0; } -static enum ast_rtp_get_result unistim_get_vrtp_peer(struct ast_channel *chan, - struct ast_rtp **rtp) +static enum ast_rtp_glue_result unistim_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) { - return AST_RTP_TRY_NATIVE; -} + struct unistim_subchannel *sub = chan->tech_pvt; -static enum ast_rtp_get_result unistim_get_rtp_peer(struct ast_channel *chan, - struct ast_rtp **rtp) -{ - struct unistim_subchannel *sub; - enum ast_rtp_get_result res = AST_RTP_GET_FAILED; + ao2_ref(sub->rtp, +1); + *instance = sub->rtp; - if (unistimdebug) - ast_verb(0, "unistim_get_rtp_peer called\n"); - - sub = chan->tech_pvt; - if (sub && sub->rtp) { - *rtp = sub->rtp; - res = AST_RTP_TRY_NATIVE; - } - - return res; + return AST_RTP_GLUE_RESULT_LOCAL; } -static int unistim_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, - struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) -{ - struct unistim_subchannel *sub; - - if (unistimdebug) - ast_verb(0, "unistim_set_rtp_peer called\n"); - - sub = chan->tech_pvt; - - if (sub) - return 0; - - return -1; -} - -static struct ast_rtp_protocol unistim_rtp = { +static struct ast_rtp_glue unistim_rtp_glue = { .type = channel_type, .get_rtp_info = unistim_get_rtp_peer, - .get_vrtp_info = unistim_get_vrtp_peer, - .set_rtp_peer = unistim_set_rtp_peer, }; /*--- load_module: PBX load module - initialization ---*/ @@ -5602,7 +5572,7 @@ goto chanreg_failed; } - ast_rtp_proto_register(&unistim_rtp); + ast_rtp_glue_register(&unistim_rtp_glue); ast_cli_register_multiple(unistim_cli, ARRAY_LEN(unistim_cli)); @@ -5633,7 +5603,7 @@ ast_cli_unregister_multiple(unistim_cli, ARRAY_LEN(unistim_cli)); ast_channel_unregister(&unistim_tech); - ast_rtp_proto_unregister(&unistim_rtp); + ast_rtp_glue_unregister(&unistim_rtp_glue); ast_mutex_lock(&monlock); if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) { Index: channels/chan_local.c =================================================================== --- a/channels/chan_local.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/chan_local.c (.../trunk) (revision 202568) @@ -39,7 +39,6 @@ #include "asterisk/pbx.h" #include "asterisk/sched.h" #include "asterisk/io.h" -#include "asterisk/rtp.h" #include "asterisk/acl.h" #include "asterisk/callerid.h" #include "asterisk/file.h" @@ -407,6 +406,40 @@ ast_moh_start(ast, data, NULL); } else if (condition == AST_CONTROL_UNHOLD) { ast_moh_stop(ast); + } else if (condition == AST_CONTROL_CONNECTED_LINE || condition == AST_CONTROL_REDIRECTING) { + struct ast_channel *this_channel; + struct ast_channel *the_other_channel; + /* A connected line update frame may only contain a partial amount of data, such + * as just a source, or just a ton, and not the full amount of information. However, + * the collected information is all stored in the outgoing channel's connectedline + * structure, so when receiving a connected line update on an outgoing local channel, + * we need to transmit the collected connected line information instead of whatever + * happens to be in this control frame. The same applies for redirecting information, which + * is why it is handled here as well.*/ + ast_mutex_lock(&p->lock); + isoutbound = IS_OUTBOUND(ast, p); + if (isoutbound) { + this_channel = p->chan; + the_other_channel = p->owner; + } else { + this_channel = p->owner; + the_other_channel = p->chan; + } + if (the_other_channel) { + unsigned char frame_data[1024]; + if (condition == AST_CONTROL_CONNECTED_LINE) { + f.datalen = ast_connected_line_build_data(frame_data, sizeof(frame_data), &this_channel->connected); + } else { + f.datalen = ast_redirecting_build_data(frame_data, sizeof(frame_data), &this_channel->redirecting); + } + f.subclass = condition; + f.data.ptr = frame_data; + if (!(res = local_queue_frame(p, isoutbound, &f, ast, 1))) { + ast_mutex_unlock(&p->lock); + } + } else { + ast_mutex_unlock(&p->lock); + } } else { /* Queue up a frame representing the indication as a control frame */ ast_mutex_lock(&p->lock); @@ -510,22 +543,53 @@ if (!p) return -1; - - ast_mutex_lock(&p->lock); + /* If you value your sanity, please don't look at this code */ +start_over: + while (ast_channel_trylock(p->chan)) { + ast_channel_unlock(p->owner); + usleep(1); + ast_channel_lock(p->owner); + } + + /* p->owner and p->chan are locked now. Let's get p locked */ + if (ast_mutex_trylock(&p->lock)) { + /* @#$&$@ */ + ast_channel_unlock(p->chan); + ast_channel_unlock(p->owner); + usleep(1); + ast_channel_lock(p->owner); + goto start_over; + } + /* * Note that cid_num and cid_name aren't passed in the ast_channel_alloc * call, so it's done here instead. + * + * All these failure points just return -1. The individual strings will + * be cleared when we destroy the channel. */ - p->chan->cid.cid_dnid = ast_strdup(p->owner->cid.cid_dnid); - p->chan->cid.cid_num = ast_strdup(p->owner->cid.cid_num); - p->chan->cid.cid_name = ast_strdup(p->owner->cid.cid_name); - p->chan->cid.cid_rdnis = ast_strdup(p->owner->cid.cid_rdnis); - p->chan->cid.cid_ani = ast_strdup(p->owner->cid.cid_ani); - p->chan->cid.cid_pres = p->owner->cid.cid_pres; - p->chan->cid.cid_ani2 = p->owner->cid.cid_ani2; - p->chan->cid.cid_ton = p->owner->cid.cid_ton; + if (p->owner->cid.cid_rdnis) { + if (!(p->chan->cid.cid_rdnis = ast_strdup(p->owner->cid.cid_rdnis))) { + ast_mutex_unlock(&p->lock); + ast_channel_unlock(p->chan); + return -1; + } + } + ast_party_redirecting_copy(&p->chan->redirecting, &p->owner->redirecting); + + if (p->owner->cid.cid_dnid) { + if (!(p->chan->cid.cid_dnid = ast_strdup(p->owner->cid.cid_dnid))) { + ast_mutex_unlock(&p->lock); + ast_channel_unlock(p->chan); + return -1; + } + } p->chan->cid.cid_tns = p->owner->cid.cid_tns; + + ast_connected_line_copy_to_caller(&p->chan->cid, &p->owner->connected); + ast_connected_line_copy_from_caller(&p->chan->connected, &p->owner->cid); + ast_string_field_set(p->chan, language, p->owner->language); ast_string_field_set(p->chan, accountcode, p->owner->accountcode); ast_string_field_set(p->chan, musicclass, p->owner->musicclass); @@ -535,6 +599,7 @@ if (!ast_exists_extension(NULL, p->chan->context, p->chan->exten, 1, p->owner->cid.cid_num)) { ast_log(LOG_NOTICE, "No such extension/context %s@%s while calling Local channel\n", p->chan->exten, p->chan->context); ast_mutex_unlock(&p->lock); + ast_channel_unlock(p->chan); return -1; } @@ -561,6 +626,7 @@ ast_set_flag(p, LOCAL_LAUNCHED_PBX); ast_mutex_unlock(&p->lock); + ast_channel_unlock(p->chan); return res; } @@ -734,13 +800,12 @@ ama = 0; if (!(tmp = ast_channel_alloc(1, state, 0, 0, t, p->exten, p->context, ama, "Local/%s@%s-%04x;1", p->exten, p->context, randnum)) || !(tmp2 = ast_channel_alloc(1, AST_STATE_RING, 0, 0, t, p->exten, p->context, ama, "Local/%s@%s-%04x;2", p->exten, p->context, randnum))) { - if (tmp) - ast_channel_free(tmp); - if (tmp2) - ast_channel_free(tmp2); + if (tmp) { + tmp = ast_channel_release(tmp); + } ast_log(LOG_WARNING, "Unable to allocate channel structure(s)\n"); return NULL; - } + } tmp2->tech = tmp->tech = &local_tech; Index: channels/chan_bridge.c =================================================================== --- a/channels/chan_bridge.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/chan_bridge.c (.../trunk) (revision 202568) @@ -39,7 +39,6 @@ #include "asterisk/pbx.h" #include "asterisk/sched.h" #include "asterisk/io.h" -#include "asterisk/rtp.h" #include "asterisk/acl.h" #include "asterisk/callerid.h" #include "asterisk/file.h" @@ -205,7 +204,7 @@ return NULL; } if (!(p->output = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", 0, "Bridge/%p-output", p))) { - ast_channel_free(p->input); + p->input = ast_channel_release(p->input); ast_free(p); return NULL; } Index: channels/vcodecs.c =================================================================== --- a/channels/vcodecs.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/vcodecs.c (.../trunk) (revision 202568) @@ -1113,7 +1113,7 @@ //struct video_codec_desc *codec_desc; }; -static struct _cm video_formats[] = { +static const struct _cm video_formats[] = { { AST_FORMAT_H263_PLUS, CODEC_ID_H263, CM_RD }, /* incoming H263P ? */ { AST_FORMAT_H263_PLUS, CODEC_ID_H263P, CM_WR }, { AST_FORMAT_H263, CODEC_ID_H263, CM_RD }, @@ -1137,7 +1137,7 @@ } /* pointers to supported codecs. We assume the first one to be non null. */ -static struct video_codec_desc *supported_codecs[] = { +static const struct video_codec_desc *supported_codecs[] = { &h263p_codec, &h264_codec, &h263_codec, Index: channels/misdn/isdn_msg_parser.c =================================================================== --- a/channels/misdn/isdn_msg_parser.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/misdn/isdn_msg_parser.c (.../trunk) (revision 202568) @@ -11,7 +11,7 @@ * the GNU General Public License */ -/*! \file +/*! \file * \brief Interface to mISDN - message parser * \author Christian Richter */ @@ -25,21 +25,131 @@ #include "ie.c" +/*! + * \internal + * \brief Build the name, number, name/number display message string + * + * \param display Display buffer to fill in + * \param display_length Length of the display buffer to fill in + * \param display_format Display format enumeration + * \param name Name string to use + * \param number Number string to use + * + * \return Nothing + */ +static void build_display_str(char *display, size_t display_length, int display_format, const char *name, const char *number) +{ + display[0] = 0; + switch (display_format) { + default: + case 0: /* none */ + break; + case 1: /* name */ + snprintf(display, display_length, "%s", name); + break; + + case 2: /* number */ + snprintf(display, display_length, "%s", number); + break; + + case 3: /* both */ + if (name[0] || number[0]) { + snprintf(display, display_length, "\"%s\" <%s>", name, number); + } + break; + } +} + +/*! + * \internal + * \brief Encode the Facility IE and put it into the message structure. + * + * \param ntmode Where the encoded facility was put when in NT mode. + * \param msg General message structure + * \param fac Data to encode into the facility ie. + * \param nt TRUE if in NT mode. + * + * \return Nothing + */ +static void enc_ie_facility(unsigned char **ntmode, msg_t *msg, struct FacParm *fac, int nt) +{ + int len; + Q931_info_t *qi; + unsigned char *p; + unsigned char buf[256]; + + len = encodeFac(buf, fac); + if (len <= 0) { + /* + * mISDN does not know how to build the requested facility structure + * Clear facility information + */ + fac->Function = Fac_None; + return; + } + + p = msg_put(msg, len); + if (nt) { + *ntmode = p + 1; + } else { + qi = (Q931_info_t *) (msg->data + mISDN_HEADER_LEN); + qi->QI_ELEMENT(facility) = p - (unsigned char *) qi - sizeof(Q931_info_t); + } + + memcpy(p, buf, len); + + /* Clear facility information */ + fac->Function = Fac_None; +} + +/*! + * \internal + * \brief Decode the Facility IE. + * + * \param p Encoded facility ie data to decode. (NT mode) + * \param qi Encoded facility ie data to decode. (TE mode) + * \param fac Where to put the decoded facility ie data if it is available. + * \param nt TRUE if in NT mode. + * \param bc Associated B channel + * + * \return Nothing + */ +static void dec_ie_facility(unsigned char *p, Q931_info_t *qi, struct FacParm *fac, int nt, struct misdn_bchannel *bc) +{ + fac->Function = Fac_None; + + if (!nt) { + p = NULL; + if (qi->QI_ELEMENT(facility)) { + p = (unsigned char *) qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(facility) + 1; + } + } + if (!p) { + return; + } + + if (decodeFac(p, fac)) { + cb_log(3, bc->port, "Decoding facility ie failed! Unrecognized facility message?\n"); + } +} + + + static void set_channel(struct misdn_bchannel *bc, int channel) { cb_log(3,bc->port,"set_channel: bc->channel:%d channel:%d\n", bc->channel, channel); - - + + if (channel==0xff) { /* any channel */ channel=-1; } - + /* ALERT: is that everytime true ? */ if (channel > 0 && bc->nt ) { - + if (bc->channel && ( bc->channel != 0xff) ) { cb_log(0,bc->port,"We already have a channel (%d)\n", bc->channel); } else { @@ -47,179 +157,289 @@ cb_event(EVENT_NEW_CHANNEL,bc,NULL); } } - + if (channel > 0 && !bc->nt ) { bc->channel = channel; cb_event(EVENT_NEW_CHANNEL,bc,NULL); } } -static void parse_proceeding (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_proceeding (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - CALL_PROCEEDING_t *proceeding=(CALL_PROCEEDING_t*)((unsigned long)msg->data+ HEADER_LEN); + CALL_PROCEEDING_t *proceeding = (CALL_PROCEEDING_t *) (msg->data + HEADER_LEN); //struct misdn_stack *stack=get_stack_by_bc(bc); - + { int exclusive, channel; dec_ie_channel_id(proceeding->CHANNEL_ID, (Q931_info_t *)proceeding, &exclusive, &channel, nt,bc); set_channel(bc,channel); - + } - + dec_ie_progress(proceeding->PROGRESS, (Q931_info_t *)proceeding, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); - - -#ifdef DEBUG - printf("Parsing PROCEEDING Msg\n"); + + dec_ie_facility(proceeding->FACILITY, (Q931_info_t *) proceeding, &bc->fac_in, nt, bc); + + /* dec_ie_redir_dn */ + +#ifdef DEBUG + printf("Parsing PROCEEDING Msg\n"); #endif } static msg_t *build_proceeding (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; CALL_PROCEEDING_t *proceeding; - msg_t *msg =(msg_t*)create_l3msg(CC_PROCEEDING | REQUEST, MT_CALL_PROCEEDING, bc?bc->l3_id:-1, sizeof(CALL_PROCEEDING_t) ,nt); - + msg_t *msg =(msg_t*)create_l3msg(CC_PROCEEDING | REQUEST, MT_CALL_PROCEEDING, bc?bc->l3_id:-1, sizeof(CALL_PROCEEDING_t) ,nt); + proceeding=(CALL_PROCEEDING_t*)((msg->data+HEADER_LEN)); enc_ie_channel_id(&proceeding->CHANNEL_ID, msg, 1,bc->channel, nt,bc); - - if (nt) + + if (nt) enc_ie_progress(&proceeding->PROGRESS, msg, 0, nt?1:5, 8, nt,bc); - -#ifdef DEBUG - printf("Building PROCEEDING Msg\n"); + if (bc->fac_out.Function != Fac_None) { + enc_ie_facility(&proceeding->FACILITY, msg, &bc->fac_out, nt); + } + + /* enc_ie_redir_dn */ + +#ifdef DEBUG + printf("Building PROCEEDING Msg\n"); #endif - return msg; + return msg; } -static void parse_alerting (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_alerting (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - ALERTING_t *alerting=(ALERTING_t*)((unsigned long)(msg->data+HEADER_LEN)); + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + ALERTING_t *alerting = (ALERTING_t *) (msg->data + HEADER_LEN); //Q931_info_t *qi=(Q931_info_t*)(msg->data+HEADER_LEN); - + + dec_ie_facility(alerting->FACILITY, (Q931_info_t *) alerting, &bc->fac_in, nt, bc); + + /* dec_ie_redir_dn */ + dec_ie_progress(alerting->PROGRESS, (Q931_info_t *)alerting, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); - -#ifdef DEBUG - printf("Parsing ALERTING Msg\n"); + +#ifdef DEBUG + printf("Parsing ALERTING Msg\n"); #endif - + } -static msg_t *build_alerting (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_alerting (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; ALERTING_t *alerting; - msg_t *msg =(msg_t*)create_l3msg(CC_ALERTING | REQUEST, MT_ALERTING, bc?bc->l3_id:-1, sizeof(ALERTING_t) ,nt); - - alerting=(ALERTING_t*)((msg->data+HEADER_LEN)); - + msg_t *msg =(msg_t*)create_l3msg(CC_ALERTING | REQUEST, MT_ALERTING, bc?bc->l3_id:-1, sizeof(ALERTING_t) ,nt); + + alerting=(ALERTING_t*)((msg->data+HEADER_LEN)); + enc_ie_channel_id(&alerting->CHANNEL_ID, msg, 1,bc->channel, nt,bc); - - if (nt) + + if (nt) enc_ie_progress(&alerting->PROGRESS, msg, 0, nt?1:5, 8, nt,bc); -#ifdef DEBUG - printf("Building ALERTING Msg\n"); + + if (bc->fac_out.Function != Fac_None) { + enc_ie_facility(&alerting->FACILITY, msg, &bc->fac_out, nt); + } + + /* enc_ie_redir_dn */ + +#ifdef DEBUG + printf("Building ALERTING Msg\n"); #endif - return msg; + return msg; } -static void parse_progress (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_progress (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - PROGRESS_t *progress=(PROGRESS_t*)((unsigned long)(msg->data+HEADER_LEN)); - //Q931_info_t *qi=(Q931_info_t*)(msg->data+HEADER_LEN); - + PROGRESS_t *progress = (PROGRESS_t *) (msg->data + HEADER_LEN); + //Q931_info_t *qi=(Q931_info_t*)(msg->data+HEADER_LEN); + dec_ie_progress(progress->PROGRESS, (Q931_info_t *)progress, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); - -#ifdef DEBUG - printf("Parsing PROGRESS Msg\n"); + + dec_ie_facility(progress->FACILITY, (Q931_info_t *) progress, &bc->fac_in, nt, bc); + +#ifdef DEBUG + printf("Parsing PROGRESS Msg\n"); #endif } -static msg_t *build_progress (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_progress (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; PROGRESS_t *progress; - msg_t *msg =(msg_t*)create_l3msg(CC_PROGRESS | REQUEST, MT_PROGRESS, bc?bc->l3_id:-1, sizeof(PROGRESS_t) ,nt); - - progress=(PROGRESS_t*)((msg->data+HEADER_LEN)); + msg_t *msg =(msg_t*)create_l3msg(CC_PROGRESS | REQUEST, MT_PROGRESS, bc?bc->l3_id:-1, sizeof(PROGRESS_t) ,nt); -#ifdef DEBUG - printf("Building PROGRESS Msg\n"); + progress=(PROGRESS_t*)((msg->data+HEADER_LEN)); + + if (bc->fac_out.Function != Fac_None) { + enc_ie_facility(&progress->FACILITY, msg, &bc->fac_out, nt); + } + +#ifdef DEBUG + printf("Building PROGRESS Msg\n"); #endif - return msg; + return msg; } -static void parse_setup (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - SETUP_t *setup= (SETUP_t*)((unsigned long)msg->data+HEADER_LEN); - Q931_info_t *qi=(Q931_info_t*)((unsigned long)msg->data+HEADER_LEN); +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Extract the SETUP message's BC, HLC, and LLC encoded ie contents. + * + * \param setup Indexed setup message contents + * \param nt TRUE if in NT mode. + * \param bc Associated B channel + * + * \return Nothing + */ +static void extract_setup_Bc_Hlc_Llc(SETUP_t *setup, int nt, struct misdn_bchannel *bc) +{ + __u8 *p; + Q931_info_t *qi; -#ifdef DEBUG - printf("Parsing SETUP Msg\n"); -#endif - { - int type,plan,present, screen; - char id[32]; - dec_ie_calling_pn(setup->CALLING_PN, qi, &type, &plan, &present, &screen, id, sizeof(id)-1, nt,bc); + qi = (Q931_info_t *) setup; - bc->onumplan=type; - strcpy(bc->oad, id); - switch (present) { - case 0: - bc->pres=0; /* screened */ - break; - case 1: - bc->pres=1; /* not screened */ - break; - default: - bc->pres=0; + /* Extract Bearer Capability */ + if (nt) { + p = (__u8 *) setup->BEARER; + } else { + if (qi->QI_ELEMENT(bearer_capability)) { + p = (__u8 *) qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(bearer_capability) + 1; + } else { + p = NULL; } - switch (screen) { - case 0: - break; - default: - ; - } } - { - int type, plan; - char number[32]; - dec_ie_called_pn(setup->CALLED_PN, (Q931_info_t *)setup, &type, &plan, number, sizeof(number)-1, nt,bc); - strcpy(bc->dad, number); - bc->dnumplan=type; + if (!p || *p == 0 || sizeof(bc->setup_bc_hlc_llc.Bc.Contents) < *p) { + bc->setup_bc_hlc_llc.Bc.Length = 0; + } else { + bc->setup_bc_hlc_llc.Bc.Length = *p; + memcpy(bc->setup_bc_hlc_llc.Bc.Contents, p + 1, *p); } - { - char keypad[32]; - dec_ie_keypad(setup->KEYPAD, (Q931_info_t *)setup, keypad, sizeof(keypad)-1, nt,bc); - strcpy(bc->keypad, keypad); + + /* Extract Low Layer Compatibility */ + if (nt) { + p = (__u8 *) setup->LLC; + } else { + if (qi->QI_ELEMENT(llc)) { + p = (__u8 *) qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(llc) + 1; + } else { + p = NULL; + } } + if (!p || *p == 0 || sizeof(bc->setup_bc_hlc_llc.Llc.Contents) < *p) { + bc->setup_bc_hlc_llc.Llc.Length = 0; + } else { + bc->setup_bc_hlc_llc.Llc.Length = *p; + memcpy(bc->setup_bc_hlc_llc.Llc.Contents, p + 1, *p); + } - { - dec_ie_complete(setup->COMPLETE, (Q931_info_t *)setup, &bc->sending_complete, nt,bc); - + /* Extract High Layer Compatibility */ + if (nt) { + p = (__u8 *) setup->HLC; + } else { + if (qi->QI_ELEMENT(hlc)) { + p = (__u8 *) qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(hlc) + 1; + } else { + p = NULL; + } } - - { - int type, plan, present, screen, reason; - char id[32]; - dec_ie_redir_nr(setup->REDIR_NR, (Q931_info_t *)setup, &type, &plan, &present, &screen, &reason, id, sizeof(id)-1, nt,bc); - - strcpy(bc->rad, id); - bc->rnumplan=type; + if (!p || *p == 0 || sizeof(bc->setup_bc_hlc_llc.Hlc.Contents) < *p) { + bc->setup_bc_hlc_llc.Hlc.Length = 0; + } else { + bc->setup_bc_hlc_llc.Hlc.Length = *p; + memcpy(bc->setup_bc_hlc_llc.Hlc.Contents, p + 1, *p); } +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +static void parse_setup (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +{ + int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; + SETUP_t *setup = (SETUP_t *) (msg->data + HEADER_LEN); + Q931_info_t *qi = (Q931_info_t *) (msg->data + HEADER_LEN); + int type; + int plan; + int present; + int screen; + int reason; + +#ifdef DEBUG + printf("Parsing SETUP Msg\n"); +#endif + + dec_ie_calling_pn(setup->CALLING_PN, qi, &type, &plan, &present, &screen, bc->caller.number, sizeof(bc->caller.number), nt, bc); + bc->caller.number_type = type; + bc->caller.number_plan = plan; + switch (present) { + default: + case 0: + bc->caller.presentation = 0; /* presentation allowed */ + break; + case 1: + bc->caller.presentation = 1; /* presentation restricted */ + break; + case 2: + bc->caller.presentation = 2; /* Number not available */ + break; + } + if (0 <= screen) { + bc->caller.screening = screen; + } else { + bc->caller.screening = 0; /* Unscreened */ + } + + dec_ie_facility(setup->FACILITY, (Q931_info_t *) setup, &bc->fac_in, nt, bc); + + dec_ie_called_pn(setup->CALLED_PN, (Q931_info_t *) setup, &type, &plan, bc->dialed.number, sizeof(bc->dialed.number), nt, bc); + bc->dialed.number_type = type; + bc->dialed.number_plan = plan; + + dec_ie_keypad(setup->KEYPAD, (Q931_info_t *) setup, bc->keypad, sizeof(bc->keypad), nt, bc); + + dec_ie_complete(setup->COMPLETE, (Q931_info_t *) setup, &bc->sending_complete, nt, bc); + + dec_ie_redir_nr(setup->REDIR_NR, (Q931_info_t *) setup, &type, &plan, &present, &screen, &reason, bc->redirecting.from.number, sizeof(bc->redirecting.from.number), nt, bc); + bc->redirecting.from.number_type = type; + bc->redirecting.from.number_plan = plan; + switch (present) { + default: + case 0: + bc->redirecting.from.presentation = 0; /* presentation allowed */ + break; + case 1: + bc->redirecting.from.presentation = 1; /* presentation restricted */ + break; + case 2: + bc->redirecting.from.presentation = 2; /* Number not available */ + break; + } + if (0 <= screen) { + bc->redirecting.from.screening = screen; + } else { + bc->redirecting.from.screening = 0; /* Unscreened */ + } + if (0 <= reason) { + bc->redirecting.reason = reason; + } else { + bc->redirecting.reason = mISDN_REDIRECTING_REASON_UNKNOWN; + } + { int coding, capability, mode, rate, multi, user, async, urate, stopbits, dbits, parity; + dec_ie_bearer(setup->BEARER, (Q931_info_t *)setup, &coding, &capability, &mode, &rate, &multi, &user, &async, &urate, &stopbits, &dbits, &parity, nt,bc); switch (capability) { - case -1: bc->capability=INFO_CAPABILITY_DIGITAL_UNRESTRICTED; + case -1: bc->capability=INFO_CAPABILITY_DIGITAL_UNRESTRICTED; break; case 0: bc->capability=INFO_CAPABILITY_SPEECH; break; @@ -228,7 +448,7 @@ case 8: bc->capability=INFO_CAPABILITY_DIGITAL_UNRESTRICTED; bc->user1 = user; bc->urate = urate; - + bc->rate = rate; bc->mode = mode; break; @@ -237,7 +457,7 @@ default: break; } - + switch(user) { case 2: bc->law=INFO_CODEC_ULAW; @@ -247,15 +467,15 @@ break; default: bc->law=INFO_CODEC_ALAW; - + } - - bc->capability=capability; + + bc->capability=capability; } { int exclusive, channel; dec_ie_channel_id(setup->CHANNEL_ID, (Q931_info_t *)setup, &exclusive, &channel, nt,bc); - + set_channel(bc,channel); } @@ -266,55 +486,85 @@ else cb_log(1,bc->port,"NO USERUESRINFO\n"); } - + dec_ie_progress(setup->PROGRESS, (Q931_info_t *)setup, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); - + +#if defined(AST_MISDN_ENHANCEMENTS) + extract_setup_Bc_Hlc_Llc(setup, nt, bc); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ } -#define ANY_CHANNEL 0xff /* IE attribut for 'any channel' */ -static msg_t *build_setup (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +#define ANY_CHANNEL 0xff /* IE attribute for 'any channel' */ +static msg_t *build_setup (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; SETUP_t *setup; - msg_t *msg =(msg_t*)create_l3msg(CC_SETUP | REQUEST, MT_SETUP, bc?bc->l3_id:-1, sizeof(SETUP_t) ,nt); - - setup=(SETUP_t*)((msg->data+HEADER_LEN)); - + msg_t *msg =(msg_t*)create_l3msg(CC_SETUP | REQUEST, MT_SETUP, bc?bc->l3_id:-1, sizeof(SETUP_t) ,nt); + + setup=(SETUP_t*)((msg->data+HEADER_LEN)); + if (bc->channel == 0 || bc->channel == ANY_CHANNEL || bc->channel==-1) enc_ie_channel_id(&setup->CHANNEL_ID, msg, 0, bc->channel, nt,bc); - else + else enc_ie_channel_id(&setup->CHANNEL_ID, msg, 1, bc->channel, nt,bc); - - - { - int type=bc->onumplan,plan=1,present=bc->pres,screen=bc->screen; - enc_ie_calling_pn(&setup->CALLING_PN, msg, type, plan, present, - screen, bc->oad, nt, bc); + + if (bc->fac_out.Function != Fac_None) { + enc_ie_facility(&setup->FACILITY, msg, &bc->fac_out, nt); } - - { - if (bc->dad[0]) - enc_ie_called_pn(&setup->CALLED_PN, msg, bc->dnumplan, 1, bc->dad, nt,bc); + + enc_ie_calling_pn(&setup->CALLING_PN, msg, bc->caller.number_type, bc->caller.number_plan, + bc->caller.presentation, bc->caller.screening, bc->caller.number, nt, bc); + + if (bc->dialed.number[0]) { + enc_ie_called_pn(&setup->CALLED_PN, msg, bc->dialed.number_type, bc->dialed.number_plan, bc->dialed.number, nt, bc); } - { - if (bc->rad[0]) - enc_ie_redir_nr(&setup->REDIR_NR, msg, 1, 1, bc->pres, bc->screen, 0, bc->rad, nt,bc); + switch (bc->outgoing_colp) { + case 0:/* pass */ + case 1:/* restricted */ + if (bc->redirecting.from.number[0]) { +#if 1 + /* ETSI and Q.952 do not define the screening field */ + enc_ie_redir_nr(&setup->REDIR_NR, msg, bc->redirecting.from.number_type, + bc->redirecting.from.number_plan, bc->redirecting.from.presentation, 0, + bc->redirecting.reason, bc->redirecting.from.number, nt, bc); +#else + /* Q.931 defines the screening field */ + enc_ie_redir_nr(&setup->REDIR_NR, msg, bc->redirecting.from.number_type, + bc->redirecting.from.number_plan, bc->redirecting.from.presentation, + bc->redirecting.from.screening, bc->redirecting.reason, + bc->redirecting.from.number, nt, bc); +#endif + } + break; + default: + break; } - { - if (bc->keypad[0]) - enc_ie_keypad(&setup->KEYPAD, msg, bc->keypad, nt,bc); + if (bc->keypad[0]) { + enc_ie_keypad(&setup->KEYPAD, msg, bc->keypad, nt,bc); } - - + + + if (*bc->display) { - enc_ie_display(&setup->DISPLAY, msg, bc->display, nt,bc); + enc_ie_display(&setup->DISPLAY, msg, bc->display, nt, bc); + } else if (nt && bc->caller.presentation == 0) { + char display[sizeof(bc->display)]; + + /* Presentation is allowed */ + build_display_str(display, sizeof(display), bc->display_setup, bc->caller.name, bc->caller.number); + if (display[0]) { + enc_ie_display(&setup->DISPLAY, msg, display, nt, bc); + } } - + { - int coding=0, capability, mode=0 /* 2 for packet ! */ - ,user, rate=0x10; + int coding = 0; + int capability; + int mode = 0; /* 2 for packet! */ + int user; + int rate = 0x10; switch (bc->law) { case INFO_CODEC_ULAW: user=2; @@ -324,7 +574,7 @@ default: user=3; } - + switch (bc->capability) { case INFO_CAPABILITY_SPEECH: capability = 0; break; @@ -337,84 +587,129 @@ user=-1; break; default: - capability=bc->capability; + capability=bc->capability; } - - - + enc_ie_bearer(&setup->BEARER, msg, coding, capability, mode, rate, -1, user, nt,bc); } if (bc->sending_complete) { enc_ie_complete(&setup->COMPLETE,msg, bc->sending_complete, nt, bc); } - + if (bc->uulen) { int protocol=4; enc_ie_useruser(&setup->USER_USER, msg, protocol, bc->uu, bc->uulen, nt,bc); cb_log(1,bc->port,"ENCODING USERUESRINFO:%s\n",bc->uu); } -#ifdef DEBUG - printf("Building SETUP Msg\n"); +#if defined(AST_MISDN_ENHANCEMENTS) + extract_setup_Bc_Hlc_Llc(setup, nt, bc); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#ifdef DEBUG + printf("Building SETUP Msg\n"); #endif - return msg; + return msg; } -static void parse_connect (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_connect (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - CONNECT_t *connect=(CONNECT_t*)((unsigned long)(msg->data+HEADER_LEN)); - - int plan,pres,screen; - + CONNECT_t *connect = (CONNECT_t *) (msg->data + HEADER_LEN); + int type; + int plan; + int pres; + int screen; + bc->ces = connect->ces; - bc->ces = connect->ces; dec_ie_progress(connect->PROGRESS, (Q931_info_t *)connect, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); - dec_ie_connected_pn(connect->CONNECT_PN,(Q931_info_t *)connect, &bc->cpnnumplan, &plan, &pres, &screen, bc->cad, 31, nt, bc); + dec_ie_connected_pn(connect->CONNECT_PN, (Q931_info_t *) connect, &type, &plan, + &pres, &screen, bc->connected.number, sizeof(bc->connected.number), nt, bc); + bc->connected.number_type = type; + bc->connected.number_plan = plan; + switch (pres) { + default: + case 0: + bc->connected.presentation = 0; /* presentation allowed */ + break; + case 1: + bc->connected.presentation = 1; /* presentation restricted */ + break; + case 2: + bc->connected.presentation = 2; /* Number not available */ + break; + } + if (0 <= screen) { + bc->connected.screening = screen; + } else { + bc->connected.screening = 0; /* Unscreened */ + } + dec_ie_facility(connect->FACILITY, (Q931_info_t *) connect, &bc->fac_in, nt, bc); + /* cb_log(1,bc->port,"CONNETED PN: %s cpn_dialplan:%d\n", connected_pn, type); */ - -#ifdef DEBUG - printf("Parsing CONNECT Msg\n"); + +#ifdef DEBUG + printf("Parsing CONNECT Msg\n"); #endif } -static msg_t *build_connect (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_connect (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; CONNECT_t *connect; - msg_t *msg =(msg_t*)create_l3msg(CC_CONNECT | REQUEST, MT_CONNECT, bc?bc->l3_id:-1, sizeof(CONNECT_t) ,nt); - + msg_t *msg =(msg_t*)create_l3msg(CC_CONNECT | REQUEST, MT_CONNECT, bc?bc->l3_id:-1, sizeof(CONNECT_t) ,nt); + cb_log(6,bc->port,"BUILD_CONNECT: bc:%p bc->l3id:%d, nt:%d\n",bc,bc->l3_id,nt); - connect=(CONNECT_t*)((msg->data+HEADER_LEN)); + connect=(CONNECT_t*)((msg->data+HEADER_LEN)); if (nt) { time_t now; time(&now); enc_ie_date(&connect->DATE, msg, now, nt,bc); } - - { - int type=bc->cpnnumplan, plan=1, present=2, screen=0; - enc_ie_connected_pn(&connect->CONNECT_PN, msg, type,plan, present, screen, bc->cad, nt , bc); + + switch (bc->outgoing_colp) { + case 0:/* pass */ + case 1:/* restricted */ + enc_ie_connected_pn(&connect->CONNECT_PN, msg, bc->connected.number_type, + bc->connected.number_plan, bc->connected.presentation, + bc->connected.screening, bc->connected.number, nt, bc); + break; + default: + break; } -#ifdef DEBUG - printf("Building CONNECT Msg\n"); + if (nt && bc->connected.presentation == 0) { + char display[sizeof(bc->display)]; + + /* Presentation is allowed */ + build_display_str(display, sizeof(display), bc->display_connected, bc->connected.name, bc->connected.number); + if (display[0]) { + enc_ie_display(&connect->DISPLAY, msg, display, nt, bc); + } + } + + if (bc->fac_out.Function != Fac_None) { + enc_ie_facility(&connect->FACILITY, msg, &bc->fac_out, nt); + } + +#ifdef DEBUG + printf("Building CONNECT Msg\n"); #endif - return msg; + return msg; } -static void parse_setup_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_setup_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - SETUP_ACKNOWLEDGE_t *setup_acknowledge=(SETUP_ACKNOWLEDGE_t*)((unsigned long)(msg->data+HEADER_LEN)); + SETUP_ACKNOWLEDGE_t *setup_acknowledge = (SETUP_ACKNOWLEDGE_t *) (msg->data + HEADER_LEN); { int exclusive, channel; @@ -423,428 +718,443 @@ set_channel(bc, channel); } - + dec_ie_progress(setup_acknowledge->PROGRESS, (Q931_info_t *)setup_acknowledge, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); -#ifdef DEBUG - printf("Parsing SETUP_ACKNOWLEDGE Msg\n"); + + dec_ie_facility(setup_acknowledge->FACILITY, (Q931_info_t *) setup_acknowledge, &bc->fac_in, nt, bc); + +#ifdef DEBUG + printf("Parsing SETUP_ACKNOWLEDGE Msg\n"); #endif - + } -static msg_t *build_setup_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_setup_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; SETUP_ACKNOWLEDGE_t *setup_acknowledge; - msg_t *msg =(msg_t*)create_l3msg(CC_SETUP_ACKNOWLEDGE | REQUEST, MT_SETUP_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(SETUP_ACKNOWLEDGE_t) ,nt); - - setup_acknowledge=(SETUP_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); - + msg_t *msg =(msg_t*)create_l3msg(CC_SETUP_ACKNOWLEDGE | REQUEST, MT_SETUP_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(SETUP_ACKNOWLEDGE_t) ,nt); + + setup_acknowledge=(SETUP_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); + enc_ie_channel_id(&setup_acknowledge->CHANNEL_ID, msg, 1,bc->channel, nt,bc); - - if (nt) + + if (nt) enc_ie_progress(&setup_acknowledge->PROGRESS, msg, 0, nt?1:5, 8, nt,bc); - -#ifdef DEBUG - printf("Building SETUP_ACKNOWLEDGE Msg\n"); + + if (bc->fac_out.Function != Fac_None) { + enc_ie_facility(&setup_acknowledge->FACILITY, msg, &bc->fac_out, nt); + } + +#ifdef DEBUG + printf("Building SETUP_ACKNOWLEDGE Msg\n"); #endif - return msg; + return msg; } -static void parse_connect_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_connect_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { -#ifdef DEBUG - printf("Parsing CONNECT_ACKNOWLEDGE Msg\n"); +#ifdef DEBUG + printf("Parsing CONNECT_ACKNOWLEDGE Msg\n"); #endif - + } -static msg_t *build_connect_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_connect_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; CONNECT_ACKNOWLEDGE_t *connect_acknowledge; - msg_t *msg =(msg_t*)create_l3msg(CC_CONNECT | RESPONSE, MT_CONNECT, bc?bc->l3_id:-1, sizeof(CONNECT_ACKNOWLEDGE_t) ,nt); - - connect_acknowledge=(CONNECT_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); - + msg_t *msg =(msg_t*)create_l3msg(CC_CONNECT | RESPONSE, MT_CONNECT, bc?bc->l3_id:-1, sizeof(CONNECT_ACKNOWLEDGE_t) ,nt); + + connect_acknowledge=(CONNECT_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); + enc_ie_channel_id(&connect_acknowledge->CHANNEL_ID, msg, 1, bc->channel, nt,bc); - -#ifdef DEBUG - printf("Building CONNECT_ACKNOWLEDGE Msg\n"); + +#ifdef DEBUG + printf("Building CONNECT_ACKNOWLEDGE Msg\n"); #endif - return msg; + return msg; } -static void parse_user_information (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_user_information (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { -#ifdef DEBUG - printf("Parsing USER_INFORMATION Msg\n"); +#ifdef DEBUG + printf("Parsing USER_INFORMATION Msg\n"); #endif - + } -static msg_t *build_user_information (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_user_information (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; USER_INFORMATION_t *user_information; - msg_t *msg =(msg_t*)create_l3msg(CC_USER_INFORMATION | REQUEST, MT_USER_INFORMATION, bc?bc->l3_id:-1, sizeof(USER_INFORMATION_t) ,nt); - - user_information=(USER_INFORMATION_t*)((msg->data+HEADER_LEN)); + msg_t *msg =(msg_t*)create_l3msg(CC_USER_INFORMATION | REQUEST, MT_USER_INFORMATION, bc?bc->l3_id:-1, sizeof(USER_INFORMATION_t) ,nt); -#ifdef DEBUG - printf("Building USER_INFORMATION Msg\n"); + user_information=(USER_INFORMATION_t*)((msg->data+HEADER_LEN)); + +#ifdef DEBUG + printf("Building USER_INFORMATION Msg\n"); #endif - return msg; + return msg; } -static void parse_suspend_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_suspend_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { -#ifdef DEBUG - printf("Parsing SUSPEND_REJECT Msg\n"); +#ifdef DEBUG + printf("Parsing SUSPEND_REJECT Msg\n"); #endif - + } -static msg_t *build_suspend_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_suspend_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; SUSPEND_REJECT_t *suspend_reject; - msg_t *msg =(msg_t*)create_l3msg(CC_SUSPEND_REJECT | REQUEST, MT_SUSPEND_REJECT, bc?bc->l3_id:-1, sizeof(SUSPEND_REJECT_t) ,nt); - - suspend_reject=(SUSPEND_REJECT_t*)((msg->data+HEADER_LEN)); + msg_t *msg =(msg_t*)create_l3msg(CC_SUSPEND_REJECT | REQUEST, MT_SUSPEND_REJECT, bc?bc->l3_id:-1, sizeof(SUSPEND_REJECT_t) ,nt); -#ifdef DEBUG - printf("Building SUSPEND_REJECT Msg\n"); + suspend_reject=(SUSPEND_REJECT_t*)((msg->data+HEADER_LEN)); + +#ifdef DEBUG + printf("Building SUSPEND_REJECT Msg\n"); #endif - return msg; + return msg; } -static void parse_resume_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_resume_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { -#ifdef DEBUG - printf("Parsing RESUME_REJECT Msg\n"); +#ifdef DEBUG + printf("Parsing RESUME_REJECT Msg\n"); #endif - + } -static msg_t *build_resume_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_resume_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; RESUME_REJECT_t *resume_reject; - msg_t *msg =(msg_t*)create_l3msg(CC_RESUME_REJECT | REQUEST, MT_RESUME_REJECT, bc?bc->l3_id:-1, sizeof(RESUME_REJECT_t) ,nt); - - resume_reject=(RESUME_REJECT_t*)((msg->data+HEADER_LEN)); + msg_t *msg =(msg_t*)create_l3msg(CC_RESUME_REJECT | REQUEST, MT_RESUME_REJECT, bc?bc->l3_id:-1, sizeof(RESUME_REJECT_t) ,nt); -#ifdef DEBUG - printf("Building RESUME_REJECT Msg\n"); + resume_reject=(RESUME_REJECT_t*)((msg->data+HEADER_LEN)); + +#ifdef DEBUG + printf("Building RESUME_REJECT Msg\n"); #endif - return msg; + return msg; } -static void parse_hold (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_hold (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { -#ifdef DEBUG - printf("Parsing HOLD Msg\n"); +#ifdef DEBUG + printf("Parsing HOLD Msg\n"); #endif - + } -static msg_t *build_hold (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_hold (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; HOLD_t *hold; - msg_t *msg =(msg_t*)create_l3msg(CC_HOLD | REQUEST, MT_HOLD, bc?bc->l3_id:-1, sizeof(HOLD_t) ,nt); - - hold=(HOLD_t*)((msg->data+HEADER_LEN)); + msg_t *msg =(msg_t*)create_l3msg(CC_HOLD | REQUEST, MT_HOLD, bc?bc->l3_id:-1, sizeof(HOLD_t) ,nt); -#ifdef DEBUG - printf("Building HOLD Msg\n"); + hold=(HOLD_t*)((msg->data+HEADER_LEN)); + +#ifdef DEBUG + printf("Building HOLD Msg\n"); #endif - return msg; + return msg; } -static void parse_suspend (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_suspend (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { -#ifdef DEBUG - printf("Parsing SUSPEND Msg\n"); +#ifdef DEBUG + printf("Parsing SUSPEND Msg\n"); #endif - + } -static msg_t *build_suspend (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_suspend (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; SUSPEND_t *suspend; - msg_t *msg =(msg_t*)create_l3msg(CC_SUSPEND | REQUEST, MT_SUSPEND, bc?bc->l3_id:-1, sizeof(SUSPEND_t) ,nt); - - suspend=(SUSPEND_t*)((msg->data+HEADER_LEN)); + msg_t *msg =(msg_t*)create_l3msg(CC_SUSPEND | REQUEST, MT_SUSPEND, bc?bc->l3_id:-1, sizeof(SUSPEND_t) ,nt); -#ifdef DEBUG - printf("Building SUSPEND Msg\n"); + suspend=(SUSPEND_t*)((msg->data+HEADER_LEN)); + +#ifdef DEBUG + printf("Building SUSPEND Msg\n"); #endif - return msg; + return msg; } -static void parse_resume (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_resume (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { -#ifdef DEBUG - printf("Parsing RESUME Msg\n"); +#ifdef DEBUG + printf("Parsing RESUME Msg\n"); #endif - + } -static msg_t *build_resume (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_resume (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; RESUME_t *resume; - msg_t *msg =(msg_t*)create_l3msg(CC_RESUME | REQUEST, MT_RESUME, bc?bc->l3_id:-1, sizeof(RESUME_t) ,nt); - - resume=(RESUME_t*)((msg->data+HEADER_LEN)); + msg_t *msg =(msg_t*)create_l3msg(CC_RESUME | REQUEST, MT_RESUME, bc?bc->l3_id:-1, sizeof(RESUME_t) ,nt); -#ifdef DEBUG - printf("Building RESUME Msg\n"); + resume=(RESUME_t*)((msg->data+HEADER_LEN)); + +#ifdef DEBUG + printf("Building RESUME Msg\n"); #endif - return msg; + return msg; } -static void parse_hold_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_hold_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { -#ifdef DEBUG - printf("Parsing HOLD_ACKNOWLEDGE Msg\n"); +#ifdef DEBUG + printf("Parsing HOLD_ACKNOWLEDGE Msg\n"); #endif - + } -static msg_t *build_hold_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_hold_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; HOLD_ACKNOWLEDGE_t *hold_acknowledge; - msg_t *msg =(msg_t*)create_l3msg(CC_HOLD_ACKNOWLEDGE | REQUEST, MT_HOLD_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(HOLD_ACKNOWLEDGE_t) ,nt); - - hold_acknowledge=(HOLD_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); + msg_t *msg =(msg_t*)create_l3msg(CC_HOLD_ACKNOWLEDGE | REQUEST, MT_HOLD_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(HOLD_ACKNOWLEDGE_t) ,nt); -#ifdef DEBUG - printf("Building HOLD_ACKNOWLEDGE Msg\n"); + hold_acknowledge=(HOLD_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); + +#ifdef DEBUG + printf("Building HOLD_ACKNOWLEDGE Msg\n"); #endif - return msg; + return msg; } -static void parse_suspend_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_suspend_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { -#ifdef DEBUG - printf("Parsing SUSPEND_ACKNOWLEDGE Msg\n"); +#ifdef DEBUG + printf("Parsing SUSPEND_ACKNOWLEDGE Msg\n"); #endif - + } -static msg_t *build_suspend_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_suspend_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; SUSPEND_ACKNOWLEDGE_t *suspend_acknowledge; - msg_t *msg =(msg_t*)create_l3msg(CC_SUSPEND_ACKNOWLEDGE | REQUEST, MT_SUSPEND_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(SUSPEND_ACKNOWLEDGE_t) ,nt); - - suspend_acknowledge=(SUSPEND_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); + msg_t *msg =(msg_t*)create_l3msg(CC_SUSPEND_ACKNOWLEDGE | REQUEST, MT_SUSPEND_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(SUSPEND_ACKNOWLEDGE_t) ,nt); -#ifdef DEBUG - printf("Building SUSPEND_ACKNOWLEDGE Msg\n"); + suspend_acknowledge=(SUSPEND_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); + +#ifdef DEBUG + printf("Building SUSPEND_ACKNOWLEDGE Msg\n"); #endif - return msg; + return msg; } -static void parse_resume_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_resume_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { -#ifdef DEBUG - printf("Parsing RESUME_ACKNOWLEDGE Msg\n"); +#ifdef DEBUG + printf("Parsing RESUME_ACKNOWLEDGE Msg\n"); #endif - + } -static msg_t *build_resume_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_resume_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; RESUME_ACKNOWLEDGE_t *resume_acknowledge; - msg_t *msg =(msg_t*)create_l3msg(CC_RESUME_ACKNOWLEDGE | REQUEST, MT_RESUME_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(RESUME_ACKNOWLEDGE_t) ,nt); - - resume_acknowledge=(RESUME_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); + msg_t *msg =(msg_t*)create_l3msg(CC_RESUME_ACKNOWLEDGE | REQUEST, MT_RESUME_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(RESUME_ACKNOWLEDGE_t) ,nt); -#ifdef DEBUG - printf("Building RESUME_ACKNOWLEDGE Msg\n"); + resume_acknowledge=(RESUME_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); + +#ifdef DEBUG + printf("Building RESUME_ACKNOWLEDGE Msg\n"); #endif - return msg; + return msg; } -static void parse_hold_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_hold_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { -#ifdef DEBUG - printf("Parsing HOLD_REJECT Msg\n"); +#ifdef DEBUG + printf("Parsing HOLD_REJECT Msg\n"); #endif - + } -static msg_t *build_hold_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_hold_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; HOLD_REJECT_t *hold_reject; - msg_t *msg =(msg_t*)create_l3msg(CC_HOLD_REJECT | REQUEST, MT_HOLD_REJECT, bc?bc->l3_id:-1, sizeof(HOLD_REJECT_t) ,nt); - - hold_reject=(HOLD_REJECT_t*)((msg->data+HEADER_LEN)); + msg_t *msg =(msg_t*)create_l3msg(CC_HOLD_REJECT | REQUEST, MT_HOLD_REJECT, bc?bc->l3_id:-1, sizeof(HOLD_REJECT_t) ,nt); -#ifdef DEBUG - printf("Building HOLD_REJECT Msg\n"); + hold_reject=(HOLD_REJECT_t*)((msg->data+HEADER_LEN)); + +#ifdef DEBUG + printf("Building HOLD_REJECT Msg\n"); #endif - return msg; + return msg; } -static void parse_retrieve (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_retrieve (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { -#ifdef DEBUG - printf("Parsing RETRIEVE Msg\n"); +#ifdef DEBUG + printf("Parsing RETRIEVE Msg\n"); #endif - + } -static msg_t *build_retrieve (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_retrieve (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; RETRIEVE_t *retrieve; - msg_t *msg =(msg_t*)create_l3msg(CC_RETRIEVE | REQUEST, MT_RETRIEVE, bc?bc->l3_id:-1, sizeof(RETRIEVE_t) ,nt); - - retrieve=(RETRIEVE_t*)((msg->data+HEADER_LEN)); + msg_t *msg =(msg_t*)create_l3msg(CC_RETRIEVE | REQUEST, MT_RETRIEVE, bc?bc->l3_id:-1, sizeof(RETRIEVE_t) ,nt); -#ifdef DEBUG - printf("Building RETRIEVE Msg\n"); + retrieve=(RETRIEVE_t*)((msg->data+HEADER_LEN)); + +#ifdef DEBUG + printf("Building RETRIEVE Msg\n"); #endif - return msg; + return msg; } -static void parse_retrieve_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_retrieve_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { -#ifdef DEBUG - printf("Parsing RETRIEVE_ACKNOWLEDGE Msg\n"); +#ifdef DEBUG + printf("Parsing RETRIEVE_ACKNOWLEDGE Msg\n"); #endif - + } -static msg_t *build_retrieve_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_retrieve_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; RETRIEVE_ACKNOWLEDGE_t *retrieve_acknowledge; - msg_t *msg =(msg_t*)create_l3msg(CC_RETRIEVE_ACKNOWLEDGE | REQUEST, MT_RETRIEVE_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(RETRIEVE_ACKNOWLEDGE_t) ,nt); - - retrieve_acknowledge=(RETRIEVE_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); + msg_t *msg =(msg_t*)create_l3msg(CC_RETRIEVE_ACKNOWLEDGE | REQUEST, MT_RETRIEVE_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(RETRIEVE_ACKNOWLEDGE_t) ,nt); + retrieve_acknowledge=(RETRIEVE_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); + enc_ie_channel_id(&retrieve_acknowledge->CHANNEL_ID, msg, 1, bc->channel, nt,bc); -#ifdef DEBUG - printf("Building RETRIEVE_ACKNOWLEDGE Msg\n"); +#ifdef DEBUG + printf("Building RETRIEVE_ACKNOWLEDGE Msg\n"); #endif - return msg; + return msg; } -static void parse_retrieve_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_retrieve_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { -#ifdef DEBUG - printf("Parsing RETRIEVE_REJECT Msg\n"); +#ifdef DEBUG + printf("Parsing RETRIEVE_REJECT Msg\n"); #endif - + } -static msg_t *build_retrieve_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_retrieve_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; RETRIEVE_REJECT_t *retrieve_reject; - msg_t *msg =(msg_t*)create_l3msg(CC_RETRIEVE_REJECT | REQUEST, MT_RETRIEVE_REJECT, bc?bc->l3_id:-1, sizeof(RETRIEVE_REJECT_t) ,nt); - - retrieve_reject=(RETRIEVE_REJECT_t*)((msg->data+HEADER_LEN)); + msg_t *msg =(msg_t*)create_l3msg(CC_RETRIEVE_REJECT | REQUEST, MT_RETRIEVE_REJECT, bc?bc->l3_id:-1, sizeof(RETRIEVE_REJECT_t) ,nt); -#ifdef DEBUG - printf("Building RETRIEVE_REJECT Msg\n"); + retrieve_reject=(RETRIEVE_REJECT_t*)((msg->data+HEADER_LEN)); + +#ifdef DEBUG + printf("Building RETRIEVE_REJECT Msg\n"); #endif - return msg; + return msg; } -static void parse_disconnect (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_disconnect (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - DISCONNECT_t *disconnect=(DISCONNECT_t*)((unsigned long)(msg->data+HEADER_LEN)); + DISCONNECT_t *disconnect = (DISCONNECT_t *) (msg->data + HEADER_LEN); int location; - int cause; + int cause; dec_ie_cause(disconnect->CAUSE, (Q931_info_t *)(disconnect), &location, &cause, nt,bc); if (cause>0) bc->cause=cause; + dec_ie_facility(disconnect->FACILITY, (Q931_info_t *) disconnect, &bc->fac_in, nt, bc); + dec_ie_progress(disconnect->PROGRESS, (Q931_info_t *)disconnect, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); -#ifdef DEBUG - printf("Parsing DISCONNECT Msg\n"); +#ifdef DEBUG + printf("Parsing DISCONNECT Msg\n"); #endif - + } -static msg_t *build_disconnect (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_disconnect (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; DISCONNECT_t *disconnect; - msg_t *msg =(msg_t*)create_l3msg(CC_DISCONNECT | REQUEST, MT_DISCONNECT, bc?bc->l3_id:-1, sizeof(DISCONNECT_t) ,nt); - - disconnect=(DISCONNECT_t*)((msg->data+HEADER_LEN)); - + msg_t *msg =(msg_t*)create_l3msg(CC_DISCONNECT | REQUEST, MT_DISCONNECT, bc?bc->l3_id:-1, sizeof(DISCONNECT_t) ,nt); + + disconnect=(DISCONNECT_t*)((msg->data+HEADER_LEN)); + enc_ie_cause(&disconnect->CAUSE, msg, (nt)?1:0, bc->out_cause,nt,bc); - if (nt) enc_ie_progress(&disconnect->PROGRESS, msg, 0, nt?1:5, 8 ,nt,bc); + if (nt) { + enc_ie_progress(&disconnect->PROGRESS, msg, 0, nt ? 1 : 5, 8, nt, bc); + } + if (bc->fac_out.Function != Fac_None) { + enc_ie_facility(&disconnect->FACILITY, msg, &bc->fac_out, nt); + } + if (bc->uulen) { int protocol=4; enc_ie_useruser(&disconnect->USER_USER, msg, protocol, bc->uu, bc->uulen, nt,bc); cb_log(1,bc->port,"ENCODING USERUESRINFO:%s\n",bc->uu); } - -#ifdef DEBUG - printf("Building DISCONNECT Msg\n"); + +#ifdef DEBUG + printf("Building DISCONNECT Msg\n"); #endif - return msg; + return msg; } -static void parse_restart (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_restart (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - RESTART_t *restart=(RESTART_t*)((unsigned long)(msg->data+HEADER_LEN)); + RESTART_t *restart = (RESTART_t *) (msg->data + HEADER_LEN); struct misdn_stack *stack=get_stack_by_bc(bc); - -#ifdef DEBUG + +#ifdef DEBUG printf("Parsing RESTART Msg\n"); #endif - + { int exclusive; dec_ie_channel_id(restart->CHANNEL_ID, (Q931_info_t *)restart, &exclusive, &bc->restart_channel, nt,bc); cb_log(3, stack->port, "CC_RESTART Request on channel:%d on this port.\n", bc->restart_channel); } - + } -static msg_t *build_restart (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_restart (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; RESTART_t *restart; - msg_t *msg =(msg_t*)create_l3msg(CC_RESTART | REQUEST, MT_RESTART, bc?bc->l3_id:-1, sizeof(RESTART_t) ,nt); - - restart=(RESTART_t*)((msg->data+HEADER_LEN)); + msg_t *msg =(msg_t*)create_l3msg(CC_RESTART | REQUEST, MT_RESTART, bc?bc->l3_id:-1, sizeof(RESTART_t) ,nt); -#ifdef DEBUG - printf("Building RESTART Msg\n"); + restart=(RESTART_t*)((msg->data+HEADER_LEN)); + +#ifdef DEBUG + printf("Building RESTART Msg\n"); #endif if (bc->channel > 0) { @@ -855,52 +1165,59 @@ } cb_log(0,bc->port, "Restarting channel %d\n", bc->channel); - return msg; + return msg; } -static void parse_release (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_release (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - RELEASE_t *release=(RELEASE_t*)((unsigned long)(msg->data+HEADER_LEN)); + RELEASE_t *release = (RELEASE_t *) (msg->data + HEADER_LEN); int location; int cause; - + dec_ie_cause(release->CAUSE, (Q931_info_t *)(release), &location, &cause, nt,bc); if (cause>0) bc->cause=cause; -#ifdef DEBUG - printf("Parsing RELEASE Msg\n"); + + dec_ie_facility(release->FACILITY, (Q931_info_t *) release, &bc->fac_in, nt, bc); + +#ifdef DEBUG + printf("Parsing RELEASE Msg\n"); #endif - + } -static msg_t *build_release (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_release (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; RELEASE_t *release; - msg_t *msg =(msg_t*)create_l3msg(CC_RELEASE | REQUEST, MT_RELEASE, bc?bc->l3_id:-1, sizeof(RELEASE_t) ,nt); - - release=(RELEASE_t*)((msg->data+HEADER_LEN)); - + msg_t *msg =(msg_t*)create_l3msg(CC_RELEASE | REQUEST, MT_RELEASE, bc?bc->l3_id:-1, sizeof(RELEASE_t) ,nt); + + release=(RELEASE_t*)((msg->data+HEADER_LEN)); + if (bc->out_cause>= 0) enc_ie_cause(&release->CAUSE, msg, nt?1:0, bc->out_cause, nt,bc); + if (bc->fac_out.Function != Fac_None) { + enc_ie_facility(&release->FACILITY, msg, &bc->fac_out, nt); + } + if (bc->uulen) { int protocol=4; enc_ie_useruser(&release->USER_USER, msg, protocol, bc->uu, bc->uulen, nt,bc); cb_log(1,bc->port,"ENCODING USERUESRINFO:%s\n",bc->uu); } - -#ifdef DEBUG - printf("Building RELEASE Msg\n"); + +#ifdef DEBUG + printf("Building RELEASE Msg\n"); #endif - return msg; + return msg; } -static void parse_release_complete (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_release_complete (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - RELEASE_COMPLETE_t *release_complete=(RELEASE_COMPLETE_t*)((unsigned long)(msg->data+HEADER_LEN)); + RELEASE_COMPLETE_t *release_complete = (RELEASE_COMPLETE_t *) (msg->data + HEADER_LEN); int location; int cause; iframe_t *frm = (iframe_t*) msg->data; @@ -926,45 +1243,52 @@ dec_ie_cause(release_complete->CAUSE, (Q931_info_t *)(release_complete), &location, &cause, nt,bc); if (cause>0) bc->cause=cause; -#ifdef DEBUG - printf("Parsing RELEASE_COMPLETE Msg\n"); + dec_ie_facility(release_complete->FACILITY, (Q931_info_t *) release_complete, &bc->fac_in, nt, bc); + +#ifdef DEBUG + printf("Parsing RELEASE_COMPLETE Msg\n"); #endif } -static msg_t *build_release_complete (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_release_complete (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; RELEASE_COMPLETE_t *release_complete; - msg_t *msg =(msg_t*)create_l3msg(CC_RELEASE_COMPLETE | REQUEST, MT_RELEASE_COMPLETE, bc?bc->l3_id:-1, sizeof(RELEASE_COMPLETE_t) ,nt); - - release_complete=(RELEASE_COMPLETE_t*)((msg->data+HEADER_LEN)); - + msg_t *msg =(msg_t*)create_l3msg(CC_RELEASE_COMPLETE | REQUEST, MT_RELEASE_COMPLETE, bc?bc->l3_id:-1, sizeof(RELEASE_COMPLETE_t) ,nt); + + release_complete=(RELEASE_COMPLETE_t*)((msg->data+HEADER_LEN)); + enc_ie_cause(&release_complete->CAUSE, msg, nt?1:0, bc->out_cause, nt,bc); + if (bc->fac_out.Function != Fac_None) { + enc_ie_facility(&release_complete->FACILITY, msg, &bc->fac_out, nt); + } + if (bc->uulen) { int protocol=4; enc_ie_useruser(&release_complete->USER_USER, msg, protocol, bc->uu, bc->uulen, nt,bc); cb_log(1,bc->port,"ENCODING USERUESRINFO:%s\n",bc->uu); } - -#ifdef DEBUG - printf("Building RELEASE_COMPLETE Msg\n"); + +#ifdef DEBUG + printf("Building RELEASE_COMPLETE Msg\n"); #endif - return msg; + return msg; } -static void parse_facility (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_facility (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt ? mISDNUSER_HEAD_SIZE : mISDN_HEADER_LEN; - FACILITY_t *facility = (FACILITY_t*)(msg->data+HEADER_LEN); - Q931_info_t *qi = (Q931_info_t*)(msg->data+HEADER_LEN); + FACILITY_t *facility = (FACILITY_t*)(msg->data+HEADER_LEN); + Q931_info_t *qi = (Q931_info_t*)(msg->data+HEADER_LEN); unsigned char *p = NULL; - int err; -#ifdef DEBUG - printf("Parsing FACILITY Msg\n"); +#ifdef DEBUG + printf("Parsing FACILITY Msg\n"); #endif + bc->fac_in.Function = Fac_None; + if (!bc->nt) { if (qi->QI_ELEMENT(facility)) p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(facility) + 1; @@ -973,31 +1297,40 @@ } if (!p) return; - - err = decodeFac(p, &(bc->fac_in)); - if (err) { - cb_log(5, bc->port, "Decoding FACILITY failed! (%d)\n", err); + + if (decodeFac(p, &bc->fac_in)) { + cb_log(3, bc->port, "Decoding facility ie failed! Unrecognized facility message?\n"); } } -static msg_t *build_facility (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_facility (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { - int len, - HEADER_LEN = nt ? mISDNUSER_HEAD_SIZE : mISDN_HEADER_LEN; - unsigned char *ie_fac, - fac_tmp[256]; - msg_t *msg =(msg_t*)create_l3msg(CC_FACILITY | REQUEST, MT_FACILITY, bc?bc->l3_id:-1, sizeof(FACILITY_t) ,nt); - FACILITY_t *facility = (FACILITY_t*)(msg->data+HEADER_LEN); + int len; + int HEADER_LEN; + unsigned char *ie_fac; + unsigned char fac_tmp[256]; + msg_t *msg; + FACILITY_t *facility; Q931_info_t *qi; -#ifdef DEBUG - printf("Building FACILITY Msg\n"); +#ifdef DEBUG + printf("Building FACILITY Msg\n"); #endif - + len = encodeFac(fac_tmp, &(bc->fac_out)); - if (len <= 0) + if (len <= 0) { + /* + * mISDN does not know how to build the requested facility structure + * Clear facility information + */ + bc->fac_out.Function = Fac_None; return NULL; + } + msg = (msg_t *) create_l3msg(CC_FACILITY | REQUEST, MT_FACILITY, bc ? bc->l3_id : -1, sizeof(FACILITY_t), nt); + HEADER_LEN = nt ? mISDNUSER_HEAD_SIZE : mISDN_HEADER_LEN; + facility = (FACILITY_t *) (msg->data + HEADER_LEN); + ie_fac = msg_put(msg, len); if (bc->nt) { facility->FACILITY = ie_fac + 1; @@ -1008,148 +1341,259 @@ memcpy(ie_fac, fac_tmp, len); + /* Clear facility information */ + bc->fac_out.Function = Fac_None; + if (*bc->display) { +#ifdef DEBUG printf("Sending %s as Display\n", bc->display); +#endif enc_ie_display(&facility->DISPLAY, msg, bc->display, nt,bc); } - return msg; + return msg; } -static void parse_notify (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Parse a received REGISTER message + * + * \param msgs Search table entry that called us. + * \param msg Received message contents + * \param bc Associated B channel + * \param nt TRUE if in NT mode. + * + * \return Nothing + */ +static void parse_register(struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { -#ifdef DEBUG - printf("Parsing NOTIFY Msg\n"); + int HEADER_LEN; + REGISTER_t *reg; + + HEADER_LEN = nt ? mISDNUSER_HEAD_SIZE : mISDN_HEADER_LEN; + reg = (REGISTER_t *) (msg->data + HEADER_LEN); + + /* + * A facility ie is optional. + * The peer may just be establishing a connection to send + * messages later. + */ + dec_ie_facility(reg->FACILITY, (Q931_info_t *) reg, &bc->fac_in, nt, bc); +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \internal + * \brief Construct a REGISTER message + * + * \param msgs Search table entry that called us. + * \param bc Associated B channel + * \param nt TRUE if in NT mode. + * + * \return Allocated built message + */ +static msg_t *build_register(struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +{ + int HEADER_LEN; + REGISTER_t *reg; + msg_t *msg; + + msg = (msg_t *) create_l3msg(CC_REGISTER | REQUEST, MT_REGISTER, bc ? bc->l3_id : -1, sizeof(REGISTER_t), nt); + HEADER_LEN = nt ? mISDNUSER_HEAD_SIZE : mISDN_HEADER_LEN; + reg = (REGISTER_t *) (msg->data + HEADER_LEN); + + if (bc->fac_out.Function != Fac_None) { + enc_ie_facility(®->FACILITY, msg, &bc->fac_out, nt); + } + + return msg; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +static void parse_notify (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +{ + int HEADER_LEN = nt ? mISDNUSER_HEAD_SIZE : mISDN_HEADER_LEN; + NOTIFY_t *notify = (NOTIFY_t *) (msg->data + HEADER_LEN); + int description_code; + int type; + int plan; + int present; + char number[sizeof(bc->redirecting.to.number)]; + +#ifdef DEBUG + printf("Parsing NOTIFY Msg\n"); #endif + + dec_ie_notify(notify->NOTIFY, (Q931_info_t *) notify, &description_code, nt, bc); + if (description_code < 0) { + bc->notify_description_code = mISDN_NOTIFY_CODE_INVALID; + } else { + bc->notify_description_code = description_code; + } + + dec_ie_redir_dn(notify->REDIR_DN, (Q931_info_t *) notify, &type, &plan, &present, number, sizeof(number), nt, bc); + if (0 <= type) { + bc->redirecting.to_changed = 1; + + bc->redirecting.to.number_type = type; + bc->redirecting.to.number_plan = plan; + switch (present) { + default: + case 0: + bc->redirecting.to.presentation = 0; /* presentation allowed */ + break; + case 1: + bc->redirecting.to.presentation = 1; /* presentation restricted */ + break; + case 2: + bc->redirecting.to.presentation = 2; /* Number not available */ + break; + } + bc->redirecting.to.screening = 0; /* Unscreened */ + strcpy(bc->redirecting.to.number, number); + } } -static msg_t *build_notify (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_notify (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; NOTIFY_t *notify; - msg_t *msg =(msg_t*)create_l3msg(CC_NOTIFY | REQUEST, MT_NOTIFY, bc?bc->l3_id:-1, sizeof(NOTIFY_t) ,nt); - - notify=(NOTIFY_t*)((msg->data+HEADER_LEN)); + msg_t *msg =(msg_t*)create_l3msg(CC_NOTIFY | REQUEST, MT_NOTIFY, bc?bc->l3_id:-1, sizeof(NOTIFY_t) ,nt); -#ifdef DEBUG - printf("Building NOTIFY Msg\n"); +#ifdef DEBUG + printf("Building NOTIFY Msg\n"); #endif - return msg; + + notify = (NOTIFY_t *) (msg->data + HEADER_LEN); + + enc_ie_notify(¬ify->NOTIFY, msg, bc->notify_description_code, nt, bc); + + if (bc->redirecting.to_changed) { + bc->redirecting.to_changed = 0; + switch (bc->outgoing_colp) { + case 0:/* pass */ + case 1:/* restricted */ + enc_ie_redir_dn(¬ify->REDIR_DN, msg, bc->redirecting.to.number_type, + bc->redirecting.to.number_plan, bc->redirecting.to.presentation, + bc->redirecting.to.number, nt, bc); + break; + default: + break; + } + } + return msg; } -static void parse_status_enquiry (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_status_enquiry (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { -#ifdef DEBUG - printf("Parsing STATUS_ENQUIRY Msg\n"); +#ifdef DEBUG + printf("Parsing STATUS_ENQUIRY Msg\n"); #endif } -static msg_t *build_status_enquiry (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_status_enquiry (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; STATUS_ENQUIRY_t *status_enquiry; - msg_t *msg =(msg_t*)create_l3msg(CC_STATUS_ENQUIRY | REQUEST, MT_STATUS_ENQUIRY, bc?bc->l3_id:-1, sizeof(STATUS_ENQUIRY_t) ,nt); - - status_enquiry=(STATUS_ENQUIRY_t*)((msg->data+HEADER_LEN)); + msg_t *msg =(msg_t*)create_l3msg(CC_STATUS_ENQUIRY | REQUEST, MT_STATUS_ENQUIRY, bc?bc->l3_id:-1, sizeof(STATUS_ENQUIRY_t) ,nt); -#ifdef DEBUG - printf("Building STATUS_ENQUIRY Msg\n"); + status_enquiry=(STATUS_ENQUIRY_t*)((msg->data+HEADER_LEN)); + +#ifdef DEBUG + printf("Building STATUS_ENQUIRY Msg\n"); #endif - return msg; + return msg; } -static void parse_information (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_information (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - INFORMATION_t *information=(INFORMATION_t*)((unsigned long)(msg->data+HEADER_LEN)); - { - int type, plan; - char number[32]; - char keypad[32]; - dec_ie_called_pn(information->CALLED_PN, (Q931_info_t *)information, &type, &plan, number, sizeof(number)-1, nt, bc); - dec_ie_keypad(information->KEYPAD, (Q931_info_t *)information, keypad, sizeof(keypad)-1, nt, bc); - strcpy(bc->info_dad, number); - strcpy(bc->keypad,keypad); - } -#ifdef DEBUG - printf("Parsing INFORMATION Msg\n"); + INFORMATION_t *information = (INFORMATION_t *) (msg->data + HEADER_LEN); + int type, plan; + + dec_ie_called_pn(information->CALLED_PN, (Q931_info_t *) information, &type, &plan, bc->info_dad, sizeof(bc->info_dad), nt, bc); + dec_ie_keypad(information->KEYPAD, (Q931_info_t *) information, bc->keypad, sizeof(bc->keypad), nt, bc); + +#ifdef DEBUG + printf("Parsing INFORMATION Msg\n"); #endif } -static msg_t *build_information (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_information (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; INFORMATION_t *information; - msg_t *msg =(msg_t*)create_l3msg(CC_INFORMATION | REQUEST, MT_INFORMATION, bc?bc->l3_id:-1, sizeof(INFORMATION_t) ,nt); - - information=(INFORMATION_t*)((msg->data+HEADER_LEN)); - - { - enc_ie_called_pn(&information->CALLED_PN, msg, 0, 1, bc->info_dad, nt,bc); - } + msg_t *msg =(msg_t*)create_l3msg(CC_INFORMATION | REQUEST, MT_INFORMATION, bc?bc->l3_id:-1, sizeof(INFORMATION_t) ,nt); + information=(INFORMATION_t*)((msg->data+HEADER_LEN)); + + enc_ie_called_pn(&information->CALLED_PN, msg, 0, 1, bc->info_dad, nt,bc); + { if (*bc->display) { +#ifdef DEBUG printf("Sending %s as Display\n", bc->display); +#endif enc_ie_display(&information->DISPLAY, msg, bc->display, nt,bc); } } - -#ifdef DEBUG - printf("Building INFORMATION Msg\n"); + +#ifdef DEBUG + printf("Building INFORMATION Msg\n"); #endif - return msg; + return msg; } -static void parse_status (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_status (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - STATUS_t *status=(STATUS_t*)((unsigned long)(msg->data+HEADER_LEN)); + STATUS_t *status = (STATUS_t *) (msg->data + HEADER_LEN); int location; int cause; - + dec_ie_cause(status->CAUSE, (Q931_info_t *)(status), &location, &cause, nt,bc); if (cause>0) bc->cause=cause; - ; -#ifdef DEBUG - printf("Parsing STATUS Msg\n"); +#ifdef DEBUG + printf("Parsing STATUS Msg\n"); #endif } -static msg_t *build_status (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_status (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; STATUS_t *status; - msg_t *msg =(msg_t*)create_l3msg(CC_STATUS | REQUEST, MT_STATUS, bc?bc->l3_id:-1, sizeof(STATUS_t) ,nt); - - status=(STATUS_t*)((msg->data+HEADER_LEN)); + msg_t *msg =(msg_t*)create_l3msg(CC_STATUS | REQUEST, MT_STATUS, bc?bc->l3_id:-1, sizeof(STATUS_t) ,nt); -#ifdef DEBUG - printf("Building STATUS Msg\n"); + status=(STATUS_t*)((msg->data+HEADER_LEN)); + +#ifdef DEBUG + printf("Building STATUS Msg\n"); #endif - return msg; + return msg; } -static void parse_timeout (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) +static void parse_timeout (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) { -#ifdef DEBUG - printf("Parsing STATUS Msg\n"); -#endif +#ifdef DEBUG + printf("Parsing STATUS Msg\n"); +#endif } -static msg_t *build_timeout (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) +static msg_t *build_timeout (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) { int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; STATUS_t *status; - msg_t *msg =(msg_t*)create_l3msg(CC_STATUS | REQUEST, MT_STATUS, bc?bc->l3_id:-1, sizeof(STATUS_t) ,nt); - - status=(STATUS_t*)((msg->data+HEADER_LEN)); + msg_t *msg =(msg_t*)create_l3msg(CC_STATUS | REQUEST, MT_STATUS, bc?bc->l3_id:-1, sizeof(STATUS_t) ,nt); -#ifdef DEBUG - printf("Building STATUS Msg\n"); + status=(STATUS_t*)((msg->data+HEADER_LEN)); + +#ifdef DEBUG + printf("Building STATUS Msg\n"); #endif - return msg; + return msg; } @@ -1161,97 +1605,43 @@ /** Msg Array **/ struct isdn_msg msgs_g[] = { - {CC_PROCEEDING,L3,EVENT_PROCEEDING, - parse_proceeding,build_proceeding, - "PROCEEDING"}, - {CC_ALERTING,L3,EVENT_ALERTING, - parse_alerting,build_alerting, - "ALERTING"}, - {CC_PROGRESS,L3,EVENT_PROGRESS, - parse_progress,build_progress, - "PROGRESS"}, - {CC_SETUP,L3,EVENT_SETUP, - parse_setup,build_setup, - "SETUP"}, - {CC_CONNECT,L3,EVENT_CONNECT, - parse_connect,build_connect, - "CONNECT"}, - {CC_SETUP_ACKNOWLEDGE,L3,EVENT_SETUP_ACKNOWLEDGE, - parse_setup_acknowledge,build_setup_acknowledge, - "SETUP_ACKNOWLEDGE"}, - {CC_CONNECT_ACKNOWLEDGE ,L3,EVENT_CONNECT_ACKNOWLEDGE , - parse_connect_acknowledge ,build_connect_acknowledge, - "CONNECT_ACKNOWLEDGE "}, - {CC_USER_INFORMATION,L3,EVENT_USER_INFORMATION, - parse_user_information,build_user_information, - "USER_INFORMATION"}, - {CC_SUSPEND_REJECT,L3,EVENT_SUSPEND_REJECT, - parse_suspend_reject,build_suspend_reject, - "SUSPEND_REJECT"}, - {CC_RESUME_REJECT,L3,EVENT_RESUME_REJECT, - parse_resume_reject,build_resume_reject, - "RESUME_REJECT"}, - {CC_HOLD,L3,EVENT_HOLD, - parse_hold,build_hold, - "HOLD"}, - {CC_SUSPEND,L3,EVENT_SUSPEND, - parse_suspend,build_suspend, - "SUSPEND"}, - {CC_RESUME,L3,EVENT_RESUME, - parse_resume,build_resume, - "RESUME"}, - {CC_HOLD_ACKNOWLEDGE,L3,EVENT_HOLD_ACKNOWLEDGE, - parse_hold_acknowledge,build_hold_acknowledge, - "HOLD_ACKNOWLEDGE"}, - {CC_SUSPEND_ACKNOWLEDGE,L3,EVENT_SUSPEND_ACKNOWLEDGE, - parse_suspend_acknowledge,build_suspend_acknowledge, - "SUSPEND_ACKNOWLEDGE"}, - {CC_RESUME_ACKNOWLEDGE,L3,EVENT_RESUME_ACKNOWLEDGE, - parse_resume_acknowledge,build_resume_acknowledge, - "RESUME_ACKNOWLEDGE"}, - {CC_HOLD_REJECT,L3,EVENT_HOLD_REJECT, - parse_hold_reject,build_hold_reject, - "HOLD_REJECT"}, - {CC_RETRIEVE,L3,EVENT_RETRIEVE, - parse_retrieve,build_retrieve, - "RETRIEVE"}, - {CC_RETRIEVE_ACKNOWLEDGE,L3,EVENT_RETRIEVE_ACKNOWLEDGE, - parse_retrieve_acknowledge,build_retrieve_acknowledge, - "RETRIEVE_ACKNOWLEDGE"}, - {CC_RETRIEVE_REJECT,L3,EVENT_RETRIEVE_REJECT, - parse_retrieve_reject,build_retrieve_reject, - "RETRIEVE_REJECT"}, - {CC_DISCONNECT,L3,EVENT_DISCONNECT, - parse_disconnect,build_disconnect, - "DISCONNECT"}, - {CC_RESTART,L3,EVENT_RESTART, - parse_restart,build_restart, - "RESTART"}, - {CC_RELEASE,L3,EVENT_RELEASE, - parse_release,build_release, - "RELEASE"}, - {CC_RELEASE_COMPLETE,L3,EVENT_RELEASE_COMPLETE, - parse_release_complete,build_release_complete, - "RELEASE_COMPLETE"}, - {CC_FACILITY,L3,EVENT_FACILITY, - parse_facility,build_facility, - "FACILITY"}, - {CC_NOTIFY,L3,EVENT_NOTIFY, - parse_notify,build_notify, - "NOTIFY"}, - {CC_STATUS_ENQUIRY,L3,EVENT_STATUS_ENQUIRY, - parse_status_enquiry,build_status_enquiry, - "STATUS_ENQUIRY"}, - {CC_INFORMATION,L3,EVENT_INFORMATION, - parse_information,build_information, - "INFORMATION"}, - {CC_STATUS,L3,EVENT_STATUS, - parse_status,build_status, - "STATUS"}, - {CC_TIMEOUT,L3,EVENT_TIMEOUT, - parse_timeout,build_timeout, - "TIMEOUT"}, - {0,0,0,NULL,NULL,NULL} +/* *INDENT-OFF* */ + /* misdn_msg, event, msg_parser, msg_builder, info */ + { CC_PROCEEDING, EVENT_PROCEEDING, parse_proceeding, build_proceeding, "PROCEEDING" }, + { CC_ALERTING, EVENT_ALERTING, parse_alerting, build_alerting, "ALERTING" }, + { CC_PROGRESS, EVENT_PROGRESS, parse_progress, build_progress, "PROGRESS" }, + { CC_SETUP, EVENT_SETUP, parse_setup, build_setup, "SETUP" }, +#if defined(AST_MISDN_ENHANCEMENTS) + { CC_REGISTER, EVENT_REGISTER, parse_register, build_register, "REGISTER" }, +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + { CC_CONNECT, EVENT_CONNECT, parse_connect, build_connect, "CONNECT" }, + { CC_SETUP_ACKNOWLEDGE, EVENT_SETUP_ACKNOWLEDGE, parse_setup_acknowledge, build_setup_acknowledge, "SETUP_ACKNOWLEDGE" }, + { CC_CONNECT_ACKNOWLEDGE, EVENT_CONNECT_ACKNOWLEDGE, parse_connect_acknowledge, build_connect_acknowledge, "CONNECT_ACKNOWLEDGE " }, + { CC_USER_INFORMATION, EVENT_USER_INFORMATION, parse_user_information, build_user_information, "USER_INFORMATION" }, + { CC_SUSPEND_REJECT, EVENT_SUSPEND_REJECT, parse_suspend_reject, build_suspend_reject, "SUSPEND_REJECT" }, + { CC_RESUME_REJECT, EVENT_RESUME_REJECT, parse_resume_reject, build_resume_reject, "RESUME_REJECT" }, + { CC_HOLD, EVENT_HOLD, parse_hold, build_hold, "HOLD" }, + { CC_SUSPEND, EVENT_SUSPEND, parse_suspend, build_suspend, "SUSPEND" }, + { CC_RESUME, EVENT_RESUME, parse_resume, build_resume, "RESUME" }, + { CC_HOLD_ACKNOWLEDGE, EVENT_HOLD_ACKNOWLEDGE, parse_hold_acknowledge, build_hold_acknowledge, "HOLD_ACKNOWLEDGE" }, + { CC_SUSPEND_ACKNOWLEDGE, EVENT_SUSPEND_ACKNOWLEDGE, parse_suspend_acknowledge, build_suspend_acknowledge, "SUSPEND_ACKNOWLEDGE" }, + { CC_RESUME_ACKNOWLEDGE, EVENT_RESUME_ACKNOWLEDGE, parse_resume_acknowledge, build_resume_acknowledge, "RESUME_ACKNOWLEDGE" }, + { CC_HOLD_REJECT, EVENT_HOLD_REJECT, parse_hold_reject, build_hold_reject, "HOLD_REJECT" }, + { CC_RETRIEVE, EVENT_RETRIEVE, parse_retrieve, build_retrieve, "RETRIEVE" }, + { CC_RETRIEVE_ACKNOWLEDGE, EVENT_RETRIEVE_ACKNOWLEDGE, parse_retrieve_acknowledge, build_retrieve_acknowledge, "RETRIEVE_ACKNOWLEDGE" }, + { CC_RETRIEVE_REJECT, EVENT_RETRIEVE_REJECT, parse_retrieve_reject, build_retrieve_reject, "RETRIEVE_REJECT" }, + { CC_DISCONNECT, EVENT_DISCONNECT, parse_disconnect, build_disconnect, "DISCONNECT" }, + { CC_RESTART, EVENT_RESTART, parse_restart, build_restart, "RESTART" }, + { CC_RELEASE, EVENT_RELEASE, parse_release, build_release, "RELEASE" }, + { CC_RELEASE_COMPLETE, EVENT_RELEASE_COMPLETE, parse_release_complete, build_release_complete, "RELEASE_COMPLETE" }, + { CC_FACILITY, EVENT_FACILITY, parse_facility, build_facility, "FACILITY" }, + { CC_NOTIFY, EVENT_NOTIFY, parse_notify, build_notify, "NOTIFY" }, + { CC_STATUS_ENQUIRY, EVENT_STATUS_ENQUIRY, parse_status_enquiry, build_status_enquiry, "STATUS_ENQUIRY" }, + { CC_INFORMATION, EVENT_INFORMATION, parse_information, build_information, "INFORMATION" }, + { CC_STATUS, EVENT_STATUS, parse_status, build_status, "STATUS" }, + { CC_TIMEOUT, EVENT_TIMEOUT, parse_timeout, build_timeout, "TIMEOUT" }, + { 0, 0, NULL, NULL, NULL } +/* *INDENT-ON* */ }; #define msgs_max (sizeof(msgs_g)/sizeof(struct isdn_msg)) @@ -1263,15 +1653,15 @@ if (nt){ mISDNuser_head_t *hh = (mISDNuser_head_t*)msg->data; - + for (i=0; i< msgs_max -1; i++) { if ( (hh->prim&COMMAND_MASK)==(msgs[i].misdn_msg&COMMAND_MASK)) return i; } - + } else { iframe_t *frm = (iframe_t*)msg->data; - - for (i=0; i< msgs_max -1; i++) + + for (i=0; i< msgs_max -1; i++) if ( (frm->prim&COMMAND_MASK)==(msgs[i].misdn_msg&COMMAND_MASK)) return i; } @@ -1281,11 +1671,11 @@ int isdn_msg_get_index_by_event(struct isdn_msg msgs[], enum event_e event, int nt) { int i; - for (i=0; i< msgs_max; i++) + for (i=0; i< msgs_max; i++) if ( event == msgs[i].event) return i; cb_log(10,0, "get_index: event not found!\n"); - + return -1; } @@ -1318,9 +1708,9 @@ char * isdn_get_info(struct isdn_msg msgs[], enum event_e event, int nt) { int i=isdn_msg_get_index_by_event(msgs, event, nt); - + if(i>=0) return msgs[i].info; - + if (event == EVENT_CLEANUP) return EVENT_CLEAN_INFO; if (event == EVENT_DTMF_TONE) return EVENT_DTMF_TONE_INFO; if (event == EVENT_NEW_L3ID) return EVENT_NEW_L3ID_INFO; @@ -1331,7 +1721,7 @@ if (event == EVENT_TONE_GENERATE) return EVENT_TONE_GENERATE_INFO; if (event == EVENT_PORT_ALARM) return EVENT_PORT_ALARM_INFO; if (event == EVENT_BCHAN_ERROR) return EVENT_BCHAN_ERROR_INFO; - + return NULL; } @@ -1348,6 +1738,6 @@ { int i=isdn_msg_get_index_by_event(msgs, event, nt); if(i<0) return NULL; - + return msgs[i].msg_builder(msgs, bc, nt); } Index: channels/misdn/portinfo.c =================================================================== --- a/channels/misdn/portinfo.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/misdn/portinfo.c (.../trunk) (revision 202568) @@ -1,4 +1,4 @@ -/*! \file +/*! \file * \brief Interface to mISDN - port info * \author Christian Richter */ Index: channels/misdn/isdn_lib.c =================================================================== --- a/channels/misdn/isdn_lib.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/misdn/isdn_lib.c (.../trunk) (revision 202568) @@ -11,7 +11,7 @@ * the GNU General Public License */ -/*! \file +/*! \file * \brief Interface to mISDN * \author Christian Richter */ @@ -25,15 +25,15 @@ #include "isdn_lib_intern.h" #include "isdn_lib.h" -enum event_response_e (*cb_event) (enum event_e event, struct misdn_bchannel *bc, void *user_data); +enum event_response_e (*cb_event)(enum event_e event, struct misdn_bchannel *bc, void *user_data); -void (*cb_log) (int level, int port, char *tmpl, ...) +void (*cb_log)(int level, int port, char *tmpl, ...) __attribute__ ((format (printf, 3, 4))); int (*cb_jb_empty)(struct misdn_bchannel *bc, char *buffer, int len); -/* +/* * Define ARRAY_LEN() because I cannot * #include "asterisk/utils.h" */ @@ -48,12 +48,8 @@ int misdn_lib_get_l2_up(struct misdn_stack *stack); -struct misdn_stack* get_misdn_stack( void ); +struct misdn_stack *get_misdn_stack(void); -static int set_chan_in_stack(struct misdn_stack *stack, int channel); - -int release_cr(struct misdn_stack *stack, mISDNuser_head_t *hh); - int misdn_lib_port_is_pri(int port) { struct misdn_stack *stack=get_misdn_stack(); @@ -62,7 +58,7 @@ return stack->pri; } } - + return -1; } @@ -74,15 +70,15 @@ return stack->nt; } } - + return -1; } -void misdn_make_dummy(struct misdn_bchannel *dummybc, int port, int l3id, int nt, int channel) +void misdn_make_dummy(struct misdn_bchannel *dummybc, int port, int l3id, int nt, int channel) { memset (dummybc,0,sizeof(struct misdn_bchannel)); dummybc->port=port; - if (l3id==0) + if (l3id==0) dummybc->l3_id = MISDN_ID_DUMMY; else dummybc->l3_id=l3id; @@ -119,7 +115,7 @@ } int misdn_lib_is_port_blocked(int port) -{ +{ struct misdn_stack *stack=get_misdn_stack(); for ( ; stack; stack=stack->next) { if (stack->port == port) { @@ -138,12 +134,12 @@ return -1; } -int misdn_lib_get_maxchans(int port) +int misdn_lib_get_maxchans(int port) { struct misdn_stack *stack=get_misdn_stack(); for ( ; stack; stack=stack->next) { if (stack->port == port) { - if (stack->pri) + if (stack->pri) return 30; else return 2; @@ -159,7 +155,7 @@ if (!bc) return NULL; - + for ( ; stack; stack = stack->next) { if (bc->port == stack->port) return stack; @@ -171,19 +167,24 @@ void get_show_stack_details(int port, char *buf) { - struct misdn_stack *stack=get_misdn_stack(); - - for ( ; stack; stack=stack->next) { - if (stack->port == port) break; + struct misdn_stack *stack = get_misdn_stack(); + + for (; stack; stack = stack->next) { + if (stack->port == port) { + break; + } } - + if (stack) { - sprintf(buf, "* Port %d Type %s Prot. %s L2Link %s L1Link:%s Blocked:%d", - stack->port, stack->nt ? "NT" : "TE", stack->ptp ? "PTP" : "PMP", - stack->l2link ? "UP" : "DOWN", stack->l1link ? "UP" : "DOWN", + sprintf(buf, "* Port %2d Type %s Prot. %s L2Link %s L1Link:%s Blocked:%d", + stack->port, + stack->nt ? "NT" : "TE", + stack->ptp ? "PTP" : "PMP", + stack->l2link ? "UP " : "DOWN", + stack->l1link ? "UP " : "DOWN", stack->blocked); } else { - buf[0]=0; + buf[0] = 0; } } @@ -219,10 +220,10 @@ void *user_data; msg_queue_t upqueue; - msg_queue_t activatequeue; - + msg_queue_t activatequeue; + sem_t new_msg; - + struct misdn_stack *stack_list; } ; @@ -248,7 +249,7 @@ int misdn_lib_port_restart(int port); int misdn_lib_pid_restart(int pid); -extern struct isdn_msg msgs_g[]; +extern struct isdn_msg msgs_g[]; #define ISDN_PID_L3_B_USER 0x430000ff #define ISDN_PID_L4_B_USER 0x440000ff @@ -263,7 +264,7 @@ #define TONE_BUSY_CNT 20 /* ? */ #define TONE_BUSY_SILENCE_CNT 48 /* ? */ -static int entity; +static int entity; static struct misdn_lib *glob_mgr; @@ -280,7 +281,6 @@ /* from isdn_lib.h */ /* user iface */ -int te_lib_init( void ) ; /* returns midev */ void te_lib_destroy(int midev) ; struct misdn_bchannel *manager_find_bc_by_pid(int pid); struct misdn_bchannel *manager_find_bc_holded(struct misdn_bchannel* bc); @@ -304,7 +304,7 @@ "Res Digital", "Unknown Bearer" }; - + switch (cap) { case INFO_CAPABILITY_SPEECH: return bearers[0]; @@ -330,7 +330,7 @@ static void init_flip_bits(void) { int i,k; - + for (i = 0 ; i < 256 ; i++) { unsigned char sample = 0 ; for (k = 0; k<8; k++) { @@ -344,11 +344,11 @@ { int i; char * start = buf; - + for (i = 0 ; i < len; i++) { buf[i] = flip_table[(unsigned char)buf[i]]; } - + return start; } @@ -359,13 +359,13 @@ { int i = 0; msg_t *dmsg; - + while(i < 10) { dmsg = prep_l3data_msg(prim, dinfo, size, 256, NULL); if (dmsg) return(dmsg); - + if (!i) printf("cannot allocate memory, trying again...\n"); i++; @@ -383,10 +383,10 @@ msg_t *dmsg; Q931_info_t *qi; iframe_t *frm; - + if (!ntmode) size = sizeof(Q931_info_t)+2; - + while(i < 10) { if (ntmode) { dmsg = prep_l3data_msg(prim, dinfo, size, 256, NULL); @@ -406,7 +406,7 @@ return(dmsg); } } - + if (!i) printf("cannot allocate memory, trying again...\n"); i++; usleep(300000); @@ -425,11 +425,11 @@ cb_log(0,bc->port,"send_msg: IEK!! no stack\n "); return -1; } - + frm->addr = (stack->upper_id | FLG_MSG_DOWN); frm->dinfo = bc->l3_id; frm->len = (dmsg->len) - mISDN_HEADER_LEN; - + cb_log(4,stack->port,"Sending msg, prim:%x addr:%x dinfo:%x\n",frm->prim,frm->addr,frm->dinfo); mISDN_write(midev, dmsg->data, dmsg->len, TIMEOUT_1SEC); @@ -457,7 +457,7 @@ /* We have opted to never receive any available inband recorded messages */ return 0; } - + switch (bc->progress_indicator) { case INFO_PI_INBAND_AVAILABLE: case INFO_PI_CALL_NOT_E2E_ISDN: @@ -474,9 +474,18 @@ { int i; - for (i=0; i <= stack->b_num; i++) { - cb_log(6, stack->port, "Idx:%d stack->cchan:%d in_use:%d Chan:%d\n",i,stack->channels[i], stack->bc[i].in_use, i+1); + for (i = 0; i <= stack->b_num; ++i) { + cb_log(6, stack->port, "Idx:%d stack->cchan:%d in_use:%d Chan:%d\n", + i, stack->channels[i], stack->bc[i].in_use, i + 1); } +#if defined(AST_MISDN_ENHANCEMENTS) + for (i = MAX_BCHANS + 1; i < ARRAY_LEN(stack->bc); ++i) { + if (stack->bc[i].in_use) { + cb_log(6, stack->port, "Idx:%d stack->cchan:%d REGISTER Chan:%d in_use\n", + i, stack->channels[i], i + 1); + } + } +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ } @@ -491,10 +500,9 @@ static int set_chan_in_stack(struct misdn_stack *stack, int channel) { - cb_log(4,stack->port,"set_chan_in_stack: %d\n",channel); dump_chan_list(stack); - if (channel >=1 && channel <= MAX_BCHANS) { + if (1 <= channel && channel <= ARRAY_LEN(stack->channels)) { if (!stack->channels[channel-1]) stack->channels[channel-1] = 1; else { @@ -505,7 +513,7 @@ cb_log(0,stack->port,"couldn't set channel %d in\n", channel ); return -1; } - + return 0; } @@ -514,78 +522,92 @@ static int find_free_chan_in_stack(struct misdn_stack *stack, struct misdn_bchannel *bc, int channel, int dec) { int i; - int chan=0; - int bnums = stack->pri ? stack->b_num : stack->b_num - 1; + int chan = 0; + int bnums; - if (bc->channel_found) + if (bc->channel_found) { return 0; - - bc->channel_found=1; + } - cb_log(5,stack->port,"find_free_chan: req_chan:%d\n",channel); + bc->channel_found = 1; - if (channel < 0 || channel > MAX_BCHANS) { - cb_log(0, stack->port, " !! out of bound call to find_free_chan_in_stack! (ch:%d)\n", channel); - return 0; - } - - channel--; +#if defined(AST_MISDN_ENHANCEMENTS) + if (bc->is_register_pool) { + for (i = MAX_BCHANS + 1; i < ARRAY_LEN(stack->channels); ++i) { + if (!stack->channels[i]) { + chan = i + 1; + cb_log(3, stack->port, " --> found REGISTER chan: %d\n", chan); + break; + } + } + } else +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + { + cb_log(5, stack->port, "find_free_chan: req_chan:%d\n", channel); - if (dec) { - for (i = bnums; i >=0; i--) { - if (i != 15 && (channel < 0 || i == channel)) { /* skip E1 D channel ;) and work with chan preselection */ - if (!stack->channels[i]) { - cb_log (3, stack->port, " --> found chan%s: %d\n", channel>=0?" (preselected)":"", i+1); - chan=i+1; - break; + if (channel < 0 || channel > MAX_BCHANS) { + cb_log(0, stack->port, " !! out of bound call to find_free_chan_in_stack! (ch:%d)\n", channel); + return 0; + } + + --channel; + + bnums = stack->pri ? stack->b_num : stack->b_num - 1; + if (dec) { + for (i = bnums; i >= 0; --i) { + if (i != 15 && (channel < 0 || i == channel)) { /* skip E1 D channel ;) and work with chan preselection */ + if (!stack->channels[i]) { + chan = i + 1; + cb_log(3, stack->port, " --> found chan%s: %d\n", channel >= 0 ? " (preselected)" : "", chan); + break; + } } } - } - } else { - for (i = 0; i <= bnums; i++) { - if (i != 15 && (channel < 0 || i == channel)) { /* skip E1 D channel ;) and work with chan preselection */ - if (!stack->channels[i]) { - cb_log (3, stack->port, " --> found chan%s: %d\n", channel>=0?" (preselected)":"", i+1); - chan=i+1; - break; + } else { + for (i = 0; i <= bnums; ++i) { + if (i != 15 && (channel < 0 || i == channel)) { /* skip E1 D channel ;) and work with chan preselection */ + if (!stack->channels[i]) { + chan = i + 1; + cb_log(3, stack->port, " --> found chan%s: %d\n", channel >= 0 ? " (preselected)" : "", chan); + break; + } } } } } if (!chan) { - cb_log (1, stack->port, " !! NO FREE CHAN IN STACK\n"); + cb_log(1, stack->port, " !! NO FREE CHAN IN STACK\n"); dump_chan_list(stack); bc->out_cause = AST_CAUSE_NORMAL_CIRCUIT_CONGESTION; return -1; - } + } - if (set_chan_in_stack(stack, chan)<0) { - cb_log (0, stack->port, "Channel Already in use:%d\n", chan); + if (set_chan_in_stack(stack, chan) < 0) { + cb_log(0, stack->port, "Channel Already in use:%d\n", chan); bc->out_cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL; return -1; } - bc->channel=chan; + bc->channel = chan; return 0; } -static int empty_chan_in_stack(struct misdn_stack *stack, int channel) +static void empty_chan_in_stack(struct misdn_stack *stack, int channel) { - if (channel<=0 || channel>MAX_BCHANS) { - cb_log(0,stack?stack->port:0, "empty_chan_in_stack: cannot empty channel %d\n",channel); - return -1; + if (channel < 1 || ARRAY_LEN(stack->channels) < channel) { + cb_log(0, stack->port, "empty_chan_in_stack: cannot empty channel %d\n", channel); + return; } - - cb_log (4, stack?stack->port:0, "empty_chan_in_stack: %d\n",channel); - stack->channels[channel-1] = 0; + + cb_log(4, stack->port, "empty_chan_in_stack: %d\n", channel); + stack->channels[channel - 1] = 0; dump_chan_list(stack); - return 0; } char *bc_state2str(enum bchannel_state state) { int i; - + struct bchan_state_s { char *n; enum bchannel_state s; @@ -604,7 +626,7 @@ {"BCHAN_CLEAN_REQUEST", BCHAN_CLEAN_REQUEST}, {"BCHAN_ERROR", BCHAN_ERROR} }; - + for (i=0; i< sizeof(states)/sizeof(struct bchan_state_s); i++) if ( states[i].s == state) return states[i].n; @@ -618,7 +640,7 @@ bc->l3_id, bc_state2str(bc->bc_state), bc_state2str(state) ); - + switch (state) { case BCHAN_ACTIVATED: if (bc->next_bc_state == BCHAN_BRIDGED) { @@ -644,6 +666,38 @@ static void empty_bc(struct misdn_bchannel *bc) { + bc->caller.presentation = 0; /* allowed */ + bc->caller.number_plan = NUMPLAN_ISDN; + bc->caller.number_type = NUMTYPE_UNKNOWN; + bc->caller.name[0] = 0; + bc->caller.number[0] = 0; + bc->caller.subaddress[0] = 0; + + bc->connected.presentation = 0; /* allowed */ + bc->connected.number_plan = NUMPLAN_ISDN; + bc->connected.number_type = NUMTYPE_UNKNOWN; + bc->connected.name[0] = 0; + bc->connected.number[0] = 0; + bc->connected.subaddress[0] = 0; + + bc->redirecting.from.presentation = 0; /* allowed */ + bc->redirecting.from.number_plan = NUMPLAN_ISDN; + bc->redirecting.from.number_type = NUMTYPE_UNKNOWN; + bc->redirecting.from.name[0] = 0; + bc->redirecting.from.number[0] = 0; + bc->redirecting.from.subaddress[0] = 0; + + bc->redirecting.to.presentation = 0; /* allowed */ + bc->redirecting.to.number_plan = NUMPLAN_ISDN; + bc->redirecting.to.number_type = NUMTYPE_UNKNOWN; + bc->redirecting.to.name[0] = 0; + bc->redirecting.to.number[0] = 0; + bc->redirecting.to.subaddress[0] = 0; + + bc->redirecting.reason = mISDN_REDIRECTING_REASON_UNKNOWN; + bc->redirecting.count = 0; + bc->redirecting.to_changed = 0; + bc->dummy=0; bc->bframe_len=0; @@ -656,38 +710,32 @@ bc->sending_complete = 0; bc->restart_channel=0; - + bc->conf_id = 0; bc->need_more_infos = 0; - + bc->send_dtmf=0; bc->nodsp=0; bc->nojitter=0; bc->time_usec=0; - + bc->rxgain=0; bc->txgain=0; bc->crypt=0; bc->curptx=0; bc->curprx=0; - + bc->crypt_key[0] = 0; - + bc->generate_tone=0; bc->tone_cnt=0; - - bc->dnumplan=NUMPLAN_UNKNOWN; - bc->onumplan=NUMPLAN_UNKNOWN; - bc->rnumplan=NUMPLAN_UNKNOWN; - bc->cpnnumplan=NUMPLAN_UNKNOWN; - bc->active = 0; bc->early_bconnect = 1; - + #ifdef MISDN_1_2 *bc->pipeline = 0; #else @@ -698,17 +746,31 @@ bc->AOCD_need_export = 0; bc->orig=0; - + bc->cause = AST_CAUSE_NORMAL_CLEARING; bc->out_cause = AST_CAUSE_NORMAL_CLEARING; - bc->pres = 0; /* allowed */ - + + bc->display_connected = 0; /* none */ + bc->display_setup = 0; /* none */ + + bc->outgoing_colp = 0;/* pass */ + + bc->presentation = 0; /* allowed */ + bc->set_presentation = 0; + + bc->notify_description_code = mISDN_NOTIFY_CODE_INVALID; + bc->evq=EVENT_NOTHING; bc->progress_coding=0; bc->progress_location=0; bc->progress_indicator=0; - + +#if defined(AST_MISDN_ENHANCEMENTS) + bc->div_leg_3_rx_wanted = 0; + bc->div_leg_3_tx_pending = 0; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + /** Set Default Bearer Caps **/ bc->capability=INFO_CAPABILITY_SPEECH; bc->law=INFO_CODEC_ALAW; @@ -716,24 +778,23 @@ bc->rate=0x10; bc->user1=0; bc->urate=0; - + bc->hdlc=0; - - + + bc->dialed.number_plan = NUMPLAN_ISDN; + bc->dialed.number_type = NUMTYPE_UNKNOWN; + bc->dialed.number[0] = 0; + bc->dialed.subaddress[0] = 0; + bc->info_dad[0] = 0; bc->display[0] = 0; bc->infos_pending[0] = 0; - bc->cad[0] = 0; - bc->oad[0] = 0; - bc->dad[0] = 0; - bc->rad[0] = 0; - bc->orig_dad[0] = 0; bc->uu[0]=0; bc->uulen=0; - + bc->fac_in.Function = Fac_None; bc->fac_out.Function = Fac_None; - + bc->te_choose_channel = 0; bc->channel_found= 0; @@ -748,32 +809,32 @@ struct misdn_stack * stack; cb_log(3, bc?bc->port:0, "$$$ CLEANUP CALLED pid:%d\n", bc?bc->pid:-1); - + if (!bc ) return -1; stack=get_stack_by_bc(bc); - + if (!stack) return -1; - + switch (bc->bc_state ) { case BCHAN_CLEANED: cb_log(5, stack->port, "$$$ Already cleaned up bc with stid :%x\n", bc->b_stid); return -1; - + default: break; } - + cb_log(2, stack->port, "$$$ Cleaning up bc with stid :%x pid:%d\n", bc->b_stid, bc->pid); - + manager_ec_disable(bc); manager_bchannel_deactivate(bc); mISDN_write_frame(stack->midev, buff, bc->layer_id|FLG_MSG_TARGET|FLG_MSG_DOWN, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); - + bc->b_stid = 0; bc_state_change(bc, BCHAN_CLEANED); - + return ret; } @@ -783,27 +844,31 @@ { int i; - for (i=0; i<=stack->b_num; i++) { - if (global_state == MISDN_INITIALIZED) { - cb_event(EVENT_CLEANUP, &stack->bc[i], NULL); - empty_chan_in_stack(stack,i+1); + if (global_state == MISDN_INITIALIZED) { + for (i = 0; i <= stack->b_num; ++i) { + cb_event(EVENT_CLEANUP, &stack->bc[i], NULL); + empty_chan_in_stack(stack, i + 1); empty_bc(&stack->bc[i]); clean_up_bc(&stack->bc[i]); stack->bc[i].in_use = 0; } - - } +#if defined(AST_MISDN_ENHANCEMENTS) + for (i = MAX_BCHANS + 1; i < ARRAY_LEN(stack->bc); ++i) { + empty_chan_in_stack(stack, i + 1); + empty_bc(&stack->bc[i]); + stack->bc[i].in_use = 0; + } +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + } } static int new_te_id = 0; -#define MAXPROCS 0x100 - static int misdn_lib_get_l1_down(struct misdn_stack *stack) { - /* Pull Up L1 */ + /* Pull Up L1 */ iframe_t act; - act.prim = PH_DEACTIVATE | REQUEST; + act.prim = PH_DEACTIVATE | REQUEST; act.addr = stack->lower_id|FLG_MSG_DOWN; act.dinfo = 0; act.len = 0; @@ -815,38 +880,38 @@ static int misdn_lib_get_l2_down(struct misdn_stack *stack) { - + if (stack->ptp && (stack->nt) ) { msg_t *dmsg; /* L2 */ dmsg = create_l2msg(DL_RELEASE| REQUEST, 0, 0); - + if (stack->nst.manager_l3(&stack->nst, dmsg)) free_msg(dmsg); - + } else { iframe_t act; - + act.prim = DL_RELEASE| REQUEST; act.addr = (stack->upper_id |FLG_MSG_DOWN) ; - + act.dinfo = 0; act.len = 0; return mISDN_write(stack->midev, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC); } - + return 0; } static int misdn_lib_get_l1_up(struct misdn_stack *stack) { - /* Pull Up L1 */ + /* Pull Up L1 */ iframe_t act; - act.prim = PH_ACTIVATE | REQUEST; + act.prim = PH_ACTIVATE | REQUEST; act.addr = (stack->upper_id | FLG_MSG_DOWN) ; - + act.dinfo = 0; act.len = 0; @@ -856,26 +921,26 @@ int misdn_lib_get_l2_up(struct misdn_stack *stack) { - + if (stack->ptp && (stack->nt) ) { msg_t *dmsg; /* L2 */ dmsg = create_l2msg(DL_ESTABLISH | REQUEST, 0, 0); - + if (stack->nst.manager_l3(&stack->nst, dmsg)) free_msg(dmsg); - + } else { iframe_t act; - + act.prim = DL_ESTABLISH | REQUEST; act.addr = (stack->upper_id |FLG_MSG_DOWN) ; - + act.dinfo = 0; act.len = 0; return mISDN_write(stack->midev, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC); } - + return 0; } @@ -883,10 +948,10 @@ static int misdn_lib_get_l2_te_ptp_up(struct misdn_stack *stack) { iframe_t act; - + act.prim = DL_ESTABLISH | REQUEST; act.addr = (stack->upper_id & ~LAYER_ID_MASK) | 3 | FLG_MSG_DOWN; - + act.dinfo = 0; act.len = 0; return mISDN_write(stack->midev, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC); @@ -897,14 +962,14 @@ static int misdn_lib_get_short_status(struct misdn_stack *stack) { iframe_t act; - - - act.prim = MGR_SHORTSTATUS | REQUEST; - + + + act.prim = MGR_SHORTSTATUS | REQUEST; + act.addr = (stack->upper_id | MSG_BROADCAST) ; act.dinfo = SSTATUS_BROADCAST_BIT | SSTATUS_ALL; - + act.len = 0; return mISDN_write(stack->midev, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC); } @@ -929,7 +994,7 @@ if (stack->procids[proc_id] == 0) { break; } - } /* end for */ + } if (proc_id == MAXPROCS) { cb_log(0, stack->port, "Couldn't Create New ProcId.\n"); return -1; @@ -996,7 +1061,7 @@ cb_log(0, bc->port, "setup_bc: NO STACK FOUND!!\n"); return -1; } - + midev = stack->midev; channel = bc->channel - 1 - (bc->channel > 16); b_stid = stack->b_stids[channel >= 0 ? channel : 0]; @@ -1008,9 +1073,9 @@ cb_log(4, stack->port, "$$$ bc already setup stid :%x (state:%s)\n", b_stid, bc_state2str(bc->bc_state) ); return -1; } - + cb_log(5, stack->port, "$$$ Setting up bc with stid :%x\n", b_stid); - + /*check if the b_stid is already initialized*/ for (i=0; i <= stack->b_num; i++) { if (stack->bc[i].b_stid == b_stid) { @@ -1018,10 +1083,10 @@ return -1; } } - + if (b_stid <= 0) { cb_log(0, stack->port," -- Stid <=0 at the moment in channel:%d\n",channel); - + bc_state_change(bc,BCHAN_ERROR); return 1; } @@ -1031,10 +1096,10 @@ { layer_info_t li; memset(&li, 0, sizeof(li)); - + li.object_id = -1; li.extentions = 0; - + li.st = bc->b_stid; /* given idx */ @@ -1044,18 +1109,18 @@ #endif if ( bc->hdlc || bc->nodsp) { cb_log(4, stack->port,"setup_bc: without dsp\n"); - { + { int l = sizeof(li.name); strncpy(li.name, "B L3", l); li.name[l-1] = 0; } li.pid.layermask = ISDN_LAYER((3)); li.pid.protocol[3] = ISDN_PID_L3_B_USER; - + bc->layer=3; } else { cb_log(4, stack->port,"setup_bc: with dsp\n"); - { + { int l = sizeof(li.name); strncpy(li.name, "B L4", l); li.name[l-1] = 0; @@ -1064,8 +1129,8 @@ li.pid.protocol[4] = ISDN_PID_L4_B_USER; bc->layer=4; - } - + } + ret = mISDN_new_layer(midev, &li); if (ret ) { cb_log(0, stack->port,"New Layer Err: %d %s\n",ret,strerror(errno)); @@ -1073,23 +1138,23 @@ bc_state_change(bc,BCHAN_ERROR); return(-EINVAL); } - + bc->layer_id = li.id; } - + memset(&pid, 0, sizeof(pid)); - - - + + + cb_log(4, stack->port," --> Channel is %d\n", bc->channel); - + if (bc->nodsp) { cb_log(2, stack->port," --> TRANSPARENT Mode (no DSP, no HDLC)\n"); pid.protocol[1] = ISDN_PID_L1_B_64TRANS; pid.protocol[2] = ISDN_PID_L2_B_TRANS; pid.protocol[3] = ISDN_PID_L3_B_USER; pid.layermask = ISDN_LAYER((1)) | ISDN_LAYER((2)) | ISDN_LAYER((3)); - + } else if ( bc->hdlc ) { cb_log(2, stack->port," --> HDLC Mode\n"); pid.protocol[1] = ISDN_PID_L1_B_64HDLC ; @@ -1103,16 +1168,16 @@ pid.protocol[3] = ISDN_PID_L3_B_DSP; pid.protocol[4] = ISDN_PID_L4_B_USER; pid.layermask = ISDN_LAYER((1)) | ISDN_LAYER((2)) | ISDN_LAYER((3)) | ISDN_LAYER((4)); - - } + } + ret = mISDN_set_stack(midev, bc->b_stid, &pid); if (ret){ cb_log(0, stack->port,"$$$ Set Stack Err: %d %s\n",ret,strerror(errno)); - + mISDN_write_frame(midev, buff, bc->layer_id, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); - + bc_state_change(bc,BCHAN_ERROR); cb_event(EVENT_BCHAN_ERROR, bc, glob_mgr->user_data); return(-EINVAL); @@ -1123,7 +1188,7 @@ if (ret) { cb_log(0, stack->port,"$$$ Set StackIND Err: %d %s\n",ret,strerror(errno)); mISDN_write_frame(midev, buff, bc->layer_id, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); - + bc_state_change(bc,BCHAN_ERROR); cb_event(EVENT_BCHAN_ERROR, bc, glob_mgr->user_data); return(-EINVAL); @@ -1136,14 +1201,14 @@ if (!bc->addr) { cb_log(0, stack->port,"$$$ Get Layerid Err: %d %s\n",ret,strerror(errno)); mISDN_write_frame(midev, buff, bc->layer_id, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); - + bc_state_change(bc,BCHAN_ERROR); cb_event(EVENT_BCHAN_ERROR, bc, glob_mgr->user_data); return (-EINVAL); } manager_bchannel_activate(bc); - + bc_state_change(bc,BCHAN_ACTIVATED); return 0; @@ -1152,70 +1217,65 @@ /** IFACE **/ -static int init_bc(struct misdn_stack *stack, struct misdn_bchannel *bc, int midev, int port, int bidx, char *msn, int firsttime) +static int init_bc(struct misdn_stack *stack, struct misdn_bchannel *bc, int midev, int port, int bidx) { - unsigned char buff[1025] = ""; - iframe_t *frm = (iframe_t *)buff; - int ret; - - if (!bc) return -1; - + if (!bc) { + return -1; + } + cb_log(8, port, "Init.BC %d.\n",bidx); - - memset(bc, 0,sizeof(struct misdn_bchannel)); bc->send_lock=malloc(sizeof(struct send_lock)); if (!bc->send_lock) { return -1; } pthread_mutex_init(&bc->send_lock->lock, NULL); - - if (msn) { - int l = sizeof(bc->msn); - strncpy(bc->msn,msn, l); - bc->msn[l-1] = 0; - } - - + empty_bc(bc); bc_state_change(bc, BCHAN_CLEANED); - + bc->port=stack->port; bc->nt=stack->nt?1:0; bc->pri=stack->pri; - + { ibuffer_t* ibuf= init_ibuffer(MISDN_IBUF_SIZE); if (!ibuf) return -1; - + clear_ibuffer( ibuf); - + ibuf->rsem=malloc(sizeof(sem_t)); if (!ibuf->rsem) { return -1; } - + bc->astbuf=ibuf; if (sem_init(ibuf->rsem,1,0)<0) sem_init(ibuf->rsem,0,0); - + } - - { + +#if 0 /* This code does not seem to do anything useful */ + if (bidx <= stack->b_num) { + unsigned char buff[1025]; + iframe_t *frm = (iframe_t *) buff; stack_info_t *stinf; + int ret; + ret = mISDN_get_stack_info(midev, stack->port, buff, sizeof(buff)); if (ret < 0) { cb_log(0, port, "%s: Cannot get stack info for this port. (ret=%d)\n", __FUNCTION__, ret); return -1; } - + stinf = (stack_info_t *)&frm->data.p; - + cb_log(8, port, " --> Child %x\n",stinf->child[bidx]); } - +#endif + return 0; } @@ -1227,36 +1287,34 @@ unsigned char buff[1025]; iframe_t *frm = (iframe_t *)buff; stack_info_t *stinf; - int i; + struct misdn_stack *stack; + int i; layer_info_t li; - struct misdn_stack *stack = malloc(sizeof(struct misdn_stack)); - if (!stack ) return NULL; + stack = calloc(1, sizeof(struct misdn_stack)); + if (!stack) { + return NULL; + } + cb_log(8, port, "Init. Stack.\n"); - cb_log(8, port, "Init. Stack.\n"); - - memset(stack,0,sizeof(struct misdn_stack)); - - for (i=0; ichannels[i]=0; - stack->port=port; stack->midev=midev; stack->ptp=ptp; - + stack->holding=NULL; stack->pri=0; - + msg_queue_init(&stack->downqueue); msg_queue_init(&stack->upqueue); - + /* query port's requirements */ ret = mISDN_get_stack_info(midev, port, buff, sizeof(buff)); if (ret < 0) { cb_log(0, port, "%s: Cannot get stack info for this port. (ret=%d)\n", __FUNCTION__, ret); return(NULL); } - + stinf = (stack_info_t *)&frm->data.p; stack->d_stid = stinf->id; @@ -1264,7 +1322,7 @@ for (i=0; i<=stinf->childcnt; i++) stack->b_stids[i] = stinf->child[i]; - + switch(stinf->pid.protocol[0] & ~ISDN_PID_FEATURE_MASK) { case ISDN_PID_L0_TE_S0: stack->nt=0; @@ -1283,7 +1341,7 @@ cb_log(8, port, "TE S2M Stack\n"); stack->nt=1; stack->pri=1; - + break; default: cb_log(0, port, "this is a unknown port type 0x%08x\n", stinf->pid.protocol[0]); @@ -1291,19 +1349,19 @@ } if (!stack->nt) { - if (stinf->pid.protocol[2] & ISDN_PID_L2_DF_PTP ) { + if (stinf->pid.protocol[2] & ISDN_PID_L2_DF_PTP ) { stack->ptp = 1; } else { stack->ptp = 0; } } - + { int ret; int nt=stack->nt; cb_log(8, port, "Init. Stack.\n"); - + memset(&li, 0, sizeof(li)); { int l = sizeof(li.name); @@ -1315,15 +1373,15 @@ li.pid.protocol[nt?2:4] = nt?ISDN_PID_L2_LAPD_NET:ISDN_PID_L4_CAPI20; li.pid.layermask = ISDN_LAYER((nt?2:4)); li.st = stack->d_stid; - - + + ret = mISDN_new_layer(midev, &li); if (ret) { cb_log(0, port, "%s: Cannot add layer %d to this port.\n", __FUNCTION__, nt?2:4); return(NULL); } - - + + stack->upper_id = li.id; ret = mISDN_register_layer(midev, stack->d_stid, stack->upper_id); if (ret) @@ -1331,60 +1389,55 @@ cb_log(0,port,"Cannot register layer %d of this port.\n", nt?2:4); return(NULL); } - - stack->lower_id = mISDN_get_layerid(midev, stack->d_stid, nt?1:3); + + stack->lower_id = mISDN_get_layerid(midev, stack->d_stid, nt?1:3); if (stack->lower_id < 0) { cb_log(0, port, "%s: Cannot get layer(%d) id of this port.\n", __FUNCTION__, nt?1:3); return(NULL); } - + stack->upper_id = mISDN_get_layerid(midev, stack->d_stid, nt?2:4); if (stack->upper_id < 0) { cb_log(0, port, "%s: Cannot get layer(%d) id of this port.\n", __FUNCTION__, 2); return(NULL); } - + cb_log(8, port, "NT Stacks upper_id %x\n",stack->upper_id); - - + + /* create nst (nt-mode only) */ if (nt) { - + memset(&stack->nst, 0, sizeof(net_stack_t)); memset(&stack->mgr, 0, sizeof(manager_t)); - + stack->mgr.nst = &stack->nst; stack->nst.manager = &stack->mgr; - + stack->nst.l3_manager = handle_event_nt; stack->nst.device = midev; stack->nst.cardnr = port; stack->nst.d_stid = stack->d_stid; - + stack->nst.feature = FEATURE_NET_HOLD; if (stack->ptp) stack->nst.feature |= FEATURE_NET_PTP; if (stack->pri) stack->nst.feature |= FEATURE_NET_CRLEN2 | FEATURE_NET_EXTCID; - + stack->nst.l1_id = stack->lower_id; stack->nst.l2_id = stack->upper_id; - + msg_queue_init(&stack->nst.down_queue); - + Isdnl2Init(&stack->nst); Isdnl3Init(&stack->nst); - - } - - if (!stack->nt) { - /*assume L1 is up, we'll get DEACTIVATES soon, for non - * up L1s*/ - stack->l1link=0; + } + stack->l1link=0; stack->l2link=0; -#if 0 +#if 0 if (!stack->nt) { misdn_lib_get_short_status(stack); } else { @@ -1396,12 +1449,12 @@ misdn_lib_get_short_status(stack); misdn_lib_get_l1_up(stack); - misdn_lib_get_l2_up(stack); - + misdn_lib_get_l2_up(stack); + } cb_log(8,0,"stack_init: port:%d lowerId:%x upperId:%x\n",stack->port,stack->lower_id, stack->upper_id); - + return stack; } @@ -1415,11 +1468,11 @@ cleanup_Isdnl2(&stack->nst); cleanup_Isdnl3(&stack->nst); } - - if (stack->lower_id) + + if (stack->lower_id) mISDN_write_frame(stack->midev, buf, stack->lower_id, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); - if (stack->upper_id) + if (stack->upper_id) mISDN_write_frame(stack->midev, buf, stack->upper_id, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); } @@ -1427,59 +1480,85 @@ static struct misdn_stack * find_stack_by_addr(int addr) { struct misdn_stack *stack; - - for (stack=glob_mgr->stack_list; - stack; - stack=stack->next) { - if ( (stack->upper_id&STACK_ID_MASK) == (addr&STACK_ID_MASK)) return stack; + for (stack = glob_mgr->stack_list; stack; stack = stack->next) { + if ((stack->upper_id & STACK_ID_MASK) == (addr & STACK_ID_MASK)) { + /* Found the stack */ + break; + } } - - return NULL; + + return stack; } -static struct misdn_stack * find_stack_by_port(int port) +static struct misdn_stack *find_stack_by_port(int port) { struct misdn_stack *stack; - - for (stack=glob_mgr->stack_list; - stack; - stack=stack->next) - if (stack->port == port) return stack; - - return NULL; + + for (stack = glob_mgr->stack_list; stack; stack = stack->next) { + if (stack->port == port) { + /* Found the stack */ + break; + } + } + + return stack; } -static struct misdn_stack * find_stack_by_mgr(manager_t* mgr_nt) +static struct misdn_stack *find_stack_by_mgr(manager_t *mgr_nt) { struct misdn_stack *stack; - - for (stack=glob_mgr->stack_list; - stack; - stack=stack->next) - if ( &stack->mgr == mgr_nt) return stack; - - return NULL; + + for (stack = glob_mgr->stack_list; stack; stack = stack->next) { + if (&stack->mgr == mgr_nt) { + /* Found the stack */ + break; + } + } + + return stack; } static struct misdn_bchannel *find_bc_by_masked_l3id(struct misdn_stack *stack, unsigned long l3id, unsigned long mask) { int i; - for (i=0; i<=stack->b_num; i++) { - if ( (stack->bc[i].l3_id & mask) == (l3id & mask)) return &stack->bc[i] ; + + for (i = 0; i <= stack->b_num; ++i) { + if ((stack->bc[i].l3_id & mask) == (l3id & mask)) { + return &stack->bc[i]; + } } - return stack_holder_find(stack,l3id); +#if defined(AST_MISDN_ENHANCEMENTS) + /* Search the B channel records for a REGISTER signaling link. */ + for (i = MAX_BCHANS + 1; i < ARRAY_LEN(stack->bc); ++i) { + if ((stack->bc[i].l3_id & mask) == (l3id & mask)) { + return &stack->bc[i]; + } + } +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + return stack_holder_find(stack, l3id); } struct misdn_bchannel *find_bc_by_l3id(struct misdn_stack *stack, unsigned long l3id) { int i; - for (i=0; i<=stack->b_num; i++) { - if (stack->bc[i].l3_id == l3id) return &stack->bc[i] ; + + for (i = 0; i <= stack->b_num; ++i) { + if (stack->bc[i].l3_id == l3id) { + return &stack->bc[i]; + } } - return stack_holder_find(stack,l3id); +#if defined(AST_MISDN_ENHANCEMENTS) + /* Search the B channel records for a REGISTER signaling link. */ + for (i = MAX_BCHANS + 1; i < ARRAY_LEN(stack->bc); ++i) { + if (stack->bc[i].l3_id == l3id) { + return &stack->bc[i]; + } + } +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + return stack_holder_find(stack, l3id); } static struct misdn_bchannel *find_bc_holded(struct misdn_stack *stack) @@ -1506,7 +1585,7 @@ } } } - + return NULL; } @@ -1514,7 +1593,7 @@ { struct misdn_stack* stack; int i; - + for (stack=glob_mgr->stack_list; stack; stack=stack->next) { @@ -1533,14 +1612,14 @@ struct misdn_stack* stack=find_stack_by_port(port); int i; - if (!stack) return NULL; - + if (!stack) return NULL; + for (i=0; i<=stack->b_num; i++) { if ( stack->bc[i].channel== channel ) { return &stack->bc[i]; } } - + return NULL; } @@ -1551,16 +1630,23 @@ static int handle_event ( struct misdn_bchannel *bc, enum event_e event, iframe_t *frm) { struct misdn_stack *stack=get_stack_by_bc(bc); - + if (!stack->nt) { - + switch (event) { case EVENT_CONNECT_ACKNOWLEDGE: setup_bc(bc); if ( *bc->crypt_key ) { - cb_log(4, stack->port, "ENABLING BLOWFISH channel:%d oad%d:%s dad%d:%s\n", bc->channel, bc->onumplan,bc->oad, bc->dnumplan,bc->dad); + cb_log(4, stack->port, + "ENABLING BLOWFISH channel:%d caller%d:\"%s\" <%s> dialed%d:%s\n", + bc->channel, + bc->caller.number_type, + bc->caller.name, + bc->caller.number, + bc->dialed.number_type, + bc->dialed.number); manager_ph_control_block(bc, BF_ENABLE_KEY, bc->crypt_key, strlen(bc->crypt_key) ); } @@ -1582,7 +1668,14 @@ case EVENT_CONNECT: if ( *bc->crypt_key ) { - cb_log(4, stack->port, "ENABLING BLOWFISH channel:%d oad%d:%s dad%d:%s\n", bc->channel, bc->onumplan,bc->oad, bc->dnumplan,bc->dad); + cb_log(4, stack->port, + "ENABLING BLOWFISH channel:%d caller%d:\"%s\" <%s> dialed%d:%s\n", + bc->channel, + bc->caller.number_type, + bc->caller.name, + bc->caller.number, + bc->dialed.number_type, + bc->dialed.number); manager_ph_control_block(bc, BF_ENABLE_KEY, bc->crypt_key, strlen(bc->crypt_key) ); } case EVENT_ALERTING: @@ -1600,25 +1693,28 @@ break; } - if (!bc->channel) + if (!bc->channel) { cb_log(0, stack->port, "Any Channel Requested, but we have no more!!\n"); - else - cb_log(0, stack->port, "Requested Channel Already in Use releasing this call with cause 34!!!!\n"); + } else { + cb_log(0, stack->port, + "Requested Channel Already in Use releasing this call with cause %d!!!!\n", + bc->out_cause); + } /* when the channel is already in use, we can't - * simply clear it, we need to make sure that - * it will still be marked as in_use in the + * simply clear it, we need to make sure that + * it will still be marked as in_use in the * available channels list.*/ bc->channel=0; misdn_lib_send_event(bc,EVENT_RELEASE_COMPLETE); return -1; } + + setup_bc(bc); + break; } - setup_bc(bc); - break; - case EVENT_RELEASE_COMPLETE: case EVENT_RELEASE: break; @@ -1626,17 +1722,19 @@ break; } } else { /** NT MODE **/ - + } return 0; } static int handle_cr ( struct misdn_stack *stack, iframe_t *frm) { + struct misdn_bchannel dummybc; struct misdn_bchannel *bc; + int channel; if (!stack) return -1; - + switch (frm->prim) { case CC_NEW_CR|INDICATION: cb_log(7, stack->port, " --> lib: NEW_CR Ind with l3id:%x on this port.\n",frm->dinfo); @@ -1646,7 +1744,7 @@ cb_log(0, stack->port, " --> !! lib: No free channel!\n"); return -1; } - + cb_log(7, stack->port, " --> new_process: New L3Id: %x\n",frm->dinfo); bc->l3_id=frm->dinfo; return 1; @@ -1659,54 +1757,44 @@ case CC_RELEASE_CR|CONFIRM: break; case CC_RELEASE_CR|INDICATION: - cb_log(4, stack->port, " --> lib: RELEASE_CR Ind with l3id:%x\n",frm->dinfo); - { - struct misdn_bchannel *bc=find_bc_by_l3id(stack, frm->dinfo); - struct misdn_bchannel dummybc; - - if (!bc) { - cb_log(4, stack->port, " --> Didn't find BC so temporarily creating dummy BC (l3id:%x) on this port.\n", frm->dinfo); - misdn_make_dummy(&dummybc, stack->port, frm->dinfo, stack->nt, 0); - - bc=&dummybc; - } - - if (bc) { - int channel = bc->channel; - cb_log(4, stack->port, " --> lib: CLEANING UP l3id: %x\n",frm->dinfo); + cb_log(4, stack->port, " --> lib: RELEASE_CR Ind with l3id:%x\n", frm->dinfo); + bc = find_bc_by_l3id(stack, frm->dinfo); + if (!bc) { + cb_log(4, stack->port, " --> Didn't find BC so temporarily creating dummy BC (l3id:%x) on this port.\n", frm->dinfo); + misdn_make_dummy(&dummybc, stack->port, frm->dinfo, stack->nt, 0); + bc = &dummybc; + } - /*bc->pid = 0;*/ - bc->need_disconnect=0; - bc->need_release=0; - bc->need_release_complete=0; + channel = bc->channel; + cb_log(4, stack->port, " --> lib: CLEANING UP l3id: %x\n", frm->dinfo); - cb_event(EVENT_CLEANUP, bc, glob_mgr->user_data); + /* bc->pid = 0; */ + bc->need_disconnect = 0; + bc->need_release = 0; + bc->need_release_complete = 0; - empty_bc(bc); - clean_up_bc(bc); + cb_event(EVENT_CLEANUP, bc, glob_mgr->user_data); - if (channel>0) - empty_chan_in_stack(stack,channel); - bc->in_use=0; + empty_bc(bc); + clean_up_bc(bc); - dump_chan_list(stack); + if (channel > 0) + empty_chan_in_stack(stack, channel); + bc->in_use = 0; - if (bc->stack_holder) { - cb_log(4,stack->port, "REMOVING Holder\n"); - stack_holder_remove( stack, bc); - free(bc); - } - } - else { - if (stack->nt) - cb_log(4, stack->port, "BC with dinfo: %x not found.. (prim was %x and addr %x)\n",frm->dinfo, frm->prim, frm->addr); - } - - return 1; + dump_chan_list(stack); + + if (bc->stack_holder) { + cb_log(4, stack->port, "REMOVING Holder\n"); + stack_holder_remove(stack, bc); + free(bc); } + + return 1; + default: break; } - + return 0; } @@ -1720,10 +1808,10 @@ cb_log(1,0,"misdn_release: No Stack found\n"); return; } - - if (bc->channel>0) + + if (bc->channel>0) empty_chan_in_stack(stack,bc->channel); - + empty_bc(bc); clean_up_bc(bc); bc->in_use=0; @@ -1732,21 +1820,21 @@ -int misdn_lib_get_port_up (int port) -{ /* Pull Up L1 */ +int misdn_lib_get_port_up (int port) +{ /* Pull Up L1 */ struct misdn_stack *stack; - + for (stack=glob_mgr->stack_list; stack; stack=stack->next) { - + if (stack->port == port) { if (!stack->l1link) misdn_lib_get_l1_up(stack); if (!stack->l2link) misdn_lib_get_l2_up(stack); - + return 0; } } @@ -1754,8 +1842,8 @@ } -int misdn_lib_get_port_down (int port) -{ /* Pull Down L1 */ +int misdn_lib_get_port_down (int port) +{ /* Pull Down L1 */ struct misdn_stack *stack; for (stack=glob_mgr->stack_list; stack; @@ -1778,7 +1866,7 @@ for (stack=glob_mgr->stack_list; stack; stack=stack->next) { - + if (stack->port == port) { if (stack->blocked) { @@ -1805,36 +1893,33 @@ } } } - + return -1; } -int release_cr(struct misdn_stack *stack, mISDNuser_head_t *hh) +static int release_cr(struct misdn_stack *stack, mISDNuser_head_t *hh) { struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); struct misdn_bchannel dummybc; iframe_t frm; /* fake te frm to remove callref from global callreflist */ + frm.dinfo = hh->dinfo; - frm.addr=stack->upper_id | FLG_MSG_DOWN; - frm.prim = CC_RELEASE_CR|INDICATION; cb_log(4, stack->port, " --> CC_RELEASE_CR: Faking Release_cr for %x l3id:%x\n",frm.addr, frm.dinfo); + /** removing procid **/ if (!bc) { cb_log(4, stack->port, " --> Didn't find BC so temporarily creating dummy BC (l3id:%x) on this port.\n", hh->dinfo); misdn_make_dummy(&dummybc, stack->port, hh->dinfo, stack->nt, 0); - bc=&dummybc; + bc=&dummybc; } - if (bc) { - if ( (bc->l3_id & 0xff00) == 0xff00) { - cb_log(4, stack->port, " --> Removing Process Id:%x on this port.\n", bc->l3_id&0xff); - stack->procids[bc->l3_id&0xff] = 0 ; - } + if ((bc->l3_id & 0xff00) == 0xff00) { + cb_log(4, stack->port, " --> Removing Process Id:%x on this port.\n", bc->l3_id & 0xff); + stack->procids[bc->l3_id & 0xff] = 0; } - else cb_log(0, stack->port, "Couldn't find BC so I couldn't remove the Process!!!! this is a bad port.\n"); if (handle_cr(stack, &frm)<0) { } @@ -1842,318 +1927,319 @@ return 0 ; } -static int -handle_event_nt(void *dat, void *arg) +static int handle_event_nt(void *dat, void *arg) { + struct misdn_bchannel dummybc; + struct misdn_bchannel *bc; manager_t *mgr = (manager_t *)dat; msg_t *msg = (msg_t *)arg; + msg_t *dmsg; mISDNuser_head_t *hh; + struct misdn_stack *stack; + enum event_e event; int reject=0; - - struct misdn_stack *stack=find_stack_by_mgr(mgr); int port; + int l3id; + int channel; + int tmpcause; if (!msg || !mgr) return(-EINVAL); + stack = find_stack_by_mgr(mgr); hh=(mISDNuser_head_t*)msg->data; port=stack->port; - + cb_log(5, stack->port, " --> lib: prim %x dinfo %x\n",hh->prim, hh->dinfo); + switch(hh->prim) { + case CC_RETRIEVE|INDICATION: { - switch(hh->prim){ - case CC_RETRIEVE|INDICATION: - { - struct misdn_bchannel *bc; - struct misdn_bchannel *hold_bc; + struct misdn_bchannel *hold_bc; + iframe_t frm; /* fake te frm to add callref to global callreflist */ - iframe_t frm; /* fake te frm to add callref to global callreflist */ - frm.dinfo = hh->dinfo; + frm.dinfo = hh->dinfo; + frm.addr=stack->upper_id | FLG_MSG_DOWN; + frm.prim = CC_NEW_CR|INDICATION; + if (handle_cr( stack, &frm)< 0) { + goto ERR_NO_CHANNEL; + } - frm.addr=stack->upper_id | FLG_MSG_DOWN; + bc = find_bc_by_l3id(stack, hh->dinfo); + hold_bc = stack_holder_find(stack, bc->l3_id); + cb_log(4, stack->port, "bc_l3id:%x holded_bc_l3id:%x\n",bc->l3_id, hold_bc->l3_id); - frm.prim = CC_NEW_CR|INDICATION; - - if (handle_cr( stack, &frm)< 0) { - msg_t *dmsg; - cb_log(4, stack->port, "Patch from MEIDANIS:Sending RELEASE_COMPLETE %x (No free Chan for you..)\n", hh->dinfo); - dmsg = create_l3msg(CC_RELEASE_COMPLETE | REQUEST,MT_RELEASE_COMPLETE, hh->dinfo,sizeof(RELEASE_COMPLETE_t), 1); - stack->nst.manager_l3(&stack->nst, dmsg); - free_msg(msg); - return 0; - } - - bc = find_bc_by_l3id(stack, hh->dinfo); - hold_bc = stack_holder_find(stack, bc->l3_id); - cb_log(4, stack->port, "bc_l3id:%x holded_bc_l3id:%x\n",bc->l3_id, hold_bc->l3_id); + if (hold_bc) { + cb_log(4, stack->port, "REMOVING Holder\n"); - if (hold_bc) { - cb_log(4, stack->port, "REMOVING Holder\n"); + /* swap the backup to our new channel back */ + stack_holder_remove(stack, hold_bc); + memcpy(bc, hold_bc, sizeof(*bc)); + free(hold_bc); - /*swap the backup to our new channel back*/ - stack_holder_remove(stack, hold_bc); - memcpy(bc, hold_bc, sizeof(*bc)); - free(hold_bc); + bc->holded=0; + bc->b_stid=0; + } + break; + } - bc->holded=0; - bc->b_stid=0; - } - - } - - break; - - case CC_SETUP|CONFIRM: - { - struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); - int l3id = *((int *)(((u_char *)msg->data)+ mISDNUSER_HEAD_SIZE)); - cb_log(4, stack->port, " --> lib: Event_ind:SETUP CONFIRM [NT] : new L3ID is %x\n",l3id ); - - if (!bc) { cb_log(4, stack->port, "Bc Not found (after SETUP CONFIRM)\n"); return 0; } - cb_log (2,bc->port,"I IND :CC_SETUP|CONFIRM: old l3id:%x new l3id:%x\n", bc->l3_id, l3id); - bc->l3_id=l3id; + case CC_SETUP | CONFIRM: + l3id = *((int *) (msg->data + mISDNUSER_HEAD_SIZE)); + + cb_log(4, stack->port, " --> lib: Event_ind:SETUP CONFIRM [NT] : new L3ID is %x\n", l3id); + + bc = find_bc_by_l3id(stack, hh->dinfo); + if (bc) { + cb_log (2, bc->port, "I IND :CC_SETUP|CONFIRM: old l3id:%x new l3id:%x\n", bc->l3_id, l3id); + bc->l3_id = l3id; cb_event(EVENT_NEW_L3ID, bc, glob_mgr->user_data); + } else { + cb_log(4, stack->port, "Bc Not found (after SETUP CONFIRM)\n"); } free_msg(msg); return 0; - - case CC_SETUP|INDICATION: - { - struct misdn_bchannel* bc=misdn_lib_get_free_bc(stack->port, 0, 1, 0); - if (!bc) - ERR_NO_CHANNEL: - { - msg_t *dmsg; - cb_log(4, stack->port, "Patch from MEIDANIS:Sending RELEASE_COMPLETE %x (No free Chan for you..)\n", hh->dinfo); - dmsg = create_l3msg(CC_RELEASE_COMPLETE | REQUEST,MT_RELEASE_COMPLETE, hh->dinfo,sizeof(RELEASE_COMPLETE_t), 1); - stack->nst.manager_l3(&stack->nst, dmsg); - free_msg(msg); - return 0; - } - - cb_log(4, stack->port, " --> new_process: New L3Id: %x\n",hh->dinfo); - bc->l3_id=hh->dinfo; + + case CC_SETUP | INDICATION: + bc = misdn_lib_get_free_bc(stack->port, 0, 1, 0); + if (!bc) { + goto ERR_NO_CHANNEL; } + + cb_log(4, stack->port, " --> new_process: New L3Id: %x\n",hh->dinfo); + bc->l3_id=hh->dinfo; break; - case CC_CONNECT_ACKNOWLEDGE|INDICATION: +#if defined(AST_MISDN_ENHANCEMENTS) + case CC_REGISTER | CONFIRM: + l3id = *((int *) (msg->data + mISDNUSER_HEAD_SIZE)); + + cb_log(4, stack->port, " --> lib: Event_ind:REGISTER CONFIRM [NT] : new L3ID is %x\n", l3id); + + bc = find_bc_by_l3id(stack, hh->dinfo); + if (bc) { + cb_log (2, bc->port, "I IND :CC_REGISTER|CONFIRM: old l3id:%x new l3id:%x\n", bc->l3_id, l3id); + bc->l3_id = l3id; + } else { + cb_log(4, stack->port, "Bc Not found (after REGISTER CONFIRM)\n"); + } + free_msg(msg); + return 0; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + +#if defined(AST_MISDN_ENHANCEMENTS) + case CC_REGISTER | INDICATION: + bc = misdn_lib_get_register_bc(stack->port); + if (!bc) { + goto ERR_NO_CHANNEL; + } + + cb_log(4, stack->port, " --> new_process: New L3Id: %x\n",hh->dinfo); + bc->l3_id=hh->dinfo; break; - - case CC_ALERTING|INDICATION: - case CC_PROCEEDING|INDICATION: - case CC_SETUP_ACKNOWLEDGE|INDICATION: - if(!stack->ptp) break; - case CC_CONNECT|INDICATION: +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + + case CC_CONNECT_ACKNOWLEDGE|INDICATION: break; - case CC_DISCONNECT|INDICATION: - { - struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); - if (!bc) { - bc=find_bc_by_masked_l3id(stack, hh->dinfo, 0xffff0000); - if (bc) { - int myprocid=bc->l3_id&0x0000ffff; - hh->dinfo=(hh->dinfo&0xffff0000)|myprocid; - cb_log(3,stack->port,"Reject dinfo: %x cause:%d\n",hh->dinfo,bc->cause); - reject=1; - } + + case CC_ALERTING|INDICATION: + case CC_PROCEEDING|INDICATION: + case CC_SETUP_ACKNOWLEDGE|INDICATION: + case CC_CONNECT|INDICATION: + break; + case CC_DISCONNECT|INDICATION: + bc = find_bc_by_l3id(stack, hh->dinfo); + if (!bc) { + bc=find_bc_by_masked_l3id(stack, hh->dinfo, 0xffff0000); + if (bc) { + int myprocid=bc->l3_id&0x0000ffff; + + hh->dinfo=(hh->dinfo&0xffff0000)|myprocid; + cb_log(3,stack->port,"Reject dinfo: %x cause:%d\n",hh->dinfo,bc->cause); + reject=1; } } break; - - case CC_FACILITY|INDICATION: - { - struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); - if (!bc) { - bc=find_bc_by_masked_l3id(stack, hh->dinfo, 0xffff0000); - if (bc) { - int myprocid=bc->l3_id&0x0000ffff; - hh->dinfo=(hh->dinfo&0xffff0000)|myprocid; - cb_log(4,bc->port,"Repaired reject Bug, new dinfo: %x\n",hh->dinfo); - } + + case CC_FACILITY|INDICATION: + bc = find_bc_by_l3id(stack, hh->dinfo); + if (!bc) { + bc=find_bc_by_masked_l3id(stack, hh->dinfo, 0xffff0000); + if (bc) { + int myprocid=bc->l3_id&0x0000ffff; + + hh->dinfo=(hh->dinfo&0xffff0000)|myprocid; + cb_log(4,bc->port,"Repaired reject Bug, new dinfo: %x\n",hh->dinfo); } } break; - - case CC_RELEASE_COMPLETE|INDICATION: - break; - case CC_SUSPEND|INDICATION: - { - msg_t *dmsg; - cb_log(4, stack->port, " --> Got Suspend, sending Reject for now\n"); - dmsg = create_l3msg(CC_SUSPEND_REJECT | REQUEST,MT_SUSPEND_REJECT, hh->dinfo,sizeof(RELEASE_COMPLETE_t), 1); - stack->nst.manager_l3(&stack->nst, dmsg); - free_msg(msg); - return 0; + case CC_RELEASE_COMPLETE|INDICATION: + break; + + case CC_SUSPEND|INDICATION: + cb_log(4, stack->port, " --> Got Suspend, sending Reject for now\n"); + dmsg = create_l3msg(CC_SUSPEND_REJECT | REQUEST,MT_SUSPEND_REJECT, hh->dinfo,sizeof(RELEASE_COMPLETE_t), 1); + stack->nst.manager_l3(&stack->nst, dmsg); + free_msg(msg); + return 0; + + case CC_RESUME|INDICATION: + break; + + case CC_RELEASE|CONFIRM: + bc = find_bc_by_l3id(stack, hh->dinfo); + if (bc) { + cb_log(1, stack->port, "CC_RELEASE|CONFIRM (l3id:%x), sending RELEASE_COMPLETE\n", hh->dinfo); + misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE); } break; - case CC_RESUME|INDICATION: - break; - case CC_RELEASE|CONFIRM: - { - struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); + case CC_RELEASE|INDICATION: + break; - if (bc) { - cb_log(1, stack->port, "CC_RELEASE|CONFIRM (l3id:%x), sending RELEASE_COMPLETE\n", hh->dinfo); - misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE); - } - } - break; - - case CC_RELEASE|INDICATION: - break; + case CC_RELEASE_CR|INDICATION: + release_cr(stack, hh); + free_msg(msg); + return 0; - case CC_RELEASE_CR|INDICATION: - release_cr(stack, hh); - free_msg(msg); - return 0 ; - break; - - case CC_NEW_CR|INDICATION: - /* Got New CR for bchan, for now I handle this one in */ - /* connect_ack, Need to be changed */ - { - struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); - int l3id = *((int *)(((u_char *)msg->data)+ mISDNUSER_HEAD_SIZE)); - if (!bc) { cb_log(0, stack->port, " --> In NEW_CR: didn't found bc ??\n"); return -1;}; - if (((l3id&0xff00)!=0xff00) && ((bc->l3_id&0xff00)==0xff00)) { - cb_log(4, stack->port, " --> Removing Process Id:%x on this port.\n", 0xff&bc->l3_id); - stack->procids[bc->l3_id&0xff] = 0 ; - } - cb_log(4, stack->port, "lib: Event_ind:CC_NEW_CR : very new L3ID is %x\n",l3id ); - - bc->l3_id =l3id; + case CC_NEW_CR|INDICATION: + /* Got New CR for bchan, for now I handle this one in */ + /* connect_ack, Need to be changed */ + l3id = *((int *) (msg->data + mISDNUSER_HEAD_SIZE)); + + bc = find_bc_by_l3id(stack, hh->dinfo); + if (!bc) { + cb_log(0, stack->port, " --> In NEW_CR: didn't found bc ??\n"); + return -1; + } + if (((l3id&0xff00)!=0xff00) && ((bc->l3_id&0xff00)==0xff00)) { + cb_log(4, stack->port, " --> Removing Process Id:%x on this port.\n", 0xff&bc->l3_id); + stack->procids[bc->l3_id&0xff] = 0 ; + } + cb_log(4, stack->port, "lib: Event_ind:CC_NEW_CR : very new L3ID is %x\n",l3id ); + + bc->l3_id =l3id; + if (!bc->is_register_pool) { cb_event(EVENT_NEW_L3ID, bc, glob_mgr->user_data); - - free_msg(msg); - return 0; } - - case DL_ESTABLISH | INDICATION: - case DL_ESTABLISH | CONFIRM: - { - cb_log(3, stack->port, "%% GOT L2 Activate Info.\n"); - - if (stack->ptp && stack->l2link) { - cb_log(0, stack->port, "%% GOT L2 Activate Info. but we're activated already.. this l2 is faulty, blocking port\n"); - cb_event(EVENT_PORT_ALARM, &stack->bc[0], glob_mgr->user_data); - } - if (stack->ptp && !stack->restart_sent) { - /* make sure we restart the interface of the - * other side */ - stack->restart_sent=1; - misdn_lib_send_restart(stack->port, -1); + free_msg(msg); + return 0; - } - - /* when we get the L2 UP, the L1 is UP definitely too*/ - stack->l2link = 1; - stack->l2upcnt=0; - - free_msg(msg); - return 0; + case DL_ESTABLISH | INDICATION: + case DL_ESTABLISH | CONFIRM: + cb_log(3, stack->port, "%% GOT L2 Activate Info.\n"); + + if (stack->ptp && stack->l2link) { + cb_log(0, stack->port, "%% GOT L2 Activate Info. but we're activated already.. this l2 is faulty, blocking port\n"); + cb_event(EVENT_PORT_ALARM, &stack->bc[0], glob_mgr->user_data); } - break; + if (stack->ptp && !stack->restart_sent) { + /* make sure we restart the interface of the + * other side */ + stack->restart_sent=1; + misdn_lib_send_restart(stack->port, -1); - case DL_RELEASE | INDICATION: - case DL_RELEASE | CONFIRM: - { - if (stack->ptp) { - cb_log(3 , stack->port, "%% GOT L2 DeActivate Info.\n"); + } - if (stack->l2upcnt>3) { - cb_log(0 , stack->port, "!!! Could not Get the L2 up after 3 Attempts!!!\n"); - } else { + /* when we get the L2 UP, the L1 is UP definitely too*/ + stack->l2link = 1; + stack->l2upcnt=0; + + free_msg(msg); + return 0; + + case DL_RELEASE | INDICATION: + case DL_RELEASE | CONFIRM: + if (stack->ptp) { + cb_log(3 , stack->port, "%% GOT L2 DeActivate Info.\n"); + + if (stack->l2upcnt>3) { + cb_log(0 , stack->port, "!!! Could not Get the L2 up after 3 Attempts!!!\n"); + } else { #if 0 - if (stack->nt) misdn_lib_reinit_nt_stack(stack->port); + if (stack->nt) + misdn_lib_reinit_nt_stack(stack->port); #endif - if (stack->l1link) { - misdn_lib_get_l2_up(stack); - stack->l2upcnt++; - } + if (stack->l1link) { + misdn_lib_get_l2_up(stack); + stack->l2upcnt++; } - - } else - cb_log(3, stack->port, "%% GOT L2 DeActivate Info.\n"); - - stack->l2link = 0; - free_msg(msg); - return 0; - } + } + + } else + cb_log(3, stack->port, "%% GOT L2 DeActivate Info.\n"); + + stack->l2link = 0; + free_msg(msg); + return 0; + + default: break; - } } - - { - /* Parse Events and fire_up to App. */ - struct misdn_bchannel *bc; - struct misdn_bchannel dummybc; - - enum event_e event = isdn_msg_get_event(msgs_g, msg, 1); - - bc=find_bc_by_l3id(stack, hh->dinfo); - - if (!bc) { - cb_log(4, stack->port, " --> Didn't find BC so temporarily creating dummy BC (l3id:%x).\n", hh->dinfo); - misdn_make_dummy(&dummybc, stack->port, hh->dinfo, stack->nt, 0); - bc=&dummybc; + + /* Parse Events and fire_up to App. */ + event = isdn_msg_get_event(msgs_g, msg, 1); + + bc = find_bc_by_l3id(stack, hh->dinfo); + if (!bc) { + cb_log(4, stack->port, " --> Didn't find BC so temporarily creating dummy BC (l3id:%x).\n", hh->dinfo); + misdn_make_dummy(&dummybc, stack->port, hh->dinfo, stack->nt, 0); + bc = &dummybc; + } + + isdn_msg_parse_event(msgs_g, msg, bc, 1); + + switch (event) { + case EVENT_SETUP: + if (bc->channel <= 0 || bc->channel == 0xff) { + bc->channel = 0; } - if (bc ) { - isdn_msg_parse_event(msgs_g,msg,bc, 1); - switch (event) { - case EVENT_SETUP: - if (bc->channel<=0 || bc->channel==0xff) - bc->channel=0; - - if (find_free_chan_in_stack(stack,bc, bc->channel,0)<0) - goto ERR_NO_CHANNEL; - break; - case EVENT_RELEASE: - case EVENT_RELEASE_COMPLETE: - { - int channel=bc->channel; - int tmpcause=bc->cause; - empty_bc(bc); - bc->cause=tmpcause; - clean_up_bc(bc); + if (find_free_chan_in_stack(stack, bc, bc->channel, 0) < 0) { + goto ERR_NO_CHANNEL; + } + break; + case EVENT_RELEASE: + case EVENT_RELEASE_COMPLETE: + channel = bc->channel; + tmpcause = bc->cause; - if (channel>0) - empty_chan_in_stack(stack,channel); - bc->in_use=0; - } - break; + empty_bc(bc); + bc->cause = tmpcause; + clean_up_bc(bc); - default: + if (channel > 0) + empty_chan_in_stack(stack, channel); + bc->in_use = 0; + break; + default: + break; + } + + if(!isdn_get_info(msgs_g, event, 1)) { + cb_log(4, stack->port, "Unknown Event Ind: prim %x dinfo %x\n", hh->prim, hh->dinfo); + } else { + if (reject) { + switch(bc->cause) { + case AST_CAUSE_USER_BUSY: + cb_log(1, stack->port, "Siemens Busy reject..\n"); break; + default: + break; } - - if(!isdn_get_info(msgs_g,event,1)) { - cb_log(4, stack->port, "Unknown Event Ind: prim %x dinfo %x\n",hh->prim, hh->dinfo); - } else { - if (reject) { - switch(bc->cause){ - case AST_CAUSE_USER_BUSY: - cb_log(1, stack->port, "Siemens Busy reject..\n"); - - break; - default: - break; - } - } - cb_event(event, bc, glob_mgr->user_data); - } - } else { - cb_log(4, stack->port, "No BC found with l3id: prim %x dinfo %x\n",hh->prim, hh->dinfo); } - - free_msg(msg); + cb_event(event, bc, glob_mgr->user_data); } + free_msg(msg); + return 0; +ERR_NO_CHANNEL: + cb_log(4, stack->port, "Patch from MEIDANIS:Sending RELEASE_COMPLETE %x (No free Chan for you..)\n", hh->dinfo); + dmsg = create_l3msg(CC_RELEASE_COMPLETE | REQUEST, MT_RELEASE_COMPLETE, hh->dinfo, sizeof(RELEASE_COMPLETE_t), 1); + stack->nst.manager_l3(&stack->nst, dmsg); + free_msg(msg); return 0; } @@ -2161,8 +2247,8 @@ static int handle_timers(msg_t* msg) { iframe_t *frm= (iframe_t*)msg->data; - struct misdn_stack *stack; - + struct misdn_stack *stack; + /* Timer Stuff */ switch (frm->prim) { case MGR_INITTIMER | CONFIRM: @@ -2172,17 +2258,17 @@ free_msg(msg); return(1); } - - - + + + if (frm->prim==(MGR_TIMER | INDICATION) ) { for (stack = glob_mgr->stack_list; stack; stack = stack->next) { itimer_t *it; - + if (!stack->nt) continue; - + it = stack->nst.tlist; /* find timer */ for(it=stack->nst.tlist; @@ -2201,12 +2287,12 @@ return 1; } } - + cb_log(0, 0, "Timer Msg without Timer ??\n"); free_msg(msg); return 1; } - + return 0; } @@ -2226,23 +2312,23 @@ static int do_tone(struct misdn_bchannel *bc, int len) { bc->tone_cnt=len; - + if (bc->generate_tone) { cb_event(EVENT_TONE_GENERATE, bc, glob_mgr->user_data); - + if ( !bc->nojitter ) { misdn_tx_jitter(bc,len); } - + return 1; } - + return 0; } #ifdef MISDN_SAVE_DATA -static void misdn_save_data(int id, char *p1, int l1, char *p2, int l2) +static void misdn_save_data(int id, char *p1, int l1, char *p2, int l2) { char n1[32],n2[32]; FILE *rx, *tx; @@ -2250,17 +2336,21 @@ sprintf(n1,"/tmp/misdn-rx-%d.raw",id); sprintf(n2,"/tmp/misdn-tx-%d.raw",id); - rx = fopen(n1,"a+"); + rx = fopen(n1,"a+"); tx = fopen(n2,"a+"); if (!rx || !tx) { cb_log(0,0,"Couldn't open files: %s\n",strerror(errno)); + if (rx) + fclose(rx); + if (tx) + fclose(tx); return ; } - + fwrite(p1,1,l1,rx); fwrite(p2,1,l2,tx); - + fclose(rx); fclose(tx); @@ -2273,25 +2363,25 @@ char *data=&buf[mISDN_HEADER_LEN]; iframe_t *txfrm= (iframe_t*)buf; int jlen, r; - + jlen=cb_jb_empty(bc,data,len); - + if (jlen) { #ifdef MISDN_SAVE_DATA misdn_save_data((bc->port*100+bc->channel), data, jlen, bc->bframe, bc->bframe_len); #endif flip_buf_bits( data, jlen); - + if (jlen < len) { cb_log(1, bc->port, "Jitterbuffer Underrun. Got %d of expected %d\n", jlen, len); } - + txfrm->prim = DL_DATA|REQUEST; - + txfrm->dinfo = 0; - + txfrm->addr = bc->addr|FLG_MSG_DOWN; /* | IF_DOWN; */ - + txfrm->len =jlen; cb_log(9, bc->port, "Transmitting %d samples 2 misdn\n", txfrm->len); @@ -2337,25 +2427,25 @@ iframe_t *frm= (iframe_t*)msg->data; struct misdn_bchannel *bc=find_bc_by_addr(frm->addr); struct misdn_stack *stack; - + if (!bc) { cb_log(1,0,"handle_bchan: BC not found for prim:%x with addr:%x dinfo:%x\n", frm->prim, frm->addr, frm->dinfo); return 0 ; } - + stack = get_stack_by_bc(bc); - + if (!stack) { cb_log(0, bc->port,"handle_bchan: STACK not found for prim:%x with addr:%x dinfo:%x\n", frm->prim, frm->addr, frm->dinfo); return 0; } - + switch (frm->prim) { case MGR_SETSTACK| CONFIRM: cb_log(3, stack->port, "BCHAN: MGR_SETSTACK|CONFIRM pid:%d\n",bc->pid); break; - + case MGR_SETSTACK| INDICATION: cb_log(3, stack->port, "BCHAN: MGR_SETSTACK|IND pid:%d\n",bc->pid); break; @@ -2368,9 +2458,9 @@ usleep(1000); goto AGAIN; } - + cb_log(0,stack->port,"$$$ Get Layer (%d) Id Error: %s\n",bc->layer,strerror(errno)); - + /* we kill the channel later, when we received some data. */ bc->addr= frm->addr; @@ -2378,12 +2468,12 @@ cb_log(0, stack->port,"$$$ bc->addr <0 Error:%s\n",strerror(errno)); bc->addr=0; } - + cb_log(4, stack->port," --> Got Adr %x\n", bc->addr); free_msg(msg); - - + + switch(bc->bc_state) { case BCHAN_SETUP: bc_state_change(bc,BCHAN_SETUPED); @@ -2400,31 +2490,31 @@ case MGR_DELLAYER| INDICATION: cb_log(3, stack->port, "BCHAN: MGR_DELLAYER|IND pid:%d\n",bc->pid); break; - + case MGR_DELLAYER| CONFIRM: cb_log(3, stack->port, "BCHAN: MGR_DELLAYER|CNF pid:%d\n",bc->pid); - + bc->pid=0; bc->addr=0; - + free_msg(msg); return 1; - + case PH_ACTIVATE | INDICATION: case DL_ESTABLISH | INDICATION: cb_log(3, stack->port, "BCHAN: ACT Ind pid:%d\n", bc->pid); free_msg(msg); - return 1; + return 1; case PH_ACTIVATE | CONFIRM: case DL_ESTABLISH | CONFIRM: - + cb_log(3, stack->port, "BCHAN: bchan ACT Confirm pid:%d\n",bc->pid); free_msg(msg); - - return 1; + return 1; + case DL_ESTABLISH | REQUEST: { char buf[128]; @@ -2440,33 +2530,40 @@ } free_msg(msg); return 1; - + case PH_DEACTIVATE | INDICATION: case DL_RELEASE | INDICATION: cb_log (3, stack->port, "BCHAN: DeACT Ind pid:%d\n",bc->pid); - + free_msg(msg); return 1; - + case PH_DEACTIVATE | CONFIRM: case DL_RELEASE | CONFIRM: cb_log(3, stack->port, "BCHAN: DeACT Conf pid:%d\n",bc->pid); - + free_msg(msg); return 1; - + case PH_CONTROL|INDICATION: { unsigned int *cont = (unsigned int *) &frm->data.p; - - cb_log(4, stack->port, "PH_CONTROL: channel:%d oad%d:%s dad%d:%s \n", bc->channel, bc->onumplan,bc->oad, bc->dnumplan,bc->dad); + cb_log(4, stack->port, + "PH_CONTROL: channel:%d caller%d:\"%s\" <%s> dialed%d:%s \n", + bc->channel, + bc->caller.number_type, + bc->caller.name, + bc->caller.number, + bc->dialed.number_type, + bc->dialed.number); + if ((*cont & ~DTMF_TONE_MASK) == DTMF_TONE_VAL) { int dtmf = *cont & DTMF_TONE_MASK; cb_log(4, stack->port, " --> DTMF TONE: %c\n",dtmf); bc->dtmf=dtmf; cb_event(EVENT_DTMF_TONE, bc, glob_mgr->user_data); - + free_msg(msg); return 1; } @@ -2487,11 +2584,11 @@ case DL_DATA|REQUEST: cb_log(0, stack->port, "DL_DATA REQUEST \n"); do_tone(bc, 64); - + free_msg(msg); return 1; - - + + case PH_DATA|INDICATION: case DL_DATA|INDICATION: { @@ -2499,10 +2596,10 @@ bc->bframe_len = frm->len; /** Anyway flip the bufbits **/ - if ( misdn_cap_is_speech(bc->capability) ) + if ( misdn_cap_is_speech(bc->capability) ) flip_buf_bits(bc->bframe, bc->bframe_len); - + if (!bc->bframe_len) { cb_log(2, stack->port, "DL_DATA INDICATION bc->addr:%x frm->addr:%x\n", bc->addr, frm->addr); free_msg(msg); @@ -2514,12 +2611,12 @@ free_msg(msg); return 1; } - + #if MISDN_DEBUG cb_log(0, stack->port, "DL_DATA INDICATION Len %d\n", frm->len); #endif - + if ( (bc->bc_state == BCHAN_ACTIVATED) && frm->len > 0) { int t; @@ -2533,7 +2630,7 @@ #endif if ( !t ) { int i; - + if ( misdn_cap_is_speech(bc->capability)) { if ( !bc->nojitter ) { #ifdef MISDN_B_DEBUG @@ -2546,15 +2643,15 @@ } } -#ifdef MISDN_B_DEBUG +#ifdef MISDN_B_DEBUG cb_log(0,bc->port,"EVENT_B_DATA START\n"); #endif - + i = cb_event(EVENT_BCHAN_DATA, bc, glob_mgr->user_data); -#ifdef MISDN_B_DEBUG +#ifdef MISDN_B_DEBUG cb_log(0,bc->port,"EVENT_B_DATA STOP\n"); #endif - + if (i<0) { cb_log(10,stack->port,"cb_event returned <0\n"); /*clean_up_bc(bc);*/ @@ -2587,7 +2684,7 @@ #endif break; } - + return 0; } @@ -2601,187 +2698,173 @@ stack=find_stack_by_addr( frm->addr ); - - + + if (!stack || !stack->nt) { return 0; } - + if ((err=stack->nst.l1_l2(&stack->nst,msg))) { - + if (nt_err_cnt > 0 ) { if (nt_err_cnt < 100) { - nt_err_cnt++; + nt_err_cnt++; cb_log(0, stack->port, "NT Stack sends us error: %d \n", err); } else if (nt_err_cnt < 105){ cb_log(0, stack->port, "NT Stack sends us error: %d over 100 times, so I'll stop this message\n", err); - nt_err_cnt = - 1; + nt_err_cnt = - 1; } } free_msg(msg); return 1; - + } - + return 1; } static int handle_frm(msg_t *msg) { - iframe_t *frm = (iframe_t*) msg->data; - - struct misdn_stack *stack=find_stack_by_addr(frm->addr); + struct misdn_bchannel dummybc; + struct misdn_bchannel *bc; + iframe_t *frm; + struct misdn_stack *stack; + enum event_e event; + enum event_response_e response; + int ret; + int channel; + int tmpcause; + int tmp_out_cause; + frm = (iframe_t*) msg->data; + stack = find_stack_by_addr(frm->addr); if (!stack || stack->nt) { return 0; } - - cb_log(4,stack?stack->port:0,"handle_frm: frm->addr:%x frm->prim:%x\n",frm->addr,frm->prim); - { - struct misdn_bchannel dummybc; - struct misdn_bchannel *bc; - int ret=handle_cr(stack, frm); + cb_log(4, stack ? stack->port : 0, "handle_frm: frm->addr:%x frm->prim:%x\n", frm->addr, frm->prim); - if (ret<0) { - cb_log(3,stack?stack->port:0,"handle_frm: handle_cr <0 prim:%x addr:%x\n", frm->prim, frm->addr); + ret = handle_cr(stack, frm); + if (ret < 0) { + cb_log(3, stack ? stack->port : 0, "handle_frm: handle_cr <0 prim:%x addr:%x\n", frm->prim, frm->addr); + } + if (ret) { + free_msg(msg); + return 1; + } + bc = find_bc_by_l3id(stack, frm->dinfo); + if (!bc) { + misdn_make_dummy(&dummybc, stack->port, 0, stack->nt, 0); + switch (frm->prim) { + case CC_RESTART | CONFIRM: + dummybc.l3_id = MISDN_ID_GLOBAL; + bc = &dummybc; + break; + case CC_SETUP | INDICATION: + dummybc.l3_id = frm->dinfo; + bc = &dummybc; - } + misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE); - if(ret) { free_msg(msg); return 1; + default: + if (frm->prim == (CC_FACILITY | INDICATION)) { + cb_log(5, stack->port, " --> Using Dummy BC for FACILITY\n"); + } else { + cb_log(0, stack->port, " --> Didn't find BC so temporarily creating dummy BC (l3id:%x) on this port.\n", frm->dinfo); + dummybc.l3_id = frm->dinfo; + } + bc = &dummybc; + break; } - - bc=find_bc_by_l3id(stack, frm->dinfo); + } - if (!bc && (frm->prim==(CC_RESTART|CONFIRM)) ) { - misdn_make_dummy(&dummybc, stack->port, MISDN_ID_GLOBAL, stack->nt, 0); - bc=&dummybc; - } + event = isdn_msg_get_event(msgs_g, msg, 0); + isdn_msg_parse_event(msgs_g, msg, bc, 0); - if (!bc && (frm->prim==(CC_SETUP|INDICATION)) ) { - misdn_make_dummy(&dummybc, stack->port, MISDN_ID_GLOBAL, stack->nt, 0); - dummybc.port=stack->port; - dummybc.l3_id=frm->dinfo; - bc=&dummybc; + /* Preprocess some Events */ + ret = handle_event(bc, event, frm); + if (ret < 0) { + cb_log(0, stack->port, "couldn't handle event\n"); + free_msg(msg); + return 1; + } - misdn_lib_send_event(bc,EVENT_RELEASE_COMPLETE); + /* shoot up event to App: */ + cb_log(5, stack->port, "lib Got Prim: Addr %x prim %x dinfo %x\n", frm->addr, frm->prim, frm->dinfo); - free_msg(msg); - return 1; - } + if (!isdn_get_info(msgs_g, event, 0)) { + cb_log(0, stack->port, "Unknown Event Ind: Addr:%x prim %x dinfo %x\n", frm->addr, frm->prim, frm->dinfo); + response = RESPONSE_OK; + } else { + response = cb_event(event, bc, glob_mgr->user_data); + } - -handle_frm_bc: - if (bc ) { - enum event_e event = isdn_msg_get_event(msgs_g, msg, 0); - enum event_response_e response=RESPONSE_OK; - int ret; - - isdn_msg_parse_event(msgs_g,msg,bc, 0); - - /** Preprocess some Events **/ - ret = handle_event(bc, event, frm); - if (ret<0) { - cb_log(0,stack->port,"couldn't handle event\n"); - free_msg(msg); - return 1; + switch (event) { + case EVENT_SETUP: + switch (response) { + case RESPONSE_IGNORE_SETUP_WITHOUT_CLOSE: + cb_log(0, stack->port, "TOTALLY IGNORING SETUP\n"); + break; + case RESPONSE_IGNORE_SETUP: + /* I think we should send CC_RELEASE_CR, but am not sure*/ + bc->out_cause = AST_CAUSE_NORMAL_CLEARING; + /* fall through */ + case RESPONSE_RELEASE_SETUP: + misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE); + if (bc->channel > 0) { + empty_chan_in_stack(stack, bc->channel); } - /* shoot up event to App: */ - cb_log(5, stack->port, "lib Got Prim: Addr %x prim %x dinfo %x\n",frm->addr, frm->prim, frm->dinfo); - - if(!isdn_get_info(msgs_g,event,0)) - cb_log(0, stack->port, "Unknown Event Ind: Addr:%x prim %x dinfo %x\n",frm->addr, frm->prim, frm->dinfo); - else - response=cb_event(event, bc, glob_mgr->user_data); -#if 1 - if (event == EVENT_SETUP) { - switch (response) { - case RESPONSE_IGNORE_SETUP_WITHOUT_CLOSE: + empty_bc(bc); + bc_state_change(bc, BCHAN_CLEANED); + bc->in_use = 0; - cb_log(0, stack->port, "TOTALLY IGNORING SETUP\n"); - - break; - case RESPONSE_IGNORE_SETUP: - /* I think we should send CC_RELEASE_CR, but am not sure*/ - bc->out_cause = AST_CAUSE_NORMAL_CLEARING; - - case RESPONSE_RELEASE_SETUP: - misdn_lib_send_event(bc,EVENT_RELEASE_COMPLETE); - if (bc->channel>0) - empty_chan_in_stack(stack, bc->channel); - empty_bc(bc); - bc_state_change(bc,BCHAN_CLEANED); - bc->in_use=0; + cb_log(0, stack->port, "GOT IGNORE SETUP\n"); + break; + case RESPONSE_OK: + cb_log(4, stack->port, "GOT SETUP OK\n"); + break; + default: + break; + } + break; + case EVENT_RELEASE_COMPLETE: + /* release bchannel only after we've announced the RELEASE_COMPLETE */ + channel = bc->channel; + tmpcause = bc->cause; + tmp_out_cause = bc->out_cause; - cb_log(0, stack->port, "GOT IGNORE SETUP\n"); - break; - case RESPONSE_OK: - cb_log(4, stack->port, "GOT SETUP OK\n"); + empty_bc(bc); + bc->cause = tmpcause; + bc->out_cause = tmp_out_cause; + clean_up_bc(bc); - - break; - default: - break; - } - } - - if (event == EVENT_RELEASE_COMPLETE) { - /* release bchannel only after we've announced the RELEASE_COMPLETE */ - int channel=bc->channel; - int tmpcause=bc->cause; - int tmp_out_cause=bc->out_cause; - empty_bc(bc); - bc->cause=tmpcause; - bc->out_cause=tmp_out_cause; - clean_up_bc(bc); - - if (tmpcause == AST_CAUSE_REQUESTED_CHAN_UNAVAIL) { - cb_log(0,stack->port,"**** Received CAUSE:%d, so not cleaning up channel %d\n", AST_CAUSE_REQUESTED_CHAN_UNAVAIL, channel); - cb_log(0,stack->port,"**** This channel is now no longer available,\nplease try to restart it with 'misdn send restart '\n"); - set_chan_in_stack(stack, channel); - bc->channel=channel; - misdn_lib_send_restart(stack->port, channel); - } else { - if (channel>0) - empty_chan_in_stack(stack, channel); - } - bc->in_use=0; - } - - if (event == EVENT_RESTART) { - cb_log(0, stack->port, "**** Received RESTART_ACK channel:%d\n", bc->restart_channel); - empty_chan_in_stack(stack, bc->restart_channel); - } - - cb_log(5, stack->port, "Freeing Msg on prim:%x \n",frm->prim); - - - free_msg(msg); - return 1; -#endif - - } else { - struct misdn_bchannel dummybc; - if (frm->prim!=(CC_FACILITY|INDICATION)) - cb_log(0, stack->port, " --> Didn't find BC so temporarily creating dummy BC (l3id:%x) on this port.\n", frm->dinfo); - else - cb_log(5, stack->port, " --> Using Dummy BC for FACILITY\n"); - - memset (&dummybc,0,sizeof(dummybc)); - dummybc.port=stack->port; - dummybc.l3_id=frm->dinfo; - bc=&dummybc; - goto handle_frm_bc; + if (tmpcause == AST_CAUSE_REQUESTED_CHAN_UNAVAIL) { + cb_log(0, stack->port, "**** Received CAUSE:%d, so not cleaning up channel %d\n", AST_CAUSE_REQUESTED_CHAN_UNAVAIL, channel); + cb_log(0, stack->port, "**** This channel is now no longer available,\nplease try to restart it with 'misdn send restart '\n"); + set_chan_in_stack(stack, channel); + bc->channel = channel; + misdn_lib_send_restart(stack->port, channel); + } else if (channel > 0) { + empty_chan_in_stack(stack, channel); } + bc->in_use = 0; + break; + case EVENT_RESTART: + cb_log(0, stack->port, "**** Received RESTART_ACK channel:%d\n", bc->restart_channel); + empty_chan_in_stack(stack, bc->restart_channel); + break; + default: + break; } - cb_log(4, stack->port, "TE_FRM_HANDLER: Returning 0 on prim:%x \n",frm->prim); - return 0; + cb_log(5, stack->port, "Freeing Msg on prim:%x \n", frm->prim); + free_msg(msg); + return 1; } @@ -2790,7 +2873,7 @@ iframe_t *frm = (iframe_t*) msg->data; struct misdn_stack *stack = find_stack_by_addr(frm->addr); int i ; - + if (!stack) return 0 ; switch (frm->prim) { @@ -2798,9 +2881,9 @@ case PH_ACTIVATE | INDICATION: cb_log (3, stack->port, "L1: PH L1Link Up!\n"); stack->l1link=1; - + if (stack->nt) { - + if (stack->nst.l1_l2(&stack->nst, msg)) free_msg(msg); @@ -2809,14 +2892,14 @@ } else { free_msg(msg); } - + for (i=0;i<=stack->b_num; i++) { if (stack->bc[i].evq != EVENT_NOTHING) { cb_log(4, stack->port, "Firing Queued Event %s because L1 got up\n", isdn_get_info(msgs_g, stack->bc[i].evq, 0)); misdn_lib_send_event(&stack->bc[i],stack->bc[i].evq); stack->bc[i].evq=EVENT_NOTHING; } - + } return 1; @@ -2824,16 +2907,16 @@ free_msg(msg); cb_log(3,stack->port,"L1: PH_ACTIVATE|REQUEST \n"); return 1; - + case PH_DEACTIVATE | REQUEST: free_msg(msg); cb_log(3,stack->port,"L1: PH_DEACTIVATE|REQUEST \n"); return 1; - + case PH_DEACTIVATE | CONFIRM: case PH_DEACTIVATE | INDICATION: cb_log (3, stack->port, "L1: PH L1Link Down! \n"); - + #if 0 for (i=0; i<=stack->b_num; i++) { if (global_state == MISDN_INITIALIZED) { @@ -2841,19 +2924,19 @@ } } #endif - + if (stack->nt) { if (stack->nst.l1_l2(&stack->nst, msg)) free_msg(msg); } else { free_msg(msg); } - + stack->l1link=0; stack->l2link=0; return 1; } - + return 0; } @@ -2862,11 +2945,11 @@ iframe_t *frm = (iframe_t*) msg->data; struct misdn_stack *stack = find_stack_by_addr(frm->addr); - + if (!stack) { return 0 ; } - + switch(frm->prim) { case DL_ESTABLISH | REQUEST: @@ -2875,13 +2958,13 @@ case DL_RELEASE | REQUEST: cb_log(1,stack->port,"DL_RELEASE|REQUEST \n"); return 1; - + case DL_ESTABLISH | INDICATION: case DL_ESTABLISH | CONFIRM: { cb_log (3, stack->port, "L2: L2Link Up! \n"); if (stack->ptp && stack->l2link) { - cb_log (-1, stack->port, "L2: L2Link Up! but it's already UP.. must be faulty, blocking port\n"); + cb_log (-1, stack->port, "L2: L2Link Up! but it's already UP.. must be faulty, blocking port\n"); cb_event(EVENT_PORT_ALARM, &stack->bc[0], glob_mgr->user_data); } stack->l2link=1; @@ -2889,13 +2972,13 @@ return 1; } break; - + case DL_RELEASE | INDICATION: case DL_RELEASE | CONFIRM: { cb_log (3, stack->port, "L2: L2Link Down! \n"); stack->l2link=0; - + free_msg(msg); return 1; } @@ -2914,9 +2997,9 @@ free_msg(msg); return 1; } - + stack = find_stack_by_addr(frm->addr); - + if (!stack) { if (frm->prim == (MGR_DELLAYER|CONFIRM)) { cb_log(2, 0, "MGMT: DELLAYER|CONFIRM Addr: %x !\n", @@ -2924,20 +3007,20 @@ free_msg(msg); return 1; } - + return 0; } - + switch(frm->prim) { case MGR_SHORTSTATUS | INDICATION: case MGR_SHORTSTATUS | CONFIRM: cb_log(5, 0, "MGMT: Short status dinfo %x\n",frm->dinfo); - + switch (frm->dinfo) { case SSTATUS_L1_ACTIVATED: cb_log(3, 0, "MGMT: SSTATUS: L1_ACTIVATED \n"); stack->l1link=1; - + break; case SSTATUS_L1_DEACTIVATED: cb_log(3, 0, "MGMT: SSTATUS: L1_DEACTIVATED \n"); @@ -2951,16 +3034,16 @@ cb_log(3, stack->port, "MGMT: SSTATUS: L2_ESTABLISH \n"); stack->l2link=1; break; - + case SSTATUS_L2_RELEASED: cb_log(3, stack->port, "MGMT: SSTATUS: L2_RELEASED \n"); stack->l2link=0; break; } - + free_msg(msg); return 1; - + case MGR_SETSTACK | INDICATION: cb_log(4, stack->port, "MGMT: SETSTACK|IND dinfo %x\n",frm->dinfo); free_msg(msg); @@ -2969,21 +3052,21 @@ cb_log(4, stack->port, "MGMT: DELLAYER|CNF dinfo %x\n",frm->dinfo) ; free_msg(msg); return 1; - + } - + /* if ( (frm->prim & 0x0f0000) == 0x0f0000) { cb_log(5, 0, "$$$ MGMT FRAME: prim %x addr %x dinfo %x\n",frm->prim, frm->addr, frm->dinfo) ; free_msg(msg); return 1; } */ - + return 0; } -static msg_t *fetch_msg(int midev) +static msg_t *fetch_msg(int midev) { msg_t *msg=alloc_msg(MAX_MSG_SIZE); int r; @@ -2996,7 +3079,7 @@ AGAIN: r=mISDN_read(midev,msg->data,MAX_MSG_SIZE, TIMEOUT_10SEC); msg->len=r; - + if (r==0) { free_msg(msg); /* danger, cause usually freeing in main_loop */ cb_log(6,0,"Got empty Msg..\n"); @@ -3010,8 +3093,8 @@ usleep(5000); goto AGAIN; } - - cb_log(0,0,"mISDN_read returned :%d error:%s (%d)\n",r,strerror(errno),errno); + + cb_log(0,0,"mISDN_read returned :%d error:%s (%d)\n",r,strerror(errno),errno); } #if 0 @@ -3029,12 +3112,12 @@ ; if (stack) { - cb_log(4, port, "Checking L1 State\n"); + cb_log(4, port, "Checking L1 State\n"); if (!stack->l1link) { - cb_log(4, port, "L1 State Down, trying to get it up again\n"); + cb_log(4, port, "L1 State Down, trying to get it up again\n"); misdn_lib_get_short_status(stack); - misdn_lib_get_l1_up(stack); - misdn_lib_get_l2_up(stack); + misdn_lib_get_l1_up(stack); + misdn_lib_get_l2_up(stack); } } } @@ -3046,19 +3129,19 @@ int zero_frm=0 , fff_frm=0 ; int midev= mgr->midev; int port=0; - + while (1) { - msg_t *msg = fetch_msg(midev); + msg_t *msg = fetch_msg(midev); iframe_t *frm; - - + + if (!msg) continue; - + frm = (iframe_t*) msg->data; - + /** When we make a call from NT2Ast we get these frames **/ if (frm->len == 0 && frm->addr == 0 && frm->dinfo == 0 && frm->prim == 0 ) { - zero_frm++; + zero_frm++; free_msg(msg); continue; } else { @@ -3067,10 +3150,10 @@ zero_frm = 0 ; } } - + /** I get this sometimes after setup_bc **/ if (frm->len == 0 && frm->dinfo == 0 && frm->prim == 0xffffffff ) { - fff_frm++; + fff_frm++; free_msg(msg); continue; } else { @@ -3079,7 +3162,7 @@ fff_frm = 0 ; } } - + manager_isdn_handler(frm, msg); } @@ -3088,31 +3171,29 @@ /** App Interface **/ -int te_lib_init(void) { +static int te_lib_init(void) +{ char buff[1025] = ""; - iframe_t *frm=(iframe_t*)buff; - int midev=mISDN_open(); + iframe_t *frm = (iframe_t *) buff; + int midev; int ret; - if (midev<=0) return midev; - -/* create entity for layer 3 TE-mode */ + midev = mISDN_open(); + if (midev <= 0) { + return midev; + } + + /* create entity for layer 3 TE-mode */ mISDN_write_frame(midev, buff, 0, MGR_NEWENTITY | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); + ret = mISDN_read_frame(midev, frm, sizeof(iframe_t), 0, MGR_NEWENTITY | CONFIRM, TIMEOUT_1SEC); - - if (ret < mISDN_HEADER_LEN) { - noentity: - fprintf(stderr, "cannot request MGR_NEWENTITY from mISDN: %s\n",strerror(errno)); + entity = frm->dinfo & 0xffff; + if (ret < mISDN_HEADER_LEN || !entity) { + fprintf(stderr, "cannot request MGR_NEWENTITY from mISDN: %s\n", strerror(errno)); exit(-1); } - - entity = frm->dinfo & 0xffff ; - - if (!entity) - goto noentity; return midev; - } void te_lib_destroy(int midev) @@ -3136,14 +3217,14 @@ { struct misdn_stack *stack; int i; - + for (stack=glob_mgr->stack_list; stack; stack=stack->next) { for (i=0; i<=stack->b_num; i++) if (stack->bc[i].pid == pid) return &stack->bc[i]; } - + return NULL; } @@ -3158,18 +3239,20 @@ static int test_inuse(struct misdn_bchannel *bc) { struct timeval now; - gettimeofday(&now, NULL); + if (!bc->in_use) { - if (misdn_lib_port_is_pri(bc->port) && bc->last_used.tv_sec == now.tv_sec ) { - cb_log(2,bc->port, "channel with stid:%x for one second still in use! (n:%d lu:%d)\n", bc->b_stid, (int) now.tv_sec, (int) bc->last_used.tv_sec); + gettimeofday(&now, NULL); + if (bc->last_used.tv_sec == now.tv_sec + && misdn_lib_port_is_pri(bc->port)) { + cb_log(2, bc->port, "channel with stid:%x for one second still in use! (n:%d lu:%d)\n", + bc->b_stid, (int) now.tv_sec, (int) bc->last_used.tv_sec); return 1; } - cb_log(3,bc->port, "channel with stid:%x not in use!\n", bc->b_stid); return 0; } - + cb_log(2,bc->port, "channel with stid:%x in use!\n", bc->b_stid); return 1; } @@ -3195,88 +3278,125 @@ #endif } -struct misdn_bchannel* misdn_lib_get_free_bc(int port, int channel, int inout, int dec) +struct misdn_bchannel *misdn_lib_get_free_bc(int port, int channel, int inout, int dec) { struct misdn_stack *stack; int i; - + int maxnum; + if (channel < 0 || channel > MAX_BCHANS) { - cb_log(0,port,"Requested channel out of bounds (%d)\n",channel); + cb_log(0, port, "Requested channel out of bounds (%d)\n", channel); return NULL; } usleep(1000); - for (stack=glob_mgr->stack_list; stack; stack=stack->next) { - - if (stack->port == port) { - int maxnum; + /* Find the port stack structure */ + stack = find_stack_by_port(port); + if (!stack) { + cb_log(0, port, "Port is not configured (%d)\n", port); + return NULL; + } - if (stack->blocked) { - cb_log(0,port,"Port is blocked\n"); - return NULL; - } - - if (channel > 0) { - if (channel <= stack->b_num) { - for (i = 0; i < stack->b_num; i++) { - if ( stack->bc[i].channel == channel) { - if (test_inuse(&stack->bc[i])) { - cb_log(0,port,"Requested channel:%d on port:%d is already in use\n",channel, port); - return NULL; + if (stack->blocked) { + cb_log(0, port, "Port is blocked\n"); + return NULL; + } - } else { - prepare_bc(&stack->bc[i], channel); - return &stack->bc[i]; - } - } + if (channel > 0) { + if (channel <= stack->b_num) { + for (i = 0; i < stack->b_num; i++) { + if (stack->bc[i].channel == channel) { + if (test_inuse(&stack->bc[i])) { + cb_log(0, port, "Requested channel:%d on port:%d is already in use\n", channel, port); + return NULL; + } else { + prepare_bc(&stack->bc[i], channel); + return &stack->bc[i]; } - } else { - cb_log(0,port,"Requested channel:%d is out of bounds on port:%d\n",channel, port); - return NULL; } } + } else { + cb_log(0, port, "Requested channel:%d is out of bounds on port:%d\n", channel, port); + return NULL; + } + } - maxnum = inout && !stack->pri && !stack->ptp ? stack->b_num + 1 : stack->b_num; + /* Note: channel == 0 here */ + maxnum = (inout && !stack->pri && !stack->ptp) ? stack->b_num + 1 : stack->b_num; + if (dec) { + for (i = maxnum - 1; i >= 0; --i) { + if (!test_inuse(&stack->bc[i])) { + /* 3. channel on bri means CW*/ + if (!stack->pri && i == stack->b_num) { + stack->bc[i].cw = 1; + } - if (dec) { - for (i = maxnum-1; i>=0; i--) { - if (!test_inuse(&stack->bc[i])) { - /* 3. channel on bri means CW*/ - if (!stack->pri && i==stack->b_num) - stack->bc[i].cw=1; - - prepare_bc(&stack->bc[i], channel); - stack->bc[i].dec=1; - return &stack->bc[i]; - } + prepare_bc(&stack->bc[i], channel); + stack->bc[i].dec = 1; + return &stack->bc[i]; + } + } + } else { + for (i = 0; i < maxnum; ++i) { + if (!test_inuse(&stack->bc[i])) { + /* 3. channel on bri means CW */ + if (!stack->pri && i == stack->b_num) { + stack->bc[i].cw = 1; } - } else { - for (i = 0; i bc[i])) { - /* 3. channel on bri means CW*/ - if (!stack->pri && i==stack->b_num) - stack->bc[i].cw=1; - prepare_bc(&stack->bc[i], channel); - return &stack->bc[i]; - } - } + prepare_bc(&stack->bc[i], channel); + return &stack->bc[i]; } - - cb_log(1,port,"There is no free channel on port (%d)\n",port); - return NULL; } } - cb_log(0,port,"Port is not configured (%d)\n",port); + cb_log(1, port, "There is no free channel on port (%d)\n", port); return NULL; } +#if defined(AST_MISDN_ENHANCEMENTS) +/*! + * \brief Allocate a B channel struct from the REGISTER pool + * + * \param port Logical port number + * + * \retval B channel struct on success. + * \retval NULL on error. + */ +struct misdn_bchannel *misdn_lib_get_register_bc(int port) +{ + struct misdn_stack *stack; + struct misdn_bchannel *bc; + unsigned index; + /* Find the port stack structure */ + stack = find_stack_by_port(port); + if (!stack) { + cb_log(0, port, "Port is not configured (%d)\n", port); + return NULL; + } + if (stack->blocked) { + cb_log(0, port, "Port is blocked\n"); + return NULL; + } -/* ******************************************************************* */ + for (index = MAX_BCHANS + 1; index < ARRAY_LEN(stack->bc); ++index) { + bc = &stack->bc[index]; + if (!test_inuse(bc)) { + prepare_bc(bc, 0); + bc->need_disconnect = 0; + bc->need_release = 0; + return bc; + } + } + + cb_log(1, port, "There is no free REGISTER link on port (%d)\n", port); + return NULL; +} +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + /*! * \internal * \brief Convert the facility function enum value into a string. @@ -3285,36 +3405,96 @@ */ static const char *fac2str(enum FacFunction facility) { - static const struct { - enum FacFunction facility; + static const struct { + enum FacFunction facility; char *name; } arr[] = { /* *INDENT-OFF* */ { Fac_None, "Fac_None" }, - { Fac_GetSupportedServices, "Fac_GetSupportedServices" }, - { Fac_Listen, "Fac_Listen" }, - { Fac_Suspend, "Fac_Suspend" }, - { Fac_Resume, "Fac_Resume" }, +#if defined(AST_MISDN_ENHANCEMENTS) + { Fac_ERROR, "Fac_ERROR" }, + { Fac_RESULT, "Fac_RESULT" }, + { Fac_REJECT, "Fac_REJECT" }, + + { Fac_ActivationDiversion, "Fac_ActivationDiversion" }, + { Fac_DeactivationDiversion, "Fac_DeactivationDiversion" }, + { Fac_ActivationStatusNotificationDiv, "Fac_ActivationStatusNotificationDiv" }, + { Fac_DeactivationStatusNotificationDiv, "Fac_DeactivationStatusNotificationDiv" }, + { Fac_InterrogationDiversion, "Fac_InterrogationDiversion" }, + { Fac_DiversionInformation, "Fac_DiversionInformation" }, + { Fac_CallDeflection, "Fac_CallDeflection" }, + { Fac_CallRerouteing, "Fac_CallRerouteing" }, + { Fac_DivertingLegInformation2, "Fac_DivertingLegInformation2" }, + { Fac_InterrogateServedUserNumbers, "Fac_InterrogateServedUserNumbers" }, + { Fac_DivertingLegInformation1, "Fac_DivertingLegInformation1" }, + { Fac_DivertingLegInformation3, "Fac_DivertingLegInformation3" }, + + { Fac_EctExecute, "Fac_EctExecute" }, + { Fac_ExplicitEctExecute, "Fac_ExplicitEctExecute" }, + { Fac_RequestSubaddress, "Fac_RequestSubaddress" }, + { Fac_SubaddressTransfer, "Fac_SubaddressTransfer" }, + { Fac_EctLinkIdRequest, "Fac_EctLinkIdRequest" }, + { Fac_EctInform, "Fac_EctInform" }, + { Fac_EctLoopTest, "Fac_EctLoopTest" }, + + { Fac_ChargingRequest, "Fac_ChargingRequest" }, + { Fac_AOCSCurrency, "Fac_AOCSCurrency" }, + { Fac_AOCSSpecialArr, "Fac_AOCSSpecialArr" }, + { Fac_AOCDCurrency, "Fac_AOCDCurrency" }, + { Fac_AOCDChargingUnit, "Fac_AOCDChargingUnit" }, + { Fac_AOCECurrency, "Fac_AOCECurrency" }, + { Fac_AOCEChargingUnit, "Fac_AOCEChargingUnit" }, + + { Fac_StatusRequest, "Fac_StatusRequest" }, + + { Fac_CallInfoRetain, "Fac_CallInfoRetain" }, + { Fac_EraseCallLinkageID, "Fac_EraseCallLinkageID" }, + { Fac_CCBSDeactivate, "Fac_CCBSDeactivate" }, + { Fac_CCBSErase, "Fac_CCBSErase" }, + { Fac_CCBSRemoteUserFree, "Fac_CCBSRemoteUserFree" }, + { Fac_CCBSCall, "Fac_CCBSCall" }, + { Fac_CCBSStatusRequest, "Fac_CCBSStatusRequest" }, + { Fac_CCBSBFree, "Fac_CCBSBFree" }, + { Fac_CCBSStopAlerting, "Fac_CCBSStopAlerting" }, + + { Fac_CCBSRequest, "Fac_CCBSRequest" }, + { Fac_CCBSInterrogate, "Fac_CCBSInterrogate" }, + + { Fac_CCNRRequest, "Fac_CCNRRequest" }, + { Fac_CCNRInterrogate, "Fac_CCNRInterrogate" }, + + { Fac_CCBS_T_Call, "Fac_CCBS_T_Call" }, + { Fac_CCBS_T_Suspend, "Fac_CCBS_T_Suspend" }, + { Fac_CCBS_T_Resume, "Fac_CCBS_T_Resume" }, + { Fac_CCBS_T_RemoteUserFree, "Fac_CCBS_T_RemoteUserFree" }, + { Fac_CCBS_T_Available, "Fac_CCBS_T_Available" }, + + { Fac_CCBS_T_Request, "Fac_CCBS_T_Request" }, + + { Fac_CCNR_T_Request, "Fac_CCNR_T_Request" }, + +#else + { Fac_CFActivate, "Fac_CFActivate" }, { Fac_CFDeactivate, "Fac_CFDeactivate" }, - { Fac_CFInterrogateParameters, "Fac_CFInterrogateParameters" }, - { Fac_CFInterrogateNumbers, "Fac_CFInterrogateNumbers" }, { Fac_CD, "Fac_CD" }, + { Fac_AOCDCurrency, "Fac_AOCDCurrency" }, { Fac_AOCDChargingUnit, "Fac_AOCDChargingUnit" }, +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ /* *INDENT-ON* */ }; - + unsigned index; - + for (index = 0; index < ARRAY_LEN(arr); ++index) { if (arr[index].facility == facility) { return arr[index].name; } - } /* end for */ + } return "unknown"; -} /* end fac2str() */ +} void misdn_lib_log_ies(struct misdn_bchannel *bc) { @@ -3326,27 +3506,68 @@ if (!stack) return; - cb_log(2, stack->port, " --> channel:%d mode:%s cause:%d ocause:%d rad:%s cad:%s\n", bc->channel, stack->nt?"NT":"TE", bc->cause, bc->out_cause, bc->rad, bc->cad); - cb_log(2, stack->port, - " --> info_dad:%s onumplan:%c dnumplan:%c rnumplan:%c cpnnumplan:%c\n", - bc->info_dad, - bc->onumplan>=0?'0'+bc->onumplan:' ', - bc->dnumplan>=0?'0'+bc->dnumplan:' ', - bc->rnumplan>=0?'0'+bc->rnumplan:' ', - bc->cpnnumplan>=0?'0'+bc->cpnnumplan:' ' - ); - + " --> channel:%d mode:%s cause:%d ocause:%d\n", + bc->channel, + stack->nt ? "NT" : "TE", + bc->cause, + bc->out_cause); + + cb_log(2, stack->port, + " --> info_dad:%s dialed numtype:%d plan:%d\n", + bc->info_dad, + bc->dialed.number_type, + bc->dialed.number_plan); + + cb_log(2, stack->port, + " --> caller:\"%s\" <%s> type:%d plan:%d pres:%d screen:%d\n", + bc->caller.name, + bc->caller.number, + bc->caller.number_type, + bc->caller.number_plan, + bc->caller.presentation, + bc->caller.screening); + + cb_log(2, stack->port, + " --> redirecting-from:\"%s\" <%s> type:%d plan:%d pres:%d screen:%d\n", + bc->redirecting.from.name, + bc->redirecting.from.number, + bc->redirecting.from.number_type, + bc->redirecting.from.number_plan, + bc->redirecting.from.presentation, + bc->redirecting.from.screening); + cb_log(2, stack->port, + " --> redirecting-to:\"%s\" <%s> type:%d plan:%d pres:%d screen:%d\n", + bc->redirecting.to.name, + bc->redirecting.to.number, + bc->redirecting.to.number_type, + bc->redirecting.to.number_plan, + bc->redirecting.to.presentation, + bc->redirecting.to.screening); + cb_log(2, stack->port, + " --> redirecting reason:%d count:%d\n", + bc->redirecting.reason, + bc->redirecting.count); + + cb_log(2, stack->port, + " --> connected:\"%s\" <%s> type:%d plan:%d pres:%d screen:%d\n", + bc->connected.name, + bc->connected.number, + bc->connected.number_type, + bc->connected.number_plan, + bc->connected.presentation, + bc->connected.screening); + cb_log(3, stack->port, " --> caps:%s pi:%x keypad:%s sending_complete:%d\n", bearer2str(bc->capability),bc->progress_indicator, bc->keypad, bc->sending_complete); - cb_log(4, stack->port, " --> screen:%d --> pres:%d\n", - bc->screen, bc->pres); - + + cb_log(4, stack->port, " --> set_pres:%d pres:%d\n", bc->set_presentation, bc->presentation); + cb_log(4, stack->port, " --> addr:%x l3id:%x b_stid:%x layer_id:%x\n", bc->addr, bc->l3_id, bc->b_stid, bc->layer_id); - - cb_log(4, stack->port, " --> facility:%s out_facility:%s\n",fac2str(bc->fac_in.Function),fac2str(bc->fac_out.Function)); + cb_log(4, stack->port, " --> facility in:%s out:%s\n", fac2str(bc->fac_in.Function), fac2str(bc->fac_out.Function)); + cb_log(5, stack->port, " --> urate:%d rate:%d mode:%d user1:%d\n", bc->urate, bc->rate, bc->mode,bc->user1); - + cb_log(5, stack->port, " --> bc:%p h:%d sh:%d\n", bc, bc->holded, bc->stack_holder); } @@ -3369,19 +3590,29 @@ int misdn_lib_send_event(struct misdn_bchannel *bc, enum event_e event ) { - msg_t *msg; - int retval=0; + msg_t *msg; + struct misdn_bchannel *bc2; + struct misdn_bchannel *holded_bc; struct misdn_stack *stack; - - if (!bc) RETURN(-1,OUT_POST_UNLOCK); - + int retval = 0; + int channel; + int tmpcause; + int tmp_out_cause; + + if (!bc) + RETURN(-1,OUT_POST_UNLOCK); + stack = get_stack_by_bc(bc); - if (!stack) { - cb_log(0,bc->port,"SENDEVENT: no Stack for event:%s oad:%s dad:%s \n", isdn_get_info(msgs_g, event, 0), bc->oad, bc->dad); + cb_log(0,bc->port, + "SENDEVENT: no Stack for event:%s caller:\"%s\" <%s> dialed:%s \n", + isdn_get_info(msgs_g, event, 0), + bc->caller.name, + bc->caller.number, + bc->dialed.number); RETURN(-1,OUT); } - + misdn_send_lock(bc); @@ -3394,15 +3625,22 @@ misdn_lib_get_l1_up(stack); RETURN(0,OUT); } - - cb_log(1, stack->port, "I SEND:%s oad:%s dad:%s pid:%d\n", isdn_get_info(msgs_g, event, 0), bc->oad, bc->dad, bc->pid); + + cb_log(1, stack->port, + "I SEND:%s caller:\"%s\" <%s> dialed:%s pid:%d\n", + isdn_get_info(msgs_g, event, 0), + bc->caller.name, + bc->caller.number, + bc->dialed.number, + bc->pid); cb_log(4, stack->port, " --> bc_state:%s\n",bc_state2str(bc->bc_state)); misdn_lib_log_ies(bc); - + switch (event) { + case EVENT_REGISTER: case EVENT_SETUP: - if (create_process(glob_mgr->midev, bc)<0) { - cb_log(0, stack->port, " No free channel at the moment @ send_event\n"); + if (create_process(glob_mgr->midev, bc) < 0) { + cb_log(0, stack->port, " No free channel at the moment @ send_event\n"); RETURN(-ENOCHAN,OUT); } @@ -3413,10 +3651,10 @@ case EVENT_PROCEEDING: case EVENT_SETUP_ACKNOWLEDGE: case EVENT_CONNECT: - if (!stack->nt) break; + if (!stack->nt) + break; case EVENT_RETRIEVE_ACKNOWLEDGE: - if (stack->nt) { if (bc->channel <=0 ) { /* else we have the channel already */ if (find_free_chan_in_stack(stack, bc, 0, 0)<0) { @@ -3436,19 +3674,26 @@ if (misdn_cap_is_speech(bc->capability)) { if ((event==EVENT_CONNECT)||(event==EVENT_RETRIEVE_ACKNOWLEDGE)) { if ( *bc->crypt_key ) { - cb_log(4, stack->port, " --> ENABLING BLOWFISH channel:%d oad%d:%s dad%d:%s \n", bc->channel, bc->onumplan,bc->oad, bc->dnumplan,bc->dad); - + cb_log(4, stack->port, + " --> ENABLING BLOWFISH channel:%d caller%d:\"%s\" <%s> dialed%d:%s\n", + bc->channel, + bc->caller.number_type, + bc->caller.name, + bc->caller.number, + bc->dialed.number_type, + bc->dialed.number); + manager_ph_control_block(bc, BF_ENABLE_KEY, bc->crypt_key, strlen(bc->crypt_key) ); } - + if (!bc->nodsp) manager_ph_control(bc, DTMF_TONE_START, 0); manager_ec_enable(bc); - + if (bc->txgain != 0) { cb_log(4, stack->port, "--> Changing txgain to %d\n", bc->txgain); manager_ph_control(bc, VOL_CHANGE_TX, bc->txgain); } - + if ( bc->rxgain != 0 ) { cb_log(4, stack->port, "--> Changing rxgain to %d\n", bc->rxgain); manager_ph_control(bc, VOL_CHANGE_RX, bc->rxgain); @@ -3458,8 +3703,7 @@ break; case EVENT_HOLD_ACKNOWLEDGE: - { - struct misdn_bchannel *holded_bc=malloc(sizeof(struct misdn_bchannel)); + holded_bc = malloc(sizeof(struct misdn_bchannel)); if (!holded_bc) { cb_log(0,bc->port, "Could not allocate holded_bc!!!\n"); RETURN(-1,OUT); @@ -3471,13 +3715,10 @@ bc_state_change(holded_bc,BCHAN_CLEANED); stack_holder_add(stack,holded_bc); - + /*kill the bridge and clean the bchannel*/ if (stack->nt) { - int channel; if (bc->bc_state == BCHAN_BRIDGED) { - struct misdn_bchannel *bc2; - misdn_split_conf(bc,bc->conf_id); bc2 = find_bc_by_confid(bc->conf_id); if (!bc2) { @@ -3486,7 +3727,7 @@ misdn_split_conf(bc2,bc->conf_id); } } - + channel = bc->channel; empty_bc(bc); @@ -3495,24 +3736,22 @@ if (channel>0) empty_chan_in_stack(stack,channel); - bc->in_use=0; + bc->in_use=0; } - - } - break; + break; /* finishing the channel eh ? */ case EVENT_DISCONNECT: if (!bc->need_disconnect) { - cb_log(0,bc->port," --> we have already send Disconnect\n"); + cb_log(0, bc->port, " --> we have already sent DISCONNECT\n"); RETURN(-1,OUT); } - + bc->need_disconnect=0; break; case EVENT_RELEASE: if (!bc->need_release) { - cb_log(0,bc->port," --> we have already send Release\n"); + cb_log(0, bc->port, " --> we have already sent RELEASE\n"); RETURN(-1,OUT); } bc->need_disconnect=0; @@ -3520,7 +3759,7 @@ break; case EVENT_RELEASE_COMPLETE: if (!bc->need_release_complete) { - cb_log(0,bc->port," --> we have already send Release_complete\n"); + cb_log(0, bc->port, " --> we have already sent RELEASE_COMPLETE\n"); RETURN(-1,OUT); } bc->need_disconnect=0; @@ -3528,33 +3767,32 @@ bc->need_release_complete=0; if (!stack->nt) { - /*create cleanup in TE*/ - int channel=bc->channel; + /* create cleanup in TE */ + channel = bc->channel; + tmpcause = bc->cause; + tmp_out_cause = bc->out_cause; - int tmpcause=bc->cause; - int tmp_out_cause=bc->out_cause; empty_bc(bc); bc->cause=tmpcause; bc->out_cause=tmp_out_cause; clean_up_bc(bc); - + if (channel>0) empty_chan_in_stack(stack,channel); - + bc->in_use=0; } break; - + case EVENT_CONNECT_ACKNOWLEDGE: - if ( bc->nt || misdn_cap_is_speech(bc->capability)) { int retval=setup_bc(bc); if (retval == -EINVAL){ cb_log(0,bc->port,"send_event: setup_bc failed\n"); - + } } - + if (misdn_cap_is_speech(bc->capability)) { if ( !bc->nodsp) manager_ph_control(bc, DTMF_TONE_START, 0); manager_ec_enable(bc); @@ -3569,21 +3807,32 @@ } } break; - + default: break; } - + /* Later we should think about sending bchannel data directly to misdn. */ msg = isdn_msg_build_event(msgs_g, bc, event, stack->nt); - msg_queue_tail(&stack->downqueue, msg); - sem_post(&glob_mgr->new_msg); - + if (!msg) { + /* + * The message was not built. + * + * NOTE: The only time that the message will fail to build + * is because the requested FACILITY message is not supported. + * A failed malloc() results in exit() being called. + */ + RETURN(-1, OUT); + } else { + msg_queue_tail(&stack->downqueue, msg); + sem_post(&glob_mgr->new_msg); + } + OUT: misdn_send_unlock(bc); OUT_POST_UNLOCK: - return retval; + return retval; } @@ -3601,12 +3850,12 @@ cb_log(0,0,"mISDN Msg without Address pr:%x dinfo:%x (already more than 100 of them)\n",frm->prim,frm->dinfo); cnt=0; } - + free_msg(msg); return 1; - + } - + switch (frm->prim) { case MGR_SETSTACK|INDICATION: return handle_bchan(msg); @@ -3614,7 +3863,7 @@ case MGR_SETSTACK|CONFIRM: case MGR_CLEARSTACK|CONFIRM: - free_msg(msg) ; + free_msg(msg) ; return 1; break; @@ -3635,13 +3884,13 @@ struct misdn_bchannel *bc; /*we flush the read buffer here*/ - + cb_log(9,0,"BCHAN DATA without BC: addr:%x port:%d channel:%d\n",frm->addr, port,channel); - - free_msg(msg); + + free_msg(msg); return 1; - - + + bc = find_bc_by_channel(port, channel); if (!bc) { @@ -3652,7 +3901,7 @@ free_msg(msg); return 1; } - + cb_log(0,0," --> bc not found by channel\n"); if (stack->l2link) misdn_lib_get_l2_down(stack); @@ -3663,7 +3912,7 @@ free_msg(msg); return 1; } - + cb_log(3,port," --> BC in state:%s\n", bc_state2str(bc->bc_state)); } } @@ -3678,7 +3927,7 @@ struct misdn_stack *stack; stack=find_stack_by_addr( frm->addr ); - + if (!stack) { return 0; } @@ -3690,7 +3939,7 @@ #endif int manager_isdn_handler(iframe_t *frm ,msg_t *msg) -{ +{ if (frm->dinfo==0xffffffff && frm->prim==(PH_DATA|CONFIRM)) { cb_log(0,0,"SERIOUS BUG, dinfo == 0xffffffff, prim == PH_DATA | CONFIRM !!!!\n"); @@ -3701,35 +3950,35 @@ if (handle_bchan(msg)) { return 0 ; } - + if (unhandled_bmsg_count==1000) { - cb_log(0, 0, "received 1k Unhandled Bchannel Messages: prim %x len %d from addr %x, dinfo %x on this port.\n",frm->prim, frm->len, frm->addr, frm->dinfo); + cb_log(0, 0, "received 1k Unhandled Bchannel Messages: prim %x len %d from addr %x, dinfo %x on this port.\n",frm->prim, frm->len, frm->addr, frm->dinfo); unhandled_bmsg_count=0; } unhandled_bmsg_count++; free_msg(msg); return 0; - } + } #ifdef RECV_FRM_SYSLOG_DEBUG syslog(LOG_NOTICE,"mISDN recv: P(%02d): ADDR:%x PRIM:%x DINFO:%x\n",stack->port, frm->addr, frm->prim, frm->dinfo); #endif - if (handle_timers(msg)) + if (handle_timers(msg)) return 0 ; - - if (handle_mgmt(msg)) - return 0 ; - - if (handle_l2(msg)) + + if (handle_mgmt(msg)) return 0 ; + if (handle_l2(msg)) + return 0 ; + /* Its important to handle l1 AFTER l2 */ - if (handle_l1(msg)) + if (handle_l1(msg)) return 0 ; - + if (handle_frm_nt(msg)) { return 0; } @@ -3742,10 +3991,10 @@ return 0 ; } - cb_log(0, 0, "Unhandled Message: prim %x len %d from addr %x, dinfo %x on this port.\n",frm->prim, frm->len, frm->addr, frm->dinfo); + cb_log(0, 0, "Unhandled Message: prim %x len %d from addr %x, dinfo %x on this port.\n",frm->prim, frm->len, frm->addr, frm->dinfo); free_msg(msg); - + return 0; } @@ -3773,16 +4022,16 @@ frm->dinfo = 0; frm->len = 0; - + msg_queue_tail(&glob_mgr->activatequeue, msg); sem_post(&glob_mgr->new_msg); - - return 0; + + return 0; } -int queue_cleanup_bc(struct misdn_bchannel *bc) +int queue_cleanup_bc(struct misdn_bchannel *bc) { msg_t *msg=alloc_msg(MAX_MSG_SIZE); iframe_t *frm; @@ -3799,15 +4048,15 @@ frm->dinfo = bc->port; frm->len = 0; - + msg_queue_tail(&glob_mgr->activatequeue, msg); sem_post(&glob_mgr->new_msg); - return 0; + return 0; } -int misdn_lib_pid_restart(int pid) +int misdn_lib_pid_restart(int pid) { struct misdn_bchannel *bc=manager_find_bc_by_pid(pid); @@ -3824,7 +4073,7 @@ struct misdn_bchannel dummybc; /*default is all channels*/ cb_log(0, port, "Sending Restarts on this port.\n"); - + misdn_make_dummy(&dummybc, stack->port, MISDN_ID_GLOBAL, stack->nt, 0); /*default is all channels*/ @@ -3860,11 +4109,11 @@ int misdn_lib_port_restart(int port) { struct misdn_stack *stack=find_stack_by_port(port); - + cb_log(0, port, "Restarting this port.\n"); if (stack) { cb_log(0, port, "Stack:%p\n",stack); - + clear_l3(stack); { msg_t *msg=alloc_msg(MAX_MSG_SIZE); @@ -3874,7 +4123,7 @@ cb_log(0, port, "port_restart: alloc_msg failed\n"); return -1; } - + frm=(iframe_t*)msg->data; /* we must activate if we are deactivated */ /* activate bchannel */ @@ -3889,7 +4138,7 @@ if (stack->nt) misdn_lib_reinit_nt_stack(stack->port); - + } return 0; @@ -3897,25 +4146,25 @@ -static sem_t handler_started; +static sem_t handler_started; /* This is a thread */ static void manager_event_handler(void *arg) { - sem_post(&handler_started); + sem_post(&handler_started); while (1) { struct misdn_stack *stack; msg_t *msg; - + /** wait for events **/ sem_wait(&glob_mgr->new_msg); - + for (msg=msg_dequeue(&glob_mgr->activatequeue); msg; msg=msg_dequeue(&glob_mgr->activatequeue) ) { - + iframe_t *frm = (iframe_t*) msg->data ; switch ( frm->prim) { @@ -3930,7 +4179,7 @@ free_msg(msg); break; } - + bc = find_bc_by_l3id(stack, frm->addr); if (bc) { cb_log(1,bc->port,"CLEARSTACK queued, cleaning up\n"); @@ -3939,7 +4188,7 @@ cb_log(0,stack->port,"bc could not be cleaned correctly !! addr [%x]\n",frm->addr); } } - free_msg(msg); + free_msg(msg); break; case MGR_SETSTACK | REQUEST : /* Warning: memory leak here if we get this message */ @@ -3952,10 +4201,10 @@ for (stack=glob_mgr->stack_list; stack; - stack=stack->next ) { + stack=stack->next ) { while ( (msg=msg_dequeue(&stack->upqueue)) ) { - /** Handle L2/3 Signalling after bchans **/ + /** Handle L2/3 Signalling after bchans **/ if (!handle_frm_nt(msg)) { /* Maybe it's TE */ if (!handle_frm(msg)) { @@ -3965,16 +4214,16 @@ } } - /* Here we should check if we really want to - send all the messages we've queued, lets - assume we've queued a Disconnect, but + /* Here we should check if we really want to + send all the messages we've queued, lets + assume we've queued a Disconnect, but received it already from the other side!*/ - + while ( (msg=msg_dequeue(&stack->downqueue)) ) { if (stack->nt ) { if (stack->nst.manager_l3(&stack->nst, msg)) cb_log(0, stack->port, "Error@ Sending Message in NT-Stack.\n"); - + } else { iframe_t *frm = (iframe_t *)msg->data; struct misdn_bchannel *bc = find_bc_by_l3id(stack, frm->dinfo); @@ -4003,14 +4252,14 @@ int i = mISDN_open(); int max=0; - + if (i<0) return -1; max = mISDN_get_stack_count(i); - + mISDN_close(i); - + return max; } @@ -4027,12 +4276,12 @@ #endif } -void misdn_lib_nt_debug_init( int flags, char *file ) +void misdn_lib_nt_debug_init( int flags, char *file ) { static int init=0; char *f; - - if (!flags) + + if (!flags) f=NULL; else f=file; @@ -4053,50 +4302,52 @@ char plist[1024]; int midev; int port_count=0; - + cb_log = iface->cb_log; cb_event = iface->cb_event; cb_jb_empty = iface->cb_jb_empty; - + glob_mgr = mgr; - + msg_init(); misdn_lib_nt_debug_init(0,NULL); - + if (!portlist || (*portlist == 0) ) return 1; - + init_flip_bits(); - + { strncpy(plist,portlist, 1024); plist[1023] = 0; } - + memcpy(tone_425_flip,tone_425,TONE_425_SIZE); flip_buf_bits(tone_425_flip,TONE_425_SIZE); memcpy(tone_silence_flip,tone_SILENCE,TONE_SILENCE_SIZE); flip_buf_bits(tone_silence_flip,TONE_SILENCE_SIZE); - + midev=te_lib_init(); mgr->midev=midev; port_count=mISDN_get_stack_count(midev); - + msg_queue_init(&mgr->activatequeue); - + if (sem_init(&mgr->new_msg, 1, 0)<0) sem_init(&mgr->new_msg, 0, 0); - + for (tok=strtok_r(plist," ,",&tokb ); - tok; + tok; tok=strtok_r(NULL," ,",&tokb)) { int port = atoi(tok); struct misdn_stack *stack; - static int first=1; + struct misdn_stack *help; int ptp=0; - + int i; + int r; + if (strstr(tok, "ptp")) ptp=1; @@ -4104,53 +4355,59 @@ cb_log(0, port, "Couldn't Initialize this port since we have only %d ports\n", port_count); exit(1); } - stack=stack_init(midev, port, ptp); - + + stack = stack_init(midev, port, ptp); if (!stack) { perror("stack_init"); exit(1); } - - { - int i; - for(i=0;i<=stack->b_num; i++) { - int r; - if ((r=init_bc(stack, &stack->bc[i], stack->midev,port,i, "", 1))<0) { - cb_log(0, port, "Got Err @ init_bc :%d\n",r); - exit(1); - } + + /* Initialize the B channel records for real B channels. */ + for (i = 0; i <= stack->b_num; i++) { + r = init_bc(stack, &stack->bc[i], stack->midev, port, i); + if (r < 0) { + cb_log(0, port, "Got Err @ init_bc :%d\n", r); + exit(1); } } +#if defined(AST_MISDN_ENHANCEMENTS) + /* Initialize the B channel records for REGISTER signaling links. */ + for (i = MAX_BCHANS + 1; i < ARRAY_LEN(stack->bc); ++i) { + r = init_bc(stack, &stack->bc[i], stack->midev, port, i); + if (r < 0) { + cb_log(0, port, "Got Err @ init_bc :%d\n", r); + exit(1); + } + stack->bc[i].is_register_pool = 1; + } +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ - if (stack && first) { - mgr->stack_list=stack; - first=0; - continue; + /* Add the new stack to the end of the list */ + help = mgr->stack_list; + if (!help) { + mgr->stack_list = stack; + } else { + while (help->next) { + help = help->next; + } + help->next = stack; } - - if (stack) { - struct misdn_stack * help; - for ( help=mgr->stack_list; help; help=help->next ) - if (help->next == NULL) break; - help->next=stack; - } - } - + if (sem_init(&handler_started, 1, 0)<0) sem_init(&handler_started, 0, 0); - + cb_log(8, 0, "Starting Event Handler\n"); pthread_create( &mgr->event_handler_thread, NULL,(void*)manager_event_handler, mgr); - + sem_wait(&handler_started) ; cb_log(8, 0, "Starting Event Catcher\n"); pthread_create( &mgr->event_thread, NULL, (void*)misdn_lib_isdn_event_catcher, mgr); - + cb_log(8, 0, "Event Catcher started\n"); - global_state= MISDN_INITIALIZED; - + global_state= MISDN_INITIALIZED; + return (mgr == NULL); } @@ -4158,7 +4415,7 @@ { struct misdn_stack *help; int i; - + for ( help=glob_mgr->stack_list; help; help=help->next ) { for(i=0;i<=help->b_num; i++) { char buf[1024]; @@ -4168,21 +4425,21 @@ cb_log (1, help->port, "Destroying this port.\n"); stack_destroy(help); } - + if (global_state == MISDN_INITIALIZED) { cb_log(4, 0, "Killing Handler Thread\n"); if ( pthread_cancel(glob_mgr->event_handler_thread) == 0 ) { cb_log(4, 0, "Joining Handler Thread\n"); pthread_join(glob_mgr->event_handler_thread, NULL); } - + cb_log(4, 0, "Killing Main Thread\n"); if ( pthread_cancel(glob_mgr->event_thread) == 0 ) { cb_log(4, 0, "Joining Main Thread\n"); pthread_join(glob_mgr->event_thread, NULL); } } - + cb_log(1, 0, "Closing mISDN device\n"); te_lib_destroy(glob_mgr->midev); } @@ -4202,12 +4459,12 @@ cb_log(0, bc->port, "bchannel_activate: Stack not found !"); return ; } - + /* we must activate if we are deactivated */ clear_ibuffer(bc->astbuf); - + cb_log(5, stack->port, "$$$ Bchan Activated addr %x\n", bc->addr); - + mISDN_write_frame(stack->midev, buf, bc->addr | FLG_MSG_DOWN, DL_ESTABLISH | REQUEST, 0,0, NULL, TIMEOUT_1SEC); return ; @@ -4218,7 +4475,7 @@ { struct misdn_stack *stack=get_stack_by_bc(bc); iframe_t dact; - char buf[128]; + char buf[128]; switch (bc->bc_state) { case BCHAN_ACTIVATED: @@ -4231,11 +4488,11 @@ return ; } - + cb_log(5, stack->port, "$$$ Bchan deActivated addr %x\n", bc->addr); - + bc->generate_tone=0; - + dact.prim = DL_RELEASE | REQUEST; dact.addr = bc->addr | FLG_MSG_DOWN; dact.dinfo = 0; @@ -4243,9 +4500,9 @@ mISDN_write_frame(stack->midev, buf, bc->addr | FLG_MSG_DOWN, DL_RELEASE|REQUEST,0,0,NULL, TIMEOUT_1SEC); clear_ibuffer(bc->astbuf); - + bc_state_change(bc,BCHAN_RELEASE); - + return; } @@ -4265,19 +4522,19 @@ cb_log(3, bc->port, "BC not yet activated (state:%s)\n",bc_state2str(bc->bc_state)); return -1; } - + frm->prim = DL_DATA|REQUEST; frm->dinfo = 0; frm->addr = bc->addr | FLG_MSG_DOWN ; - + frm->len = len; memcpy(&buf[mISDN_HEADER_LEN], data,len); - - if ( misdn_cap_is_speech(bc->capability) ) + + if ( misdn_cap_is_speech(bc->capability) ) flip_buf_bits( &buf[mISDN_HEADER_LEN], len); else cb_log(6, stack->port, "Writing %d data bytes\n",len); - + cb_log(9, stack->port, "Writing %d bytes 2 mISDN\n",len); r=mISDN_write(stack->midev, buf, frm->len + mISDN_HEADER_LEN, TIMEOUT_INFINIT); return 0; @@ -4294,9 +4551,9 @@ iframe_t *ctrl = (iframe_t *)buffer; /* preload data */ unsigned int *d = (unsigned int*)&ctrl->data.p; /*struct misdn_stack *stack=get_stack_by_bc(bc);*/ - + cb_log(4,bc->port,"ph_control: c1:%x c2:%x\n",c1,c2); - + ctrl->prim = PH_CONTROL | REQUEST; ctrl->addr = bc->addr | FLG_MSG_DOWN; ctrl->dinfo = 0; @@ -4345,7 +4602,7 @@ iframe_t *ctrl = (iframe_t *)buffer; unsigned int *d = (unsigned int *)&ctrl->data.p; /*struct misdn_stack *stack=get_stack_by_bc(bc);*/ - + ctrl->prim = PH_CONTROL | REQUEST; ctrl->addr = bc->addr | FLG_MSG_DOWN; ctrl->dinfo = 0; @@ -4361,13 +4618,14 @@ void manager_clean_bc(struct misdn_bchannel *bc ) { struct misdn_stack *stack=get_stack_by_bc(bc); - - if (bc->channel>0) + + if (stack && bc->channel > 0) { empty_chan_in_stack(stack, bc->channel); + } empty_bc(bc); bc->in_use=0; - cb_event(EVENT_CLEANUP, bc, NULL); + cb_event(EVENT_CLEANUP, bc, NULL); } @@ -4375,15 +4633,15 @@ { struct misdn_bchannel *help; cb_log(4,stack->port, "*HOLDER: add %x\n",holder->l3_id); - + holder->stack_holder=1; holder->next=NULL; - + if (!stack->holding) { stack->holding = holder; return; } - + for (help=stack->holding; help; help=help->next) { @@ -4392,7 +4650,7 @@ break; } } - + } void stack_holder_remove(struct misdn_stack *stack, struct misdn_bchannel *holder) @@ -4400,17 +4658,17 @@ struct misdn_bchannel *h1; if (!holder->stack_holder) return; - + holder->stack_holder=0; - + cb_log(4,stack->port, "*HOLDER: remove %x\n",holder->l3_id); if (!stack || ! stack->holding) return; - + if (holder == stack->holding) { stack->holding = stack->holding->next; return; } - + for (h1=stack->holding; h1; h1=h1->next) { @@ -4426,9 +4684,9 @@ struct misdn_bchannel *help; cb_log(4,stack?stack->port:0, "*HOLDER: find_bychan %c\n", chan); - + if (!stack) return NULL; - + for (help=stack->holding; help; help=help->next) { @@ -4448,9 +4706,9 @@ struct misdn_bchannel *help; cb_log(4,stack?stack->port:0, "*HOLDER: find %lx\n",l3id); - + if (!stack) return NULL; - + for (help=stack->holding; help; help=help->next) { @@ -4466,34 +4724,34 @@ -void misdn_lib_send_tone(struct misdn_bchannel *bc, enum tone_e tone) +void misdn_lib_send_tone(struct misdn_bchannel *bc, enum tone_e tone) { char buf[mISDN_HEADER_LEN + 128] = ""; iframe_t *frm = (iframe_t*)buf; switch(tone) { case TONE_DIAL: - manager_ph_control(bc, TONE_PATT_ON, TONE_GERMAN_DIALTONE); + manager_ph_control(bc, TONE_PATT_ON, TONE_GERMAN_DIALTONE); break; - + case TONE_ALERTING: - manager_ph_control(bc, TONE_PATT_ON, TONE_GERMAN_RINGING); + manager_ph_control(bc, TONE_PATT_ON, TONE_GERMAN_RINGING); break; - + case TONE_HANGUP: - manager_ph_control(bc, TONE_PATT_ON, TONE_GERMAN_HANGUP); + manager_ph_control(bc, TONE_PATT_ON, TONE_GERMAN_HANGUP); break; case TONE_NONE: default: - manager_ph_control(bc, TONE_PATT_OFF, TONE_GERMAN_HANGUP); + manager_ph_control(bc, TONE_PATT_OFF, TONE_GERMAN_HANGUP); } frm->prim=DL_DATA|REQUEST; frm->addr=bc->addr|FLG_MSG_DOWN; frm->dinfo=0; frm->len=128; - + mISDN_write(glob_mgr->midev, frm, mISDN_HEADER_LEN+frm->len, TIMEOUT_1SEC); } @@ -4501,7 +4759,7 @@ void manager_ec_enable(struct misdn_bchannel *bc) { struct misdn_stack *stack=get_stack_by_bc(bc); - + cb_log(4, stack?stack->port:0,"ec_enable\n"); if (!misdn_cap_is_speech(bc->capability)) { @@ -4518,7 +4776,7 @@ if (bc->ec_enable) { cb_log(3, stack?stack->port:0,"Sending Control ECHOCAN_ON taps:%d\n",bc->ec_deftaps); - + switch (bc->ec_deftaps) { case 4: case 8: @@ -4535,10 +4793,10 @@ cb_log(0, stack->port, "Taps should be power of 2\n"); bc->ec_deftaps=128; } - + ec_arr[0]=bc->ec_deftaps; ec_arr[1]=0; - + manager_ph_control_block(bc, ECHOCAN_ON, ec_arr, sizeof(ec_arr)); } #endif @@ -4550,7 +4808,7 @@ void manager_ec_disable(struct misdn_bchannel *bc) { struct misdn_stack *stack=get_stack_by_bc(bc); - + cb_log(4, stack?stack->port:0," --> ec_disable\n"); if (!misdn_cap_is_speech(bc->capability)) { @@ -4568,7 +4826,8 @@ #endif } -struct misdn_stack* get_misdn_stack(void) { +struct misdn_stack *get_misdn_stack(void) +{ return glob_mgr->stack_list; } @@ -4605,10 +4864,10 @@ cb_log(4, bc1->port, "I Send: BRIDGE from:%d to:%d\n",bc1->port,bc2->port); - for (bc=bc_list; *bc; bc++) { + for (bc=bc_list; *bc; bc++) { (*bc)->conf_id=conf_id; cb_log(4, (*bc)->port, " --> bc_addr:%x\n",(*bc)->addr); - + switch((*bc)->bc_state) { case BCHAN_ACTIVATED: misdn_join_conf(*bc,conf_id); @@ -4627,15 +4886,15 @@ bc1,bc2,NULL }; struct misdn_bchannel **bc; - - for (bc=bc_list; *bc; bc++) { + + for (bc=bc_list; *bc; bc++) { if ( (*bc)->bc_state == BCHAN_BRIDGED){ misdn_split_conf( *bc, (*bc)->conf_id); } else { cb_log( 2, (*bc)->port, "BC not bridged (state:%s) so not splitting it\n",bc_state2str((*bc)->bc_state)); } } - + } @@ -4651,37 +4910,37 @@ void misdn_lib_reinit_nt_stack(int port) { struct misdn_stack *stack=find_stack_by_port(port); - + if (stack) { stack->l2link=0; stack->blocked=0; - + cleanup_Isdnl3(&stack->nst); cleanup_Isdnl2(&stack->nst); memset(&stack->nst, 0, sizeof(net_stack_t)); memset(&stack->mgr, 0, sizeof(manager_t)); - + stack->mgr.nst = &stack->nst; stack->nst.manager = &stack->mgr; - + stack->nst.l3_manager = handle_event_nt; stack->nst.device = glob_mgr->midev; stack->nst.cardnr = port; stack->nst.d_stid = stack->d_stid; - + stack->nst.feature = FEATURE_NET_HOLD; if (stack->ptp) stack->nst.feature |= FEATURE_NET_PTP; if (stack->pri) stack->nst.feature |= FEATURE_NET_CRLEN2 | FEATURE_NET_EXTCID; - + stack->nst.l1_id = stack->lower_id; stack->nst.l2_id = stack->upper_id; - + msg_queue_init(&stack->nst.down_queue); - + Isdnl2Init(&stack->nst); Isdnl3Init(&stack->nst); Index: channels/misdn/isdn_lib_intern.h =================================================================== --- a/channels/misdn/isdn_lib_intern.h (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/misdn/isdn_lib_intern.h (.../trunk) (revision 202568) @@ -41,7 +41,6 @@ struct isdn_msg { unsigned long misdn_msg; - enum layer_e layer; enum event_e event; void (*msg_parser)(struct isdn_msg *msgs, msg_t *msg, struct misdn_bchannel *bc, int nt); @@ -52,7 +51,15 @@ /* for isdn_msg_parser.c */ msg_t *create_l3msg(int prim, int mt, int dinfo , int size, int nt); +#if defined(AST_MISDN_ENHANCEMENTS) +/* Max call-completion REGISTER signaling links per stack/port */ +#define MISDN_MAX_REGISTER_LINKS MAX_BCHANS +#else +/* Max call-completion REGISTER signaling links per stack/port */ +#define MISDN_MAX_REGISTER_LINKS 0 +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ +#define MAXPROCS 0x100 struct misdn_stack { /** is first element because &nst equals &mISDNlist **/ @@ -88,8 +95,6 @@ /*! \brief TRUE if Layer 2 is UP */ int l2link; - time_t l2establish; /* Not used */ - /*! \brief TRUE if Layer 1 is UP */ int l1link; @@ -106,7 +111,7 @@ int pri; /*! \brief CR Process ID allocation table. TRUE if ID allocated */ - int procids[0x100+1]; + int procids[MAXPROCS]; /*! \brief Queue of Event messages to send to mISDN */ msg_queue_t downqueue; @@ -116,14 +121,18 @@ /*! \brief Logical Layer 1 port associated with this stack */ int port; - /*! \brief B Channel record pool array */ - struct misdn_bchannel bc[MAX_BCHANS + 1]; + /*! + * \brief B Channel record pool array + * (Must be dimensioned the same as struct misdn_stack.channels[]) + */ + struct misdn_bchannel bc[MAX_BCHANS + 1 + MISDN_MAX_REGISTER_LINKS]; - struct misdn_bchannel* bc_list; /* Not used */ + /*! + * \brief Array of B channels in use (a[0] = B1). TRUE if B channel in use. + * (Must be dimensioned the same as struct misdn_stack.bc[]) + */ + char channels[MAX_BCHANS + 1 + MISDN_MAX_REGISTER_LINKS]; - /*! \brief Array of B channels in use (a[0] = B1). TRUE if B channel in use */ - int channels[MAX_BCHANS + 1]; - /*! \brief List of holded channels */ struct misdn_bchannel *holding; Index: channels/misdn/isdn_lib.h =================================================================== --- a/channels/misdn/isdn_lib.h (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/misdn/isdn_lib.h (.../trunk) (revision 202568) @@ -96,15 +96,23 @@ ENOCHAN=1 }; - enum mISDN_NUMBER_PLAN { - NUMPLAN_UNINITIALIZED=-1, - NUMPLAN_INTERNATIONAL=0x1, - NUMPLAN_NATIONAL=0x2, - NUMPLAN_SUBSCRIBER=0x4, - NUMPLAN_UNKNOWN=0x0 + NUMPLAN_UNKNOWN = 0x0, + NUMPLAN_ISDN = 0x1, /* ISDN/Telephony numbering plan E.164 */ + NUMPLAN_DATA = 0x3, /* Data numbering plan X.121 */ + NUMPLAN_TELEX = 0x4, /* Telex numbering plan F.69 */ + NUMPLAN_NATIONAL = 0x8, + NUMPLAN_PRIVATE = 0x9 }; +enum mISDN_NUMBER_TYPE { + NUMTYPE_UNKNOWN = 0x0, + NUMTYPE_INTERNATIONAL = 0x1, + NUMTYPE_NATIONAL = 0x2, + NUMTYPE_NETWORK_SPECIFIC = 0x3, + NUMTYPE_SUBSCRIBER = 0x4, + NUMTYPE_ABBREVIATED = 0x5 +}; enum event_response_e { RESPONSE_IGNORE_SETUP_WITHOUT_CLOSE, @@ -125,6 +133,7 @@ EVENT_PROCEEDING, EVENT_PROGRESS, EVENT_SETUP, + EVENT_REGISTER, EVENT_ALERTING, EVENT_CONNECT, EVENT_SETUP_ACKNOWLEDGE, @@ -189,6 +198,44 @@ INFO_PI_INTERWORKING_NO_RELEASE_POST_ANSWER =0x13 }; +/*! + * \brief Q.931 encoded redirecting reason + */ +enum mISDN_REDIRECTING_REASON { + mISDN_REDIRECTING_REASON_UNKNOWN = 0x0, + /*! Call forwarding busy or called DTE busy */ + mISDN_REDIRECTING_REASON_CALL_FWD_BUSY = 0x1, + /*! Call forwarding no reply */ + mISDN_REDIRECTING_REASON_NO_REPLY = 0x2, + /*! Call deflection */ + mISDN_REDIRECTING_REASON_DEFLECTION = 0x4, + /*! Called DTE out of order */ + mISDN_REDIRECTING_REASON_OUT_OF_ORDER = 0x9, + /*! Call forwarding by the called DTE */ + mISDN_REDIRECTING_REASON_CALL_FWD_DTE = 0xA, + /*! Call forwarding unconditional or systematic call redirection */ + mISDN_REDIRECTING_REASON_CALL_FWD = 0xF +}; + +/*! + * \brief Notification description code enumeration + */ +enum mISDN_NOTIFY_CODE { + mISDN_NOTIFY_CODE_INVALID = -1, + /*! Call is placed on hold (Q.931) */ + mISDN_NOTIFY_CODE_USER_SUSPEND = 0x00, + /*! Call is taken off of hold (Q.931) */ + mISDN_NOTIFY_CODE_USER_RESUME = 0x01, + /*! Call is diverting (EN 300 207-1 Section 7.2.1) */ + mISDN_NOTIFY_CODE_CALL_IS_DIVERTING = 0x7B, + /*! Call diversion is enabled (cfu, cfb, cfnr) (EN 300 207-1 Section 7.2.1) */ + mISDN_NOTIFY_CODE_DIVERSION_ACTIVATED = 0x68, + /*! Call transfer, alerting (EN 300 369-1 Section 7.2) */ + mISDN_NOTIFY_CODE_CALL_TRANSFER_ALERTING = 0x69, + /*! Call transfer, active(answered) (EN 300 369-1 Section 7.2) */ + mISDN_NOTIFY_CODE_CALL_TRANSFER_ACTIVE = 0x6A, +}; + enum { /*CODECS*/ INFO_CODEC_ULAW=2, INFO_CODEC_ALAW=3 @@ -202,12 +249,116 @@ UNKNOWN }; +/*! Maximum phone number (address) length plus null terminator */ +#define MISDN_MAX_NUMBER_LEN (31 + 1) +/*! Maximum name length plus null terminator (From ECMA-164) */ +#define MISDN_MAX_NAME_LEN (50 + 1) +/*! Maximum subaddress length plus null terminator */ +#define MISDN_MAX_SUBADDRESS_LEN (23 + 1) + +/*! Maximum keypad facility content length plus null terminator */ +#define MISDN_MAX_KEYPAD_LEN (31 + 1) + +/*! \brief Dialed/Called information struct */ +struct misdn_party_dialing { + /*! \brief Type-of-number in ISDN terms for the dialed/called number */ + enum mISDN_NUMBER_TYPE number_type; + + /*! \brief Type-of-number numbering plan. */ + enum mISDN_NUMBER_PLAN number_plan; + + /*! \brief Dialed/Called Phone Number (Address) */ + char number[MISDN_MAX_NUMBER_LEN]; + + /*! \brief Dialed/Called Subaddress number */ + char subaddress[MISDN_MAX_SUBADDRESS_LEN]; +}; + +/*! \brief Connected-Line/Calling/Redirecting ID info struct */ +struct misdn_party_id { + /*! \brief Number presentation restriction code + * 0=Allowed, 1=Restricted, 2=Unavailable + */ + int presentation; + + /*! \brief Number screening code + * 0=Unscreened, 1=Passed Screen, 2=Failed Screen, 3=Network Number + */ + int screening; + + /*! \brief Type-of-number in ISDN terms for the number */ + enum mISDN_NUMBER_TYPE number_type; + + /*! \brief Type-of-number numbering plan. */ + enum mISDN_NUMBER_PLAN number_plan; + + /*! \brief Subscriber Name + * \note The name is currently obtained from Asterisk for + * potential use in display ie's since basic ISDN does + * not support names directly. + */ + char name[MISDN_MAX_NAME_LEN]; + + /*! \brief Phone number (Address) */ + char number[MISDN_MAX_NUMBER_LEN]; + + /*! \brief Subaddress number */ + char subaddress[MISDN_MAX_SUBADDRESS_LEN]; +}; + +/*! \brief Redirecting information struct */ +struct misdn_party_redirecting { + /*! \brief Who is redirecting the call (Sent to the party the call is redirected toward) */ + struct misdn_party_id from; + + /*! \brief Where the call is being redirected toward (Sent to the calling party) */ + struct misdn_party_id to; + + /*! \brief Reason a call is being redirected (Q.931 field value) */ + enum mISDN_REDIRECTING_REASON reason; + + /*! \brief Number of times the call has been redirected */ + int count; + + /*! \brief TRUE if the redirecting.to information has changed */ + int to_changed; +}; + + +/*! \brief B channel control structure */ struct misdn_bchannel { /*! \brief B channel send locking structure */ struct send_lock *send_lock; +#if defined(AST_MISDN_ENHANCEMENTS) + /*! \brief The BC, HLC (optional) and LLC (optional) contents from the SETUP message. */ + struct Q931_Bc_Hlc_Llc setup_bc_hlc_llc; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + + /*! + * \brief Dialed/Called information struct + * \note The number_type element is set to "dialplan" in /etc/asterisk/misdn.conf for outgoing calls + */ + struct misdn_party_dialing dialed; + + /*! \brief Originating/Caller ID information struct + * \note The number_type element can be set to "localdialplan" in /etc/asterisk/misdn.conf for outgoing calls + * \note The number element can be set to "callerid" in /etc/asterisk/misdn.conf for outgoing calls + */ + struct misdn_party_id caller; + + /*! \brief Connected-Party/Connected-Line ID information struct + * \note The number_type element can be set to "cpndialplan" in /etc/asterisk/misdn.conf for outgoing calls + */ + struct misdn_party_id connected; + + /*! \brief Redirecting information struct (Where a call diversion or transfer was invoked) + * \note The redirecting subaddress is not defined in Q.931 so it is not used. + */ + struct misdn_party_redirecting redirecting; + /*! \brief TRUE if this is a dummy BC record */ int dummy; @@ -264,6 +415,9 @@ /*! \brief TRUE if the B channel number is preselected */ int channel_preselected; + /*! \brief TRUE if the B channel is allocated from the REGISTER pool */ + int is_register_pool; + /*! \brief TRUE if B channel record is in use */ int in_use; @@ -286,8 +440,6 @@ /*! \brief Not used. Contents are setup but not used. */ void *astbuf; - void *misdnbuf; /* Not used */ - /*! \brief TRUE if the TE side should choose the B channel to use * \note This value is user configurable in /etc/asterisk/misdn.conf */ @@ -326,26 +478,6 @@ /*! \brief TRUE if we will not use the jitter buffer system */ int nojitter; - /*! \brief Type-of-number in ISDN terms for the dialed/called number - * \note This value is set to "dialplan" in /etc/asterisk/misdn.conf for outgoing calls - */ - enum mISDN_NUMBER_PLAN dnumplan; - - /*! \brief Type-of-number in ISDN terms for the redirecting number which a call diversion or transfer was invoked. - * \note Collected from the incoming SETUP message but not used. - */ - enum mISDN_NUMBER_PLAN rnumplan; - - /*! \brief Type-of-number in ISDN terms for the originating/calling number (Caller-ID) - * \note This value is set to "localdialplan" in /etc/asterisk/misdn.conf for outgoing calls - */ - enum mISDN_NUMBER_PLAN onumplan; - - /*! \brief Type-of-number in ISDN terms for the connected party number - * \note This value is set to "cpndialplan" in /etc/asterisk/misdn.conf for outgoing calls - */ - enum mISDN_NUMBER_PLAN cpnnumplan; - /*! \brief Progress Indicator IE coding standard field. * \note Collected from the incoming messages but not used. */ @@ -361,6 +493,18 @@ */ int progress_indicator; +#if defined(AST_MISDN_ENHANCEMENTS) + /*! + * \brief TRUE if waiting for DivertingLegInformation3 to queue redirecting update. + */ + int div_leg_3_rx_wanted; + + /*! + * \brief TRUE if a DivertingLegInformation3 needs to be sent with CONNECT. + */ + int div_leg_3_tx_pending; +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ + /*! \brief Inbound FACILITY message function type and contents */ struct FacParm fac_in; @@ -421,17 +565,51 @@ */ int stack_holder; - /*! \brief Caller ID presentation restriction code + /*! + * \brief Put a display ie in the CONNECT message + * \details + * Put a display ie in the CONNECT message containing the following + * information if it is available (nt port only): + * 0 - Do not put the connected line information in the display ie. + * 1 - Put the available connected line name in the display ie. + * 2 - Put the available connected line number in the display ie. + * 3 - Put the available connected line name and number in the display ie. + */ + int display_connected; + + /*! + * \brief Put a display ie in the SETUP message + * \details + * Put a display ie in the SETUP message containing the following + * information if it is available (nt port only): + * 0 - Do not put the caller information in the display ie. + * 1 - Put the available caller name in the display ie. + * 2 - Put the available caller number in the display ie. + * 3 - Put the available caller name and number in the display ie. + */ + int display_setup; + + /*! + * \brief Select what to do with outgoing COLP information. + * \details + * 0 - pass (Send out COLP information unaltered.) + * 1 - restricted (Force COLP to restricted on all outgoing COLP information.) + * 2 - block (Do not send COLP information.) + */ + int outgoing_colp; + + /*! \brief User set presentation restriction code * 0=Allowed, 1=Restricted, 2=Unavailable * \note It is settable by the misdn_set_opt() application. */ - int pres; + int presentation; - /*! \brief Caller ID screening code - * 0=Unscreened, 1=Passed Screen, 2=Failed Screen, 3=Network Number - */ - int screen; + /*! \brief TRUE if the user set the presentation restriction code */ + int set_presentation; + /*! \brief Notification indicator ie description code */ + enum mISDN_NOTIFY_CODE notify_description_code; + /*! \brief SETUP message bearer capability field code value */ int capability; @@ -468,40 +646,18 @@ */ char display[84]; - /*! \brief Not used. Contents are setup but not used. */ - char msn[32]; - - /*! \brief Originating/Calling Phone Number (Address) - * \note This value can be set to "callerid" in /etc/asterisk/misdn.conf for outgoing calls - */ - char oad[32]; - - /*! \brief Redirecting Phone Number (Address) where a call diversion or transfer was invoked */ - char rad[32]; - - /*! \brief Dialed/Called Phone Number (Address) */ - char dad[32]; - - /*! \brief Connected Party/Line Phone Number (Address) */ - char cad[32]; - - /*! \brief Original Dialed/Called Phone Number (Address) before national/international dialing prefix added. - * \note Not used. Contents are setup but not used. - */ - char orig_dad[32]; - /*! \brief Q.931 Keypad Facility IE contents * \note Contents exported and imported to Asterisk variable MISDN_KEYPAD */ - char keypad[32]; + char keypad[MISDN_MAX_KEYPAD_LEN]; /*! \brief Current overlap dialing digits to/from INFORMATION messages */ - char info_dad[64]; + char info_dad[MISDN_MAX_NUMBER_LEN]; /*! \brief Collected digits to go into info_dad[] while waiting for a SETUP_ACKNOWLEDGE to come in. */ - char infos_pending[64]; + char infos_pending[MISDN_MAX_NUMBER_LEN]; -/* unsigned char info_keypad[32]; */ +/* unsigned char info_keypad[MISDN_MAX_KEYPAD_LEN]; */ /* unsigned char clisub[24]; */ /* unsigned char cldsub[24]; */ @@ -600,6 +756,9 @@ void misdn_lib_transfer(struct misdn_bchannel* holded_bc); struct misdn_bchannel* misdn_lib_get_free_bc(int port, int channel, int inout, int dec); +#if defined(AST_MISDN_ENHANCEMENTS) +struct misdn_bchannel *misdn_lib_get_register_bc(int port); +#endif /* defined(AST_MISDN_ENHANCEMENTS) */ void manager_bchannel_activate(struct misdn_bchannel *bc); void manager_bchannel_deactivate(struct misdn_bchannel * bc); @@ -679,4 +838,4 @@ void misdn_make_dummy(struct misdn_bchannel *dummybc, int port, int l3id, int nt, int channel); -#endif +#endif /* TE_LIB */ Index: channels/misdn/Makefile =================================================================== --- a/channels/misdn/Makefile (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/misdn/Makefile (.../trunk) (revision 202568) @@ -13,5 +13,5 @@ portinfo: portinfo.o $(CC) -o $@ $^ -lisdnnet -lmISDN -lpthread -clean: +clean: rm -rf *.a *.o *.so portinfo *.i Index: channels/misdn/chan_misdn_config.h =================================================================== --- a/channels/misdn/chan_misdn_config.h (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/misdn/chan_misdn_config.h (.../trunk) (revision 202568) @@ -11,7 +11,7 @@ * the GNU General Public License */ -/*! \file +/*! \file * \brief Interface to mISDN - Config * \author Christian Richter */ @@ -46,10 +46,16 @@ MISDN_CFG_DIALPLAN, /* int */ MISDN_CFG_LOCALDIALPLAN, /* int */ MISDN_CFG_CPNDIALPLAN, /* int */ - MISDN_CFG_NATPREFIX, /* char[] */ - MISDN_CFG_INTERNATPREFIX, /* char[] */ + MISDN_CFG_TON_PREFIX_UNKNOWN, /* char[] */ + MISDN_CFG_TON_PREFIX_INTERNATIONAL, /* char[] */ + MISDN_CFG_TON_PREFIX_NATIONAL, /* char[] */ + MISDN_CFG_TON_PREFIX_NETWORK_SPECIFIC,/* char[] */ + MISDN_CFG_TON_PREFIX_SUBSCRIBER, /* char[] */ + MISDN_CFG_TON_PREFIX_ABBREVIATED, /* char[] */ MISDN_CFG_PRES, /* int */ MISDN_CFG_SCREEN, /* int */ + MISDN_CFG_DISPLAY_CONNECTED, /* int */ + MISDN_CFG_DISPLAY_SETUP, /* int */ MISDN_CFG_ALWAYS_IMMEDIATE, /* int (bool) */ MISDN_CFG_NODIALTONE, /* int (bool) */ MISDN_CFG_IMMEDIATE, /* int (bool) */ @@ -59,6 +65,8 @@ MISDN_CFG_EARLY_BCONNECT, /* int (bool) */ MISDN_CFG_INCOMING_EARLY_AUDIO, /* int (bool) */ MISDN_CFG_ECHOCANCEL, /* int */ + MISDN_CFG_CC_REQUEST_RETENTION,/* bool */ + MISDN_CFG_OUTGOING_COLP, /* int */ #ifdef MISDN_1_2 MISDN_CFG_PIPELINE, /* char[] */ #endif @@ -89,7 +97,7 @@ MISDN_CFG_FAXDETECT_TIMEOUT, /* int */ MISDN_CFG_PTP, /* int (bool) */ MISDN_CFG_LAST, - + /* general config items */ MISDN_GEN_FIRST, #ifndef MISDN_1_2 @@ -116,19 +124,19 @@ }; /* you must call misdn_cfg_init before any other function of this header file */ -int misdn_cfg_init(int max_ports, int reload); +int misdn_cfg_init(int max_ports, int reload); void misdn_cfg_reload(void); void misdn_cfg_destroy(void); void misdn_cfg_update_ptp( void ); -/* if you requst a general config element, the port value is ignored. if the requested - * value is not available, or the buffer is too small, the buffer will be nulled (in +/* if you requst a general config element, the port value is ignored. if the requested + * value is not available, or the buffer is too small, the buffer will be nulled (in * case of a char* only its first byte will be nulled). */ void misdn_cfg_get(int port, enum misdn_cfg_elements elem, void* buf, int bufsize); /* returns the enum element for the given name, returns MISDN_CFG_FIRST if none was found */ -enum misdn_cfg_elements misdn_cfg_get_elem (char *name); +enum misdn_cfg_elements misdn_cfg_get_elem (const char *name); /* fills the buffer with the name of the given config element */ void misdn_cfg_get_name (enum misdn_cfg_elements elem, void *buf, int bufsize); Index: channels/misdn/ie.c =================================================================== --- a/channels/misdn/ie.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/misdn/ie.c (.../trunk) (revision 202568) @@ -1,4 +1,3 @@ - /* * Chan_Misdn -- Channel Driver for Asterisk * @@ -15,7 +14,7 @@ * the GNU General Public License */ -/*! \file +/*! \file * \brief Interface to mISDN * \author Christian Richter */ @@ -39,11 +38,11 @@ #define MISDN_IE_DEBG 0 /* support stuff */ -static void strnncpy(char *dest, char *src, int len, int dst_len) +static void strnncpy(char *dest, const char *src, size_t len, size_t dst_len) { if (len > dst_len-1) len = dst_len-1; - strncpy((char *)dest, (char *)src, len); + strncpy(dest, src, len); dest[len] = '\0'; } @@ -153,7 +152,7 @@ p[4+(multi>=0)] = 0xa0 + user; } -static void dec_ie_bearer(unsigned char *p, Q931_info_t *qi, int *coding, int *capability, int *mode, int *rate, int *multi, int *user, +static void dec_ie_bearer(unsigned char *p, Q931_info_t *qi, int *coding, int *capability, int *mode, int *rate, int *multi, int *user, int *async, int *urate, int *stopbits, int *dbits, int *parity, int nt, struct misdn_bchannel *bc) { int octet; @@ -168,13 +167,13 @@ *stopbits = -1; *dbits = -1; *parity = -1; - + if (!nt) { p = NULL; #ifdef LLC_SUPPORT if (qi->QI_ELEMENT(llc)) { - + p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(llc) + 1; } #endif @@ -189,7 +188,7 @@ printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]); return; } - + *coding = (p[1]&0x60) >> 5; *capability = p[1] & 0x1f; octet = 2; @@ -221,7 +220,7 @@ if (p[0] <= octet) goto done; - + if (p[octet++] & 0x80) goto l2; @@ -231,7 +230,7 @@ if (p[0] <= octet) goto done; - + if (p[octet++] & 0x80) goto l2; @@ -239,7 +238,7 @@ if (p[0] <= octet) goto done; - + if (p[octet++] & 0x80) goto l2; @@ -247,20 +246,20 @@ if (p[0] <= octet) goto done; - + if (!p[octet++] & 0x80) goto l2; /* Wheee. V.110 speed information */ *stopbits = (p[octet] & 0x60) >> 5; - *dbits = (p[octet] & 0x18) >> 3; + *dbits = (p[octet] & 0x18) >> 3; *parity = p[octet] & 7; octet++; } l2: /* Nobody seems to want the rest so we don't bother (yet) */ - done: + done: if (MISDN_IE_DEBG) printf(" coding=%d capability=%d mode=%d rate=%d multi=%d user=%d async=%d urate=%d stopbits=%d dbits=%d parity=%d\n", *coding, *capability, *mode, *rate, *multi, *user, *async, *urate, *stopbits, *dbits, *parity); } @@ -292,7 +291,7 @@ if (MISDN_IE_DEBG) printf(debug+(i*3), " %02x", callid[i]); i++; } - + if (MISDN_IE_DEBG) printf(" callid%s\n", debug); l = callid_len; @@ -338,7 +337,7 @@ if (MISDN_IE_DEBG) printf(debug+(i*3), " %02x", callid[i]); i++; } - + if (MISDN_IE_DEBG) printf(" callid%s\n", debug); } #endif @@ -380,7 +379,7 @@ strncpy((char *)p+3, (char *)number, strlen((char *)number)); } -static void dec_ie_called_pn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, char *number, int number_len, int nt, struct misdn_bchannel *bc) +static void dec_ie_called_pn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, char *number, size_t number_len, int nt, struct misdn_bchannel *bc) { *type = -1; *plan = -1; @@ -464,7 +463,7 @@ } } -static void dec_ie_calling_pn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, int *screen, char *number, int number_len, int nt, struct misdn_bchannel *bc) +static void dec_ie_calling_pn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, int *screen, char *number, size_t number_len, int nt, struct misdn_bchannel *bc) { *type = -1; *plan = -1; @@ -501,7 +500,7 @@ } else { strnncpy(number, (char *)p+2, p[0]-1, number_len); - /* SPECIAL workarround for IBT software bug */ + /* SPECIAL workarround for IBT software bug */ /* if (number[0]==0x80) */ /* strcpy((char *)number, (char *)number+1); */ } @@ -566,7 +565,7 @@ } } -static void dec_ie_connected_pn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, int *screen, char *number, int number_len, int nt, struct misdn_bchannel *bc) +static void dec_ie_connected_pn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, int *screen, char *number, size_t number_len, int nt, struct misdn_bchannel *bc) { *type = -1; *plan = -1; @@ -691,7 +690,7 @@ int l; struct misdn_stack *stack=get_stack_by_bc(bc); int pri = stack->pri; - + if (exclusive<0 || exclusive>1) { printf("%s: ERROR: exclusive(%d) is out of range.\n", __FUNCTION__, exclusive); @@ -707,8 +706,8 @@ } /* if (MISDN_IE_DEBG) printf(" exclusive=%d channel=%d\n", exclusive, channel); */ - + if (!pri) { /* BRI */ @@ -884,7 +883,7 @@ static void enc_ie_display(unsigned char **ntmode, msg_t *msg, char *display, int nt, struct misdn_bchannel *bc) { unsigned char *p; - Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); + Q931_info_t *qi = (Q931_info_t *) (msg->data + mISDN_HEADER_LEN); int l; if (!display[0]) @@ -893,27 +892,28 @@ return; } - if (strlen((char *)display) > 80) + l = strlen(display); + if (80 < l) { - printf("%s: WARNING: display text too long (max 80 chars), cutting.\n", __FUNCTION__); - display[80] = '\0'; + l = 80; + printf("%s: WARNING: display text too long (max %d chars), cutting.\n", __FUNCTION__, l); + display[l] = '\0'; } - /* if (MISDN_IE_DEBG) printf(" display='%s' (len=%d)\n", display, strlen((char *)display)); */ + /* if (MISDN_IE_DEBG) printf(" display='%s' (len=%d)\n", display, l); */ - l = strlen((char *)display); - p = msg_put(msg, l+2); + p = msg_put(msg, l + 2); if (nt) - *ntmode = p+1; + *ntmode = p + 1; else - qi->QI_ELEMENT(display) = p - (unsigned char *)qi - sizeof(Q931_info_t); + qi->QI_ELEMENT(display) = p - (unsigned char *) qi - sizeof(Q931_info_t); p[0] = IE_DISPLAY; p[1] = l; - strncpy((char *)p+2, (char *)display, strlen((char *)display)); + strncpy((char *) p + 2, display, l); } #if 0 -static void dec_ie_display(unsigned char *p, Q931_info_t *qi, char *display, int display_len, int nt, struct misdn_bchannel *bc) +static void dec_ie_display(unsigned char *p, Q931_info_t *qi, char *display, size_t display_len, int nt, struct misdn_bchannel *bc) { *display = '\0'; @@ -965,7 +965,7 @@ } #endif -static void dec_ie_keypad(unsigned char *p, Q931_info_t *qi, char *keypad, int keypad_len, int nt, struct misdn_bchannel *bc) +static void dec_ie_keypad(unsigned char *p, Q931_info_t *qi, char *keypad, size_t keypad_len, int nt, struct misdn_bchannel *bc) { *keypad = '\0'; @@ -990,7 +990,6 @@ /* IE_NOTIFY */ -#if 0 static void enc_ie_notify(unsigned char **ntmode, msg_t *msg, int notify, int nt, struct misdn_bchannel *bc) { unsigned char *p; @@ -1015,9 +1014,7 @@ p[1] = l; p[2] = 0x80 + notify; } -#endif -#if 0 static void dec_ie_notify(unsigned char *p, Q931_info_t *qi, int *notify, int nt, struct misdn_bchannel *bc) { *notify = -1; @@ -1040,7 +1037,6 @@ if (MISDN_IE_DEBG) printf(" notify=%d\n", *notify); } -#endif /* IE_PROGRESS */ @@ -1086,7 +1082,7 @@ *location = -1; //*progress = -1; *progress = 0; - + if (!nt) { p = NULL; @@ -1184,7 +1180,7 @@ } } -static void dec_ie_redir_nr(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, int *screen, int *reason, char *number, int number_len, int nt, struct misdn_bchannel *bc) +static void dec_ie_redir_nr(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, int *screen, int *reason, char *number, size_t number_len, int nt, struct misdn_bchannel *bc) { *type = -1; *plan = -1; @@ -1231,11 +1227,10 @@ /* IE_REDIR_DN (redirection = during MT_NOTIFY) */ -#if 0 static void enc_ie_redir_dn(unsigned char **ntmode, msg_t *msg, int type, int plan, int present, char *number, int nt, struct misdn_bchannel *bc) { unsigned char *p; -/* Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); */ + Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); int l; if (type<0 || type>7) @@ -1264,9 +1259,9 @@ p = msg_put(msg, l+2); if (nt) *ntmode = p+1; - else -/* #warning REINSERT redir_dn, when included in te-mode */ - /*qi->QI_ELEMENT(redir_dn) = p - (unsigned char *)qi - sizeof(Q931_info_t)*/; + else { + qi->QI_ELEMENT(redirect_dn) = p - (unsigned char *)qi - sizeof(Q931_info_t); + } p[0] = IE_REDIR_DN; p[1] = l; if (present >= 0) @@ -1282,10 +1277,8 @@ strncpy((char *)p+3, (char *)number, strlen((char *)number)); } } -#endif -#if 0 -static void dec_ie_redir_dn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, char *number, int number_len, int nt, struct misdn_bchannel *bc) +static void dec_ie_redir_dn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, char *number, size_t number_len, int nt, struct misdn_bchannel *bc) { *type = -1; *plan = -1; @@ -1295,9 +1288,8 @@ if (!nt) { p = NULL; -/* #warning REINSERT redir_dn, when included in te-mode */ -/* if (qi->QI_ELEMENT(redir_dn)) */ -/* p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(redir_dn) + 1; */ + if (qi->QI_ELEMENT(redirect_dn)) + p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(redirect_dn) + 1; } if (!p) return; @@ -1320,7 +1312,6 @@ if (MISDN_IE_DEBG) printf(" type=%d plan=%d present=%d number='%s'\n", *type, *plan, *present, number); } -#endif /* IE_USERUSER */ @@ -1331,9 +1322,6 @@ Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); int l; - char debug[768]; - int i; - if (protocol<0 || protocol>127) { printf("%s: ERROR: protocol(%d) is out of range.\n", __FUNCTION__, protocol); @@ -1344,14 +1332,16 @@ return; } - i = 0; - while(i < user_len) - { - if (MISDN_IE_DEBG) sprintf(debug+(i*3), " %02x", user[i]); - i++; + if (MISDN_IE_DEBG) { + size_t i; + char debug[768]; + + for (i = 0; i < user_len; ++i) { + sprintf(debug + (i * 3), " %02x", user[i]); + } + debug[i * 3] = 0; + printf(" protocol=%d user-user%s\n", protocol, debug); } - - if (MISDN_IE_DEBG) printf(" protocol=%d user-user%s\n", protocol, debug); l = user_len+1; p = msg_put(msg, l+3); @@ -1369,9 +1359,6 @@ #if 1 static void dec_ie_useruser(unsigned char *p, Q931_info_t *qi, int *protocol, char *user, int *user_len, int nt, struct misdn_bchannel *bc) { - char debug[768]; - int i; - *user_len = 0; *protocol = -1; @@ -1390,15 +1377,16 @@ *protocol = p[1]; memcpy(user, p+2, (*user_len<=128)?*(user_len):128); /* clip to 128 maximum */ - i = 0; - while(i < *user_len) - { - if (MISDN_IE_DEBG) sprintf(debug+(i*3), " %02x", user[i]); - i++; + if (MISDN_IE_DEBG) { + int i; + char debug[768]; + + for (i = 0; i < *user_len; ++i) { + sprintf(debug + (i * 3), " %02x", user[i]); + } + debug[i * 3] = 0; + printf(" protocol=%d user-user%s\n", *protocol, debug); } - debug[i*3] = '\0'; - - if (MISDN_IE_DEBG) printf(" protocol=%d user-user%s\n", *protocol, debug); } #endif Index: channels/chan_gtalk.c =================================================================== --- a/channels/chan_gtalk.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/chan_gtalk.c (.../trunk) (revision 202568) @@ -52,7 +52,8 @@ #include "asterisk/pbx.h" #include "asterisk/sched.h" #include "asterisk/io.h" -#include "asterisk/rtp.h" +#include "asterisk/rtp_engine.h" +#include "asterisk/stun.h" #include "asterisk/acl.h" #include "asterisk/callerid.h" #include "asterisk/file.h" @@ -112,8 +113,8 @@ char cid_name[80]; /*!< Caller ID name */ char exten[80]; /*!< Called extension */ struct ast_channel *owner; /*!< Master Channel */ - struct ast_rtp *rtp; /*!< RTP audio session */ - struct ast_rtp *vrtp; /*!< RTP video session */ + struct ast_rtp_instance *rtp; /*!< RTP audio session */ + struct ast_rtp_instance *vrtp; /*!< RTP video session */ int jointcapability; /*!< Supported capability at both ends (codecs ) */ int peercapability; struct gtalk_pvt *next; /* Next entity */ @@ -183,11 +184,6 @@ static struct gtalk_pvt *gtalk_alloc(struct gtalk *client, const char *us, const char *them, const char *sid); static char *gtalk_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); static char *gtalk_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); -/*----- RTP interface functions */ -static int gtalk_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, - struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active); -static enum ast_rtp_get_result gtalk_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); -static int gtalk_get_codec(struct ast_channel *chan); /*! \brief PBX interface structure for channel registration */ static const struct ast_channel_tech gtalk_tech = { @@ -197,7 +193,7 @@ .requester = gtalk_request, .send_digit_begin = gtalk_digit_begin, .send_digit_end = gtalk_digit_end, - .bridge = ast_rtp_bridge, + .bridge = ast_rtp_instance_bridge, .call = gtalk_call, .hangup = gtalk_hangup, .answer = gtalk_answer, @@ -216,14 +212,6 @@ static struct io_context *io; /*!< The IO context */ static struct in_addr __ourip; -/*! \brief RTP driver interface */ -static struct ast_rtp_protocol gtalk_rtp = { - type: "Gtalk", - get_rtp_info: gtalk_get_rtp_peer, - set_rtp_peer: gtalk_set_rtp_peer, - get_codec: gtalk_get_codec, -}; - static struct ast_cli_entry gtalk_cli[] = { AST_CLI_DEFINE(gtalk_do_reload, "Reload GoogleTalk configuration"), AST_CLI_DEFINE(gtalk_show_channels, "Show GoogleTalk channels"), @@ -371,7 +359,7 @@ iks_insert_node(dcodecs, payload_gsm); res++; } - ast_rtp_lookup_code(p->rtp, 1, codec); + return res; } @@ -523,18 +511,19 @@ return res; } -static enum ast_rtp_get_result gtalk_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) +static enum ast_rtp_glue_result gtalk_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) { struct gtalk_pvt *p = chan->tech_pvt; - enum ast_rtp_get_result res = AST_RTP_GET_FAILED; + enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_FORBID; if (!p) return res; ast_mutex_lock(&p->lock); if (p->rtp){ - *rtp = p->rtp; - res = AST_RTP_TRY_PARTIAL; + ao2_ref(p->rtp, +1); + *instance = p->rtp; + res = AST_RTP_GLUE_RESULT_LOCAL; } ast_mutex_unlock(&p->lock); @@ -547,7 +536,7 @@ return p->peercapability; } -static int gtalk_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) +static int gtalk_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *trtp, int codecs, int nat_active) { struct gtalk_pvt *p; @@ -567,6 +556,13 @@ return 0; } +static struct ast_rtp_glue gtalk_rtp_glue = { + .type = "Gtalk", + .get_rtp_info = gtalk_get_rtp_peer, + .get_codec = gtalk_get_codec, + .update_peer = gtalk_set_rtp_peer, +}; + static int gtalk_response(struct gtalk *client, char *from, ikspak *pak, const char *reasonstr, const char *reasonstr2) { iks *response = NULL, *error = NULL, *reason = NULL; @@ -617,13 +613,13 @@ /* codec points to the first tag */ codec = iks_first_tag(iks_first_tag(iks_first_tag(pak->x))); while (codec) { - ast_rtp_set_m_type(tmp->rtp, atoi(iks_find_attrib(codec, "id"))); - ast_rtp_set_rtpmap_type(tmp->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); + ast_rtp_codecs_payloads_set_m_type(ast_rtp_instance_get_codecs(tmp->rtp), tmp->rtp, atoi(iks_find_attrib(codec, "id"))); + ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(tmp->rtp), tmp->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); codec = iks_next_tag(codec); } /* Now gather all of the codecs that we are asked for */ - ast_rtp_get_current_formats(tmp->rtp, &tmp->peercapability, &peernoncodeccapability); + ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(tmp->rtp), &tmp->peercapability, &peernoncodeccapability); /* at this point, we received an awser from the remote Gtalk client, which allows us to compare capabilities */ @@ -774,7 +770,7 @@ struct gtalk_candidate *tmp; struct aji_client *c = client->connection; struct gtalk_candidate *ours1 = NULL, *ours2 = NULL; - struct sockaddr_in sin; + struct sockaddr_in sin = { 0, }; struct sockaddr_in dest; struct in_addr us; iks *iq, *gtalk, *candidate, *transport; @@ -810,7 +806,7 @@ goto safeout; } - ast_rtp_get_us(p->rtp, &sin); + ast_rtp_instance_get_local_address(p->rtp, &sin); ast_find_ourip(&us, bindaddr); if (!strcmp(ast_inet_ntoa(us), "127.0.0.1")) { ast_log(LOG_WARNING, "Found a loopback IP on the system, check your network configuration or set the bindaddr attribute."); @@ -951,8 +947,13 @@ tmp->initiator = 1; } /* clear codecs */ - tmp->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); - ast_rtp_pt_clear(tmp->rtp); + if (!(tmp->rtp = ast_rtp_instance_new(NULL, sched, &bindaddr, NULL))) { + ast_log(LOG_ERROR, "Failed to create a new RTP instance (possibly an invalid bindaddr?)\n"); + ast_free(tmp); + return NULL; + } + ast_rtp_instance_set_prop(tmp->rtp, AST_RTP_PROPERTY_RTCP, 1); + ast_rtp_codecs_payloads_clear(ast_rtp_instance_get_codecs(tmp->rtp), tmp->rtp); /* add user configured codec capabilites */ if (client->capability) @@ -1014,20 +1015,20 @@ /* Set Frame packetization */ if (i->rtp) - ast_rtp_codec_setpref(i->rtp, &i->prefs); + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(i->rtp), i->rtp, &i->prefs); tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) | (i->jointcapability & AST_FORMAT_VIDEO_MASK); fmt = ast_best_codec(tmp->nativeformats); if (i->rtp) { - ast_rtp_setstun(i->rtp, 1); - ast_channel_set_fd(tmp, 0, ast_rtp_fd(i->rtp)); - ast_channel_set_fd(tmp, 1, ast_rtcp_fd(i->rtp)); + ast_rtp_instance_set_prop(i->rtp, AST_RTP_PROPERTY_STUN, 1); + ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(i->rtp, 0)); + ast_channel_set_fd(tmp, 1, ast_rtp_instance_fd(i->rtp, 1)); } if (i->vrtp) { - ast_rtp_setstun(i->rtp, 1); - ast_channel_set_fd(tmp, 2, ast_rtp_fd(i->vrtp)); - ast_channel_set_fd(tmp, 3, ast_rtcp_fd(i->vrtp)); + ast_rtp_instance_set_prop(i->vrtp, AST_RTP_PROPERTY_STUN, 1); + ast_channel_set_fd(tmp, 2, ast_rtp_instance_fd(i->vrtp, 0)); + ast_channel_set_fd(tmp, 3, ast_rtp_instance_fd(i->vrtp, 1)); } if (state == AST_STATE_RING) tmp->rings = 1; @@ -1142,9 +1143,9 @@ if (p->owner) ast_log(LOG_WARNING, "Uh oh, there's an owner, this is going to be messy.\n"); if (p->rtp) - ast_rtp_destroy(p->rtp); + ast_rtp_instance_destroy(p->rtp); if (p->vrtp) - ast_rtp_destroy(p->vrtp); + ast_rtp_instance_destroy(p->vrtp); gtalk_free_candidates(p->theircandidates); ast_free(p); } @@ -1207,13 +1208,13 @@ codec = iks_first_tag(iks_first_tag(iks_first_tag(pak->x))); while (codec) { - ast_rtp_set_m_type(p->rtp, atoi(iks_find_attrib(codec, "id"))); - ast_rtp_set_rtpmap_type(p->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); + ast_rtp_codecs_payloads_set_m_type(ast_rtp_instance_get_codecs(p->rtp), p->rtp, atoi(iks_find_attrib(codec, "id"))); + ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(p->rtp), p->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); codec = iks_next_tag(codec); } /* Now gather all of the codecs that we are asked for */ - ast_rtp_get_current_formats(p->rtp, &p->peercapability, &peernoncodeccapability); + ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(p->rtp), &p->peercapability, &peernoncodeccapability); p->jointcapability = p->capability & p->peercapability; ast_mutex_unlock(&p->lock); @@ -1226,7 +1227,7 @@ gtalk_action(client, p, "reject"); p->alreadygone = 1; gtalk_hangup(chan); - ast_channel_free(chan); + ast_channel_release(chan); return -1; } @@ -1257,8 +1258,8 @@ struct gtalk_candidate *tmp; struct hostent *hp; struct ast_hostent ahp; - struct sockaddr_in sin; - struct sockaddr_in aux; + struct sockaddr_in sin = { 0, }; + struct sockaddr_in aux = { 0, }; if (time(NULL) == p->laststun) return 0; @@ -1277,16 +1278,16 @@ p->ourcandidates->username); /* Find out the result of the STUN */ - ast_rtp_get_peer(p->rtp, &aux); + ast_rtp_instance_get_remote_address(p->rtp, &aux); /* If the STUN result is different from the IP of the hostname, lock on the stun IP of the hostname advertised by the remote client */ if (aux.sin_addr.s_addr && aux.sin_addr.s_addr != sin.sin_addr.s_addr) - ast_rtp_stun_request(p->rtp, &aux, username); + ast_rtp_instance_stun_request(p->rtp, &aux, username); else - ast_rtp_stun_request(p->rtp, &sin, username); + ast_rtp_instance_stun_request(p->rtp, &sin, username); if (aux.sin_addr.s_addr) { ast_debug(4, "Receiving RTP traffic from IP %s, matches with remote candidate's IP %s\n", ast_inet_ntoa(aux.sin_addr), tmp->ip); @@ -1387,7 +1388,7 @@ if (!p->rtp) return &ast_null_frame; - f = ast_rtp_read(p->rtp); + f = ast_rtp_instance_read(p->rtp, 0); gtalk_update_stun(p->parent, p); if (p->owner) { /* We already hold the channel lock */ @@ -1438,7 +1439,7 @@ if (p) { ast_mutex_lock(&p->lock); if (p->rtp) { - res = ast_rtp_write(p->rtp, frame); + res = ast_rtp_instance_write(p->rtp, frame); } ast_mutex_unlock(&p->lock); } @@ -1447,7 +1448,7 @@ if (p) { ast_mutex_lock(&p->lock); if (p->vrtp) { - res = ast_rtp_write(p->vrtp, frame); + res = ast_rtp_instance_write(p->vrtp, frame); } ast_mutex_unlock(&p->lock); } @@ -2062,7 +2063,7 @@ return 0; } - ast_rtp_proto_register(>alk_rtp); + ast_rtp_glue_register(>alk_rtp_glue); ast_cli_register_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli)); /* Make sure we can register our channel type */ @@ -2086,7 +2087,7 @@ ast_cli_unregister_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli)); /* First, take us out of the channel loop */ ast_channel_unregister(>alk_tech); - ast_rtp_proto_unregister(>alk_rtp); + ast_rtp_glue_unregister(>alk_rtp_glue); if (!ast_mutex_lock(>alklock)) { /* Hangup all interfaces if they have an owner */ Index: channels/iax2-parser.c =================================================================== --- a/channels/iax2-parser.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/channels/iax2-parser.c (.../trunk) (revision 202568) @@ -279,7 +279,7 @@ { IAX_IE_OSPTOKEN, "OSPTOKEN" }, }; -static struct iax2_ie prov_ies[] = { +static const struct iax2_ie prov_ies[] = { { PROV_IE_USEDHCP, "USEDHCP" }, { PROV_IE_IPADDR, "IPADDR", dump_ipaddr }, { PROV_IE_SUBNET, "SUBNET", dump_ipaddr }, @@ -576,6 +576,9 @@ "VIDUPDT", "T38 ", "SRCUPDT", + "TXFER ", + "CNLINE ", + "REDIR ", }; struct ast_iax2_full_hdr *fh; char retries[20]; Index: CREDITS =================================================================== --- a/CREDITS (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/CREDITS (.../trunk) (revision 202568) @@ -200,6 +200,10 @@ Eliel C. Sardanons - XML documentation implementation, and various other contributions eliels(AT)gmail.com +Sean Bright - Snom call pickup, newt interface for menuselect, cdr_tds rewrite, + countless other improvements, fixes, and good ideas. + sean(AT)malleable.com + === OTHER CONTRIBUTIONS === John Todd - Monkey sounds and associated teletorture prompt Michael Jerris - bug marshaling Index: BUGS =================================================================== --- a/BUGS (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/BUGS (.../trunk) (revision 202568) @@ -4,7 +4,7 @@ To learn about and report Asterisk bugs, please visit the official Asterisk Bug Tracker at: - http://bugs.digium.com + https://issues.asterisk.org For more information on using the bug tracker, or to learn how you can contribute by acting as a bug marshal Index: tests/test_logger.c =================================================================== --- a/tests/test_logger.c (.../tags/1.6.2.0-beta3) (revision 0) +++ b/tests/test_logger.c (.../trunk) (revision 202568) @@ -0,0 +1,209 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2009, Digium, Inc. + * + * Kevin P. Fleming + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Test module for the logging subsystem + * + * \author\verbatim Kevin P. Fleming \endverbatim + * + * \ingroup tests + */ + +/*** MODULEINFO + no + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/file.h" +#include "asterisk/channel.h" +#include "asterisk/pbx.h" +#include "asterisk/module.h" +#include "asterisk/lock.h" +#include "asterisk/app.h" +#include "asterisk/cli.h" + +struct test { + const char *name; + unsigned int x_success; + unsigned int x_failure; + unsigned int u_success; + unsigned int u_failure; +}; + +static void output_tests(struct test *tests, size_t num_tests, int fd) +{ + unsigned int x; + + for (x = 0; x < num_tests; x++) { + ast_cli(fd, "Test %d: %s\n", x + 1, tests[x].name); + ast_cli(fd, "\tExpected Successes: %d\n", tests[x].x_success); + ast_cli(fd, "\tExpected Failures: %d\n", tests[x].x_failure); + ast_cli(fd, "\tUnexpected Successes: %d\n", tests[x].u_success); + ast_cli(fd, "\tUnexpected Failures: %d\n", tests[x].u_failure); + ast_cli(fd, "Test %d Result: %s\n", x + 1, (tests[x].u_success + tests[x].u_failure) ? "FAIL" : "PASS"); + } +} + +static char *handle_cli_dynamic_level_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + unsigned int level; + unsigned int x; + unsigned int test; + struct test tests[] = { + { .name = "Simple register/message/unregister", + }, + { .name = "Register multiple levels", + }, + }; + + switch (cmd) { + case CLI_INIT: + e->command = "logger test dynamic"; + e->usage = "" + "Usage: logger test dynamic\n" + ""; + return NULL; + case CLI_GENERATE: + return NULL; + } + + for (test = 0; test < ARRAY_LEN(tests); test++) { + ast_cli(a->fd, "Test %d: %s.\n", test + 1, tests[test].name); + switch (test) { + case 0: + if ((level = ast_logger_register_level("test")) != -1) { + ast_cli(a->fd, "Test: got level %d\n", level); + ast_log_dynamic_level(level, "Logger Dynamic Test: Test 1\n"); + ast_logger_unregister_level("test"); + tests[test].x_success++; + } else { + ast_cli(a->fd, "Test: Failed, could not register level 'test'.\n"); + tests[test].u_failure++; + } + break; + case 1: + { + char level_name[18][8]; + + for (x = 0; x < ARRAY_LEN(level_name); x++) { + sprintf(level_name[x], "level%02d", x); + if ((level = ast_logger_register_level(level_name[x])) == -1) { + if (x < 16) { + tests[test].u_failure++; + } else { + tests[test].x_failure++; + } + level_name[x][0] = '\0'; + } else { + ast_cli(a->fd, "Test: registered '%s', got level %d\n", level_name[x], level); + if (x < 16) { + tests[test].x_success++; + } else { + tests[test].u_success++; + } + } + } + + for (x = 0; x < ARRAY_LEN(level_name); x++) { + if (!ast_strlen_zero(level_name[x])) { + ast_logger_unregister_level(level_name[x]); + } + } + } + } + } + + output_tests(tests, ARRAY_LEN(tests), a->fd); + + return CLI_SUCCESS; +} + +static char *handle_cli_performance_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + unsigned int level; + unsigned int test; + struct test tests[] = { + { .name = "Log 10,000 messages", + }, + }; + + switch (cmd) { + case CLI_INIT: + e->command = "logger test performance"; + e->usage = "" + "Usage: logger test performance\n" + ""; + return NULL; + case CLI_GENERATE: + return NULL; + } + + for (test = 0; test < ARRAY_LEN(tests); test++) { + ast_cli(a->fd, "Test %d: %s.\n", test + 1, tests[test].name); + switch (test) { + case 0: + if ((level = ast_logger_register_level("perftest")) != -1) { + unsigned int x; + struct timeval start, end; + int elapsed; + + ast_cli(a->fd, "Test: got level %d\n", level); + start = ast_tvnow(); + for (x = 0; x < 10000; x++) { + ast_log_dynamic_level(level, "Performance test log message\n"); + } + end = ast_tvnow(); + elapsed = ast_tvdiff_ms(end, start); + ast_cli(a->fd, "Test: 10,000 messages in %f seconds.\n", (float) elapsed / 1000); + ast_logger_unregister_level("perftest"); + tests[test].x_success++; + } else { + ast_cli(a->fd, "Test: Failed, could not register level 'perftest'.\n"); + tests[test].u_failure++; + } + break; + } + } + + output_tests(tests, ARRAY_LEN(tests), a->fd); + + return CLI_SUCCESS; +} + +static struct ast_cli_entry cli_logger[] = { + AST_CLI_DEFINE(handle_cli_dynamic_level_test, "Test the dynamic logger level implementation"), + AST_CLI_DEFINE(handle_cli_performance_test, "Test the logger performance"), +}; + +static int unload_module(void) +{ + ast_cli_unregister_multiple(cli_logger, ARRAY_LEN(cli_logger)); + return 0; +} + +static int load_module(void) +{ + ast_cli_register_multiple(cli_logger, ARRAY_LEN(cli_logger)); + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Logger Test Module"); Property changes on: tests/test_logger.c ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + Author Date Id Revision Index: tests/test_sched.c =================================================================== --- a/tests/test_sched.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/tests/test_sched.c (.../trunk) (revision 202568) @@ -168,7 +168,7 @@ case CLI_INIT: e->command = "sched benchmark"; e->usage = "" - "Usage: sched test \n" + "Usage: sched benchmark \n" ""; return NULL; case CLI_GENERATE: Index: tests/test_substitution.c =================================================================== --- a/tests/test_substitution.c (.../tags/1.6.2.0-beta3) (revision 0) +++ b/tests/test_substitution.c (.../trunk) (revision 202568) @@ -0,0 +1,241 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2009, Digium, Inc. + * + * Tilghman Lesher + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Substitution Test + * + * \author\verbatim Tilghman Lesher \endverbatim + * + * \ingroup tests + */ + +/*** MODULEINFO + no + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/file.h" +#include "asterisk/channel.h" +#include "asterisk/pbx.h" +#include "asterisk/module.h" +#include "asterisk/lock.h" +#include "asterisk/app.h" +#include "asterisk/strings.h" +#include "asterisk/stringfields.h" +#include "asterisk/threadstorage.h" +#include "asterisk/cli.h" + +AST_THREADSTORAGE(buf_buf); +AST_THREADSTORAGE(var_buf); + +static void test_chan_integer(int fd, struct ast_channel *c, int *ifield, const char *expression) +{ + int i, okay = 1, value1 = -1, value2 = -1; + char workspace[4096]; + struct ast_str *str = ast_str_thread_get(&buf_buf, 16); + + for (i = 0; i < 256; i++) { + *ifield = i; + ast_str_substitute_variables(&str, 0, c, expression); + pbx_substitute_variables_helper(c, expression, workspace, sizeof(workspace)); + if (sscanf(workspace, "%d", &value1) != 1 || value1 != i || sscanf(ast_str_buffer(str), "%d", &value2) != 1 || value2 != i) { + ast_cli(fd, "%s != %s and/or %d != %d != %d\n", ast_str_buffer(str), workspace, value1, value2, i); + okay = 0; + break; + } + } + ast_cli(fd, "Testing '%s' . . . . . %s\n", expression, okay ? "passed" : "FAILED"); +} + +static void test_chan_string(int fd, struct ast_channel *c, char *cfield, size_t cfieldsize, const char *expression) +{ + const char *values[] = { "one", "three", "reallylongdinosaursoundingthingwithwordsinit" }; + int i, okay = 1; + char workspace[4096]; + struct ast_str *str = ast_str_thread_get(&buf_buf, 16); + + for (i = 0; i < ARRAY_LEN(values); i++) { + ast_copy_string(cfield, values[i], cfieldsize); + ast_str_substitute_variables(&str, 0, c, expression); + pbx_substitute_variables_helper(c, expression, workspace, sizeof(workspace)); + if (strcmp(cfield, ast_str_buffer(str)) != 0 || strcmp(cfield, workspace) != 0) { + ast_cli(fd, "%s != %s != %s\n", cfield, ast_str_buffer(str), workspace); + okay = 0; + break; + } + } + ast_cli(fd, "Testing '%s' . . . . . %s\n", expression, okay ? "passed" : "FAILED"); +} + +static void test_chan_variable(int fd, struct ast_channel *c, const char *varname) +{ + const char *values[] = { "one", "three", "reallylongdinosaursoundingthingwithwordsinit" }; + int i, okay = 1; + char workspace[4096]; + struct ast_str *str = ast_str_thread_get(&buf_buf, 16); + struct ast_str *var = ast_str_thread_get(&var_buf, 16); + + ast_str_set(&var, 0, "${%s}", varname); + for (i = 0; i < ARRAY_LEN(values); i++) { + pbx_builtin_setvar_helper(c, varname, values[i]); + ast_str_substitute_variables(&str, 0, c, ast_str_buffer(var)); + pbx_substitute_variables_helper(c, ast_str_buffer(var), workspace, sizeof(workspace)); + if (strcmp(values[i], ast_str_buffer(str)) != 0 || strcmp(values[i], workspace) != 0) { + ast_cli(fd, "%s != %s != %s\n", values[i], ast_str_buffer(str), workspace); + okay = 0; + break; + } + } + ast_cli(fd, "Testing '%s' . . . . . %s\n", ast_str_buffer(var), okay ? "passed" : "FAILED"); +} + +static void test_chan_function(int fd, struct ast_channel *c, const char *expression) +{ + int okay = 1; + char workspace[4096]; + struct ast_str *str = ast_str_thread_get(&buf_buf, 16); + + ast_str_substitute_variables(&str, 0, c, expression); + pbx_substitute_variables_helper(c, expression, workspace, sizeof(workspace)); + if (strcmp(workspace, ast_str_buffer(str)) != 0) { + ast_cli(fd, "%s != %s\n", ast_str_buffer(str), workspace); + okay = 0; + } + ast_cli(fd, "Testing '%s' . . . . . %s\n", expression, okay ? "passed" : "FAILED"); +} + +static void test_2way_function(int fd, struct ast_channel *c, const char *encode1, const char *encode2, const char *decode1, const char *decode2) +{ + struct ast_str *str = ast_str_thread_get(&buf_buf, 16), *expression = ast_str_alloca(120); + + ast_str_set(&expression, 0, "%s%s%s", encode1, "foobarbaz", encode2); + ast_str_substitute_variables(&str, 0, c, ast_str_buffer(expression)); + ast_str_set(&expression, 0, "%s%s%s", decode1, ast_str_buffer(str), decode2); + ast_str_substitute_variables(&str, 0, c, ast_str_buffer(expression)); + ast_cli(fd, "Testing '%s%s' and '%s%s' . . . . . %s\n", encode1, encode2, decode1, decode2, !strcmp(ast_str_buffer(str), "foobarbaz") ? "passed" : "FAILED"); + if (strcmp(ast_str_buffer(str), "foobarbaz")) { + ast_cli(fd, " '%s' != 'foobarbaz'\n", ast_str_buffer(str)); + } +} + +static void test_expected_result(int fd, struct ast_channel *c, const char *expression, const char *result) +{ + struct ast_str *str = ast_str_thread_get(&buf_buf, 16); + ast_str_substitute_variables(&str, 0, c, expression); + ast_cli(fd, "Testing '%s' ('%s') == '%s' . . . . . %s\n", ast_str_buffer(str), expression, result, !strcmp(ast_str_buffer(str), result) ? "passed" : "FAILED"); +} + +static char *handle_cli_test_substitution(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + struct ast_channel *c; + int i; + + switch (cmd) { + case CLI_INIT: + e->command = "test substitution"; + e->usage = "" + "Usage: test substitution\n" + " Test variable and function substitution.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc != e->args) { + return CLI_SHOWUSAGE; + } + + ast_cli(a->fd, "Testing variable substitution ...\n"); + c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Test/substitution"); + + test_chan_integer(a->fd, c, &c->cid.cid_pres, "${CALLINGPRES}"); + test_chan_integer(a->fd, c, &c->cid.cid_ani2, "${CALLINGANI2}"); + test_chan_integer(a->fd, c, &c->cid.cid_ton, "${CALLINGTON}"); + test_chan_integer(a->fd, c, &c->cid.cid_tns, "${CALLINGTNS}"); + test_chan_integer(a->fd, c, &c->hangupcause, "${HANGUPCAUSE}"); + test_chan_integer(a->fd, c, &c->priority, "${PRIORITY}"); + test_chan_string(a->fd, c, c->context, sizeof(c->context), "${CONTEXT}"); + test_chan_string(a->fd, c, c->exten, sizeof(c->exten), "${EXTEN}"); + test_chan_variable(a->fd, c, "CHANNEL(language)"); + test_chan_variable(a->fd, c, "CHANNEL(musicclass)"); + test_chan_variable(a->fd, c, "CHANNEL(parkinglot)"); + test_chan_variable(a->fd, c, "CALLERID(name)"); + test_chan_variable(a->fd, c, "CURLOPT(proxyuserpwd)"); + test_chan_variable(a->fd, c, "CDR(foo)"); + test_chan_variable(a->fd, c, "ENV(foo)"); + test_chan_variable(a->fd, c, "GLOBAL(foo)"); + test_chan_variable(a->fd, c, "GROUP()"); + test_2way_function(a->fd, c, "${AES_ENCRYPT(abcdefghijklmnop,", ")}", "${AES_DECRYPT(abcdefghijklmnop,", ")}"); + test_2way_function(a->fd, c, "${BASE64_ENCODE(", ")}", "${BASE64_DECODE(", ")}"); + pbx_builtin_setvar_helper(c, "foo", "123"); + pbx_builtin_setvar_helper(c, "bar", "foo"); + pbx_builtin_setvar_helper(c, "baz", "fo"); + test_expected_result(a->fd, c, "${foo}${foo}", "123123"); + test_expected_result(a->fd, c, "A${foo}A${foo}A", "A123A123A"); + test_expected_result(a->fd, c, "A${${bar}}A", "A123A"); + test_expected_result(a->fd, c, "A${${baz}o}A", "A123A"); + test_expected_result(a->fd, c, "A${${baz}o:1}A", "A23A"); + test_expected_result(a->fd, c, "A${${baz}o:1:1}A", "A2A"); + test_expected_result(a->fd, c, "A${${baz}o:1:-1}A", "A2A"); + test_expected_result(a->fd, c, "A${${baz}o:-1:1}A", "A3A"); + test_expected_result(a->fd, c, "A${${baz}o:-2:1}A", "A2A"); + test_expected_result(a->fd, c, "A${${baz}o:-2:-1}A", "A2A"); + + /* For testing dialplan functions */ + for (i = 0; ; i++) { + char *cmd = ast_cli_generator("core show function", "", i); + if (cmd == NULL) { + break; + } + if (strcmp(cmd, "CHANNEL") && strcmp(cmd, "CALLERID") && strcmp(cmd, "CURLOPT") && strncmp(cmd, "AES", 3) && strncmp(cmd, "BASE64", 6) && strcmp(cmd, "CDR") && strcmp(cmd, "ENV") && strcmp(cmd, "GLOBAL") && strcmp(cmd, "GROUP") && strcmp(cmd, "CUT") && strcmp(cmd, "LISTFILTER") && strcmp(cmd, "PP_EACH_EXTENSION") && strcmp(cmd, "SET")) { + struct ast_custom_function *acf = ast_custom_function_find(cmd); + if (acf->read && acf->read2) { + char expression[80]; + snprintf(expression, sizeof(expression), "${%s(foo)}", cmd); + test_chan_function(a->fd, c, expression); + } + } + ast_free(cmd); + } + + ast_hangup(c); + + return CLI_SUCCESS; +} + +static struct ast_cli_entry cli_substitution[] = { + AST_CLI_DEFINE(handle_cli_test_substitution, "Test variable substitution"), +}; + +static int unload_module(void) +{ + ast_cli_unregister_multiple(cli_substitution, ARRAY_LEN(cli_substitution)); + return 0; +} + +static int load_module(void) +{ + ast_cli_register_multiple(cli_substitution, ARRAY_LEN(cli_substitution)); + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Substitution tests"); Property changes on: tests/test_substitution.c ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + Author Date Id Revision Index: tests/test_skel.c =================================================================== --- a/tests/test_skel.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/tests/test_skel.c (.../trunk) (revision 202568) @@ -40,6 +40,7 @@ #include "asterisk/module.h" #include "asterisk/lock.h" #include "asterisk/app.h" +#include "asterisk/cli.h" static int unload_module(void) { Index: configure.ac =================================================================== --- a/configure.ac (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/configure.ac (.../trunk) (revision 202568) @@ -244,6 +244,7 @@ AST_EXT_LIB_SETUP([GTK2], [gtk2 libraries], [gtk2]) AST_EXT_LIB_SETUP([GMIME], [GMime library], [gmime]) AST_EXT_LIB_SETUP([HOARD], [Hoard Memory Allocator], [hoard]) +AST_EXT_LIB_SETUP([ICAL], [ical libraries], [ical]) AST_EXT_LIB_SETUP([ICONV], [Iconv Library], [iconv]) AST_EXT_LIB_SETUP([IKSEMEL], [Iksemel Jabber Library], [iksemel]) AST_EXT_LIB_SETUP([IMAP_TK], [UW IMAP Toolkit], [imap]) @@ -258,6 +259,7 @@ AST_EXT_LIB_SETUP([MISDN], [mISDN User Library], [misdn]) AST_EXT_LIB_SETUP([NBS], [Network Broadcast Sound], [nbs]) AST_EXT_LIB_SETUP([NCURSES], [ncurses], [ncurses]) +AST_EXT_LIB_SETUP([NEON], [neon], [neon]) AST_EXT_LIB_SETUP([NETSNMP], [Net-SNMP], [netsnmp]) AST_EXT_LIB_SETUP([NEWT], [newt], [newt]) AST_EXT_LIB_SETUP([OGG], [OGG], [ogg]) @@ -512,9 +514,8 @@ AST_GCC_ATTRIBUTE(deprecated) AST_GCC_ATTRIBUTE(sentinel) AST_GCC_ATTRIBUTE(warn_unused_result) -AST_GCC_ATTRIBUTE(weak) +AST_GCC_ATTRIBUTE(weakref, [weakref("foo")], static) AST_GCC_ATTRIBUTE(weak_import) -AST_GCC_ATTRIBUTE(alias, [alias("foo")]) AC_MSG_CHECKING(for -ffunction-sections support) saved_CFLAGS="${CFLAGS}" @@ -627,6 +628,10 @@ AC_MSG_RESULT(no) ) +AST_C_DEFINE_CHECK([GLOB_NOMAGIC], [GLOB_NOMAGIC], [glob.h]) + +AST_C_DEFINE_CHECK([GLOB_BRACE], [GLOB_BRACE], [glob.h]) + AST_C_DEFINE_CHECK([IP_MTU_DISCOVER], [IP_MTU_DISCOVER], [netinet/in.h]) AC_CHECK_HEADER([libkern/OSAtomic.h], @@ -650,6 +655,8 @@ AST_C_COMPILE_CHECK([DAHDI_LINEREVERSE_VMWI], [struct dahdi_vmwi_info booger], [dahdi/user.h], , [enhanced dahdi vmwi support]) +AST_C_COMPILE_CHECK([DAHDI_ECHOCANCEL_FAX_MODE], [int foo = DAHDI_ECHOCANCEL_FAX_MODE], [dahdi/user.h]) + # BSD might not have exp2, and/or log2 AST_EXT_LIB_CHECK([EXP2L], [m], [exp2l]) AST_EXT_LIB_CHECK([LOG2L], [m], [log2l]) @@ -773,6 +780,8 @@ PBX_ICONV=1 fi +AST_EXT_LIB_CHECK([ICAL], [ical], [icaltimezone_new], [libical/ical.h], [-lpthread]) + AST_EXT_LIB_CHECK([IKSEMEL], [iksemel], [iks_start_sasl], [iksemel.h]) if test "${USE_IMAP_TK}" != "no"; then @@ -1344,6 +1353,8 @@ AST_EXT_LIB_CHECK([NCURSES], [ncurses], [initscr], [curses.h]) +AST_EXT_TOOL_CHECK([NEON], [neon]) + AST_EXT_TOOL_CHECK([NETSNMP], [net-snmp], , [--agent-libs], [#include #include @@ -1422,6 +1433,8 @@ AST_EXT_LIB_CHECK([PRI_INBANDDISCONNECT], [pri], [pri_set_inbanddisconnect], [libpri.h]) +AST_EXT_LIB_CHECK([PRI_SERVICE_MESSAGES], [pri], [pri_maintenance_service], [libpri.h]) + AST_EXT_LIB_CHECK([RESAMPLE], [resample], [resample_open], [libresample.h], [-lm]) AST_C_COMPILE_CHECK([SPANDSP], [ Index: apps/app_dahdiscan.c =================================================================== --- a/apps/app_dahdiscan.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_dahdiscan.c (.../trunk) (revision 202568) @@ -1,378 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * Mark Spencer - * - * Modified from app_zapbarge by David Troy - * - * Special thanks to comphealth.com for sponsoring this - * GPL application. - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \brief DAHDI Scanner - * - * \author Mark Spencer - * - * \ingroup applications - */ - -/*** MODULEINFO - dahdi - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 153365 $") - -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/config.h" -#include "asterisk/app.h" -#include "asterisk/utils.h" -#include "asterisk/cli.h" -#include "asterisk/say.h" -#include "asterisk/options.h" - -/*** DOCUMENTATION - - - Scan DAHDI channels to monitor calls. - - - - Limit scanning to a channel group by setting this option. - - - - Allows a call center manager to monitor DAHDI channels in a - convenient way. Use # to select the next channel and use * to exit. - - - ***/ -static char *app = "DAHDIScan"; - -#define CONF_SIZE 160 - -static struct ast_channel *get_dahdi_channel_locked(int num) { - char name[80]; - - snprintf(name, sizeof(name), "DAHDI/%d-1", num); - return ast_get_channel_by_name_locked(name); -} - -static int careful_write(int fd, unsigned char *data, int len) -{ - int res; - while (len) { - res = write(fd, data, len); - if (res < 1) { - if (errno != EAGAIN) { - ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno)); - return -1; - } else { - return 0; - } - } - len -= res; - data += res; - } - return 0; -} - -static int conf_run(struct ast_channel *chan, int confno, int confflags) -{ - int fd; - struct dahdi_confinfo dahdic; - struct ast_frame *f; - struct ast_channel *c; - struct ast_frame fr; - int outfd; - int ms; - int nfds; - int res; - int flags; - int retrydahdi; - int origfd; - int ret = -1; - char input[4]; - int ic = 0; - - struct dahdi_bufferinfo bi; - char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET]; - char *buf = __buf + AST_FRIENDLY_OFFSET; - - /* Set it into U-law mode (write) */ - if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) { - ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name); - goto outrun; - } - - /* Set it into U-law mode (read) */ - if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) { - ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name); - goto outrun; - } - ast_indicate(chan, -1); - retrydahdi = strcasecmp(chan->tech->type, "DAHDI"); - dahdiretry: - origfd = chan->fds[0]; - if (retrydahdi) { - fd = open("/dev/dahdi/pseudo", O_RDWR); - if (fd < 0) { - ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno)); - goto outrun; - } - /* Make non-blocking */ - flags = fcntl(fd, F_GETFL); - if (flags < 0) { - ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno)); - close(fd); - goto outrun; - } - if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { - ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno)); - close(fd); - goto outrun; - } - /* Setup buffering information */ - memset(&bi, 0, sizeof(bi)); - bi.bufsize = CONF_SIZE; - bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE; - bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE; - bi.numbufs = 4; - if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) { - ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno)); - close(fd); - goto outrun; - } - nfds = 1; - } else { - /* XXX Make sure we're not running on a pseudo channel XXX */ - fd = chan->fds[0]; - nfds = 0; - } - memset(&dahdic, 0, sizeof(dahdic)); - /* Check to see if we're in a conference... */ - dahdic.chan = 0; - if (ioctl(fd, DAHDI_GETCONF, &dahdic)) { - ast_log(LOG_WARNING, "Error getting conference\n"); - close(fd); - goto outrun; - } - if (dahdic.confmode) { - /* Whoa, already in a conference... Retry... */ - if (!retrydahdi) { - ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n"); - retrydahdi = 1; - goto dahdiretry; - } - } - memset(&dahdic, 0, sizeof(dahdic)); - /* Add us to the conference */ - dahdic.chan = 0; - dahdic.confno = confno; - dahdic.confmode = DAHDI_CONF_MONITORBOTH; - - if (ioctl(fd, DAHDI_SETCONF, &dahdic)) { - ast_log(LOG_WARNING, "Error setting conference\n"); - close(fd); - goto outrun; - } - ast_debug(1, "Placed channel %s in DAHDI channel %d monitor\n", chan->name, confno); - - for (;;) { - outfd = -1; - ms = -1; - c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms); - if (c) { - if (c->fds[0] != origfd) { - if (retrydahdi) { - /* Kill old pseudo */ - close(fd); - } - ast_debug(1, "Ooh, something swapped out under us, starting over\n"); - retrydahdi = 0; - goto dahdiretry; - } - f = ast_read(c); - if (!f) { - break; - } - if (f->frametype == AST_FRAME_DTMF) { - if (f->subclass == '#') { - ret = 0; - break; - } else if (f->subclass == '*') { - ret = -1; - break; - } else { - input[ic++] = f->subclass; - } - if (ic == 3) { - input[ic++] = '\0'; - ic = 0; - ret = atoi(input); - ast_verb(3, "DAHDIScan: change channel to %d\n", ret); - break; - } - } - - if (fd != chan->fds[0]) { - if (f->frametype == AST_FRAME_VOICE) { - if (f->subclass == AST_FORMAT_ULAW) { - /* Carefully write */ - careful_write(fd, f->data.ptr, f->datalen); - } else { - ast_log(LOG_WARNING, "Huh? Got a non-ulaw (%d) frame in the conference\n", f->subclass); - } - } - } - ast_frfree(f); - } else if (outfd > -1) { - res = read(outfd, buf, CONF_SIZE); - if (res > 0) { - memset(&fr, 0, sizeof(fr)); - fr.frametype = AST_FRAME_VOICE; - fr.subclass = AST_FORMAT_ULAW; - fr.datalen = res; - fr.samples = res; - fr.data.ptr = buf; - fr.offset = AST_FRIENDLY_OFFSET; - if (ast_write(chan, &fr) < 0) { - ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno)); - /* break; */ - } - } else { - ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno)); - } - } - } - if (f) { - ast_frfree(f); - } - if (fd != chan->fds[0]) { - close(fd); - } else { - /* Take out of conference */ - /* Add us to the conference */ - dahdic.chan = 0; - dahdic.confno = 0; - dahdic.confmode = 0; - if (ioctl(fd, DAHDI_SETCONF, &dahdic)) { - ast_log(LOG_WARNING, "Error setting conference\n"); - } - } - - outrun: - - return ret; -} - -static int conf_exec(struct ast_channel *chan, void *data) -{ - int res=-1; - int confflags = 0; - int confno = 0; - char confnostr[80] = "", *tmp = NULL; - struct ast_channel *tempchan = NULL, *lastchan = NULL, *ichan = NULL; - struct ast_frame *f; - char *desired_group; - int input = 0, search_group = 0; - - if (chan->_state != AST_STATE_UP) - ast_answer(chan); - - desired_group = ast_strdupa(data); - if (!ast_strlen_zero(desired_group)) { - ast_verb(3, "Scanning for group %s\n", desired_group); - search_group = 1; - } - - for (;;) { - if (ast_waitfor(chan, 100) < 0) - break; - - f = ast_read(chan); - if (!f) - break; - if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*')) { - ast_frfree(f); - break; - } - ast_frfree(f); - ichan = NULL; - if(input) { - ichan = get_dahdi_channel_locked(input); - input = 0; - } - - tempchan = ichan ? ichan : ast_channel_walk_locked(tempchan); - - if (!tempchan && !lastchan) { - break; - } - - if (tempchan && search_group) { - const char *mygroup; - if ((mygroup = pbx_builtin_getvar_helper(tempchan, "GROUP")) && (!strcmp(mygroup, desired_group))) { - ast_verb(3, "Found Matching Channel %s in group %s\n", tempchan->name, desired_group); - } else { - ast_channel_unlock(tempchan); - lastchan = tempchan; - continue; - } - } - if (tempchan && (!strcmp(tempchan->tech->type, "DAHDI")) && (tempchan != chan)) { - ast_verb(3, "DAHDI channel %s is in-use, monitoring...\n", tempchan->name); - ast_copy_string(confnostr, tempchan->name, sizeof(confnostr)); - ast_channel_unlock(tempchan); - if ((tmp = strchr(confnostr, '-'))) { - *tmp = '\0'; - } - confno = atoi(strchr(confnostr, '/') + 1); - ast_stopstream(chan); - ast_say_number(chan, confno, AST_DIGIT_ANY, chan->language, (char *) NULL); - res = conf_run(chan, confno, confflags); - if (res < 0) { - break; - } - input = res; - } else if (tempchan) { - ast_channel_unlock(tempchan); - } - lastchan = tempchan; - } - return res; -} - -static int unload_module(void) -{ - return ast_unregister_application(app); -} - -static int load_module(void) -{ - return ((ast_register_application_xml(app, conf_exec)) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Scan DAHDI channels application"); - Index: apps/app_stack.c =================================================================== --- a/apps/app_stack.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_stack.c (.../trunk) (revision 202568) @@ -164,12 +164,27 @@ Return + + + Cause the channel to execute the specified dialplan subroutine. + + + + + + + + + Cause the channel to execute the specified dialplan subroutine, + returning to the dialplan with execution of a Return(). + + ***/ -static const char *app_gosub = "Gosub"; -static const char *app_gosubif = "GosubIf"; -static const char *app_return = "Return"; -static const char *app_pop = "StackPop"; +static const char * const app_gosub = "Gosub"; +static const char * const app_gosubif = "GosubIf"; +static const char * const app_return = "Return"; +static const char * const app_pop = "StackPop"; static void gosub_free(void *data); @@ -266,7 +281,7 @@ ast_free(oldlist); } -static int pop_exec(struct ast_channel *chan, void *data) +static int pop_exec(struct ast_channel *chan, const char *data) { struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL); struct gosub_stack_frame *oldframe; @@ -290,12 +305,12 @@ return 0; } -static int return_exec(struct ast_channel *chan, void *data) +static int return_exec(struct ast_channel *chan, const char *data) { struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL); struct gosub_stack_frame *oldframe; AST_LIST_HEAD(, gosub_stack_frame) *oldlist; - char *retval = data; + const char *retval = data; if (!stack_store) { ast_log(LOG_ERROR, "Return without Gosub: stack is unallocated\n"); @@ -320,7 +335,7 @@ return 0; } -static int gosub_exec(struct ast_channel *chan, void *data) +static int gosub_exec(struct ast_channel *chan, const char *data) { struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL); AST_LIST_HEAD(, gosub_stack_frame) *oldlist; @@ -410,7 +425,7 @@ return 0; } -static int gosubif_exec(struct ast_channel *chan, void *data) +static int gosubif_exec(struct ast_channel *chan, const char *data) { char *args; int res=0; @@ -537,7 +552,7 @@ .read = peek_read, }; -static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, char **argv) +static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, const char * const *argv) { int old_priority, priority; char old_context[AST_MAX_CONTEXT], old_extension[AST_MAX_EXTENSION]; @@ -627,19 +642,12 @@ return RESULT_SUCCESS; } -static char usage_gosub[] = -" Usage: GOSUB []\n" -" Cause the channel to execute the specified dialplan subroutine, returning\n" -" to the dialplan with execution of a Return()\n"; +static struct agi_command gosub_agi_command = + { { "gosub", NULL }, handle_gosub, NULL, NULL, 0 }; -struct agi_command gosub_agi_command = - { { "gosub", NULL }, handle_gosub, "Execute a dialplan subroutine", usage_gosub , 0 }; - static int unload_module(void) { - if (ast_agi_unregister) { - ast_agi_unregister(ast_module_info->self, &gosub_agi_command); - } + ast_agi_unregister(ast_module_info->self, &gosub_agi_command); ast_unregister_application(app_return); ast_unregister_application(app_pop); @@ -653,9 +661,7 @@ static int load_module(void) { - if (ast_agi_register) { - ast_agi_register(ast_module_info->self, &gosub_agi_command); - } + ast_agi_register(ast_module_info->self, &gosub_agi_command); ast_register_application_xml(app_pop, pop_exec); ast_register_application_xml(app_return, return_exec); Index: apps/app_chanspy.c =================================================================== --- a/apps/app_chanspy.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_chanspy.c (.../trunk) (revision 202568) @@ -50,6 +50,7 @@ #include "asterisk/module.h" #include "asterisk/lock.h" #include "asterisk/options.h" +#include "asterisk/autochan.h" #define AST_NAME_STRLEN 256 #define NUM_SPYGROUPS 128 @@ -141,6 +142,16 @@ name of the last channel that was spied on will be stored in the SPY_CHANNEL variable. + + + +
- + + + + Scan DAHDI channels to monitor calls. + + + + Limit scanning to a channel group by setting this option. + + + + Allows a call center manager to monitor DAHDI channels in a + convenient way. Use # to select the next channel and use * to exit. + + ***/ -static const char *app_chan = "ChanSpy"; -static const char *app_ext = "ExtenSpy"; +static const char app_chan[] = "ChanSpy"; +static const char app_ext[] = "ExtenSpy"; + +static const char app_dahdiscan[] = "DAHDIScan"; + enum { OPTION_QUIET = (1 << 0), /* Quiet, no announcement */ OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */ @@ -307,8 +345,11 @@ OPTION_NOTECH = (1 << 10), /* Skip technology name playback */ OPTION_BARGE = (1 << 11), /* Barge mode (whisper to both channels) */ OPTION_NAME = (1 << 12), /* Say the name of the person on whom we will spy */ - OPTION_DTMF_SWITCH_MODES = (1 << 13), /*Allow numeric DTMF to switch between chanspy modes */ -} chanspy_opt_flags; + OPTION_DTMF_SWITCH_MODES = (1 << 13), /* Allow numeric DTMF to switch between chanspy modes */ + OPTION_DTMF_EXIT = (1 << 14), /* Set DTMF to exit, added for DAHDIScan integration */ + OPTION_DTMF_CYCLE = (1 << 15), /* Custom DTMF for cycling next avaliable channel, (default is '*') */ + OPTION_DAHDI_SCAN = (1 << 16), /* Scan groups in DAHDIScan mode */ +}; enum { OPT_ARG_VOLUME = 0, @@ -316,8 +357,10 @@ OPT_ARG_RECORD, OPT_ARG_ENFORCED, OPT_ARG_NAME, + OPT_ARG_EXIT, + OPT_ARG_CYCLE, OPT_ARG_ARRAY_SIZE, -} chanspy_opt_args; +}; AST_APP_OPTIONS(spy_opts, { AST_APP_OPTION('q', OPTION_QUIET), @@ -334,10 +377,10 @@ AST_APP_OPTION('s', OPTION_NOTECH), AST_APP_OPTION_ARG('n', OPTION_NAME, OPT_ARG_NAME), AST_APP_OPTION('d', OPTION_DTMF_SWITCH_MODES), + AST_APP_OPTION_ARG('x', OPTION_DTMF_EXIT, OPT_ARG_EXIT), + AST_APP_OPTION_ARG('c', OPTION_DTMF_CYCLE, OPT_ARG_CYCLE), }); -static int next_unique_id_to_use = 0; - struct chanspy_translation_helper { /* spy data */ struct ast_audiohook spy_audiohook; @@ -347,6 +390,12 @@ int volfactor; }; +struct spy_dtmf_options { + char exit; + char cycle; + char volume; +}; + static void *spy_alloc(struct ast_channel *chan, void *data) { /* just store the data pointer in the channel structure */ @@ -361,7 +410,7 @@ static int spy_generate(struct ast_channel *chan, void *data, int len, int samples) { struct chanspy_translation_helper *csth = data; - struct ast_frame *f = NULL; + struct ast_frame *f, *cur; ast_audiohook_lock(&csth->spy_audiohook); if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) { @@ -377,14 +426,16 @@ if (!f) return 0; - if (ast_write(chan, f)) { - ast_frfree(f); - return -1; - } + for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) { + if (ast_write(chan, cur)) { + ast_frfree(f); + return -1; + } - if (csth->fd) { - if (write(csth->fd, f->data.ptr, f->datalen) < 0) { - ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); + if (csth->fd) { + if (write(csth->fd, cur->data.ptr, cur->datalen) < 0) { + ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); + } } } @@ -399,28 +450,22 @@ .generate = spy_generate, }; -static int start_spying(struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook) +static int start_spying(struct ast_autochan *autochan, const char *spychan_name, struct ast_audiohook *audiohook) { int res = 0; struct ast_channel *peer = NULL; - ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name); + ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, autochan->chan->name); ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE); - res = ast_audiohook_attach(chan, audiohook); + res = ast_audiohook_attach(autochan->chan, audiohook); - if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) { + if (!res && ast_test_flag(autochan->chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(autochan->chan))) { ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE); } return res; } -struct chanspy_ds { - struct ast_channel *chan; - char unique_id[20]; - ast_mutex_t lock; -}; - static void change_spy_mode(const char digit, struct ast_flags *flags) { if (digit == '4') { @@ -435,8 +480,9 @@ } } -static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds, - int *volfactor, int fd, struct ast_flags *flags, char *exitcontext) +static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_autochan, + int *volfactor, int fd, struct spy_dtmf_options *user_options, struct ast_flags *flags, + char *exitcontext) { struct chanspy_translation_helper csth; int running = 0, res, x = 0; @@ -444,32 +490,22 @@ char *name; struct ast_frame *f; struct ast_silence_generator *silgen = NULL; - struct ast_channel *spyee = NULL, *spyee_bridge = NULL; + struct ast_autochan *spyee_bridge_autochan = NULL; const char *spyer_name; ast_channel_lock(chan); spyer_name = ast_strdupa(chan->name); ast_channel_unlock(chan); - ast_mutex_lock(&spyee_chanspy_ds->lock); - if (spyee_chanspy_ds->chan) { - spyee = spyee_chanspy_ds->chan; - ast_channel_lock(spyee); - } - ast_mutex_unlock(&spyee_chanspy_ds->lock); - - if (!spyee) { - return 0; - } - /* We now hold the channel lock on spyee */ - if (ast_check_hangup(chan) || ast_check_hangup(spyee)) { - ast_channel_unlock(spyee); + if (ast_check_hangup(chan) || ast_check_hangup(spyee_autochan->chan)) { return 0; } - name = ast_strdupa(spyee->name); + ast_channel_lock(spyee_autochan->chan); + name = ast_strdupa(spyee_autochan->chan->name); + ast_channel_unlock(spyee_autochan->chan); ast_verb(2, "Spying on channel %s\n", name); manager_event(EVENT_FLAG_CALL, "ChanSpyStart", @@ -481,26 +517,23 @@ ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy"); - if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) { + if (start_spying(spyee_autochan, spyer_name, &csth.spy_audiohook)) { ast_audiohook_destroy(&csth.spy_audiohook); - ast_channel_unlock(spyee); return 0; } - ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy"); + ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy"); ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy"); - if (start_spying(spyee, spyer_name, &csth.whisper_audiohook)) { - ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", spyee->name); + if (start_spying(spyee_autochan, spyer_name, &csth.whisper_audiohook)) { + ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", name); } - if ((spyee_bridge = ast_bridged_channel(spyee))) { - ast_channel_lock(spyee_bridge); - if (start_spying(spyee_bridge, spyer_name, &csth.bridge_whisper_audiohook)) { - ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", spyee->name); + if ((spyee_bridge_autochan = ast_autochan_setup(ast_bridged_channel(spyee_autochan->chan)))) { + ast_channel_lock(spyee_bridge_autochan->chan); + if (start_spying(spyee_bridge_autochan, spyer_name, &csth.bridge_whisper_audiohook)) { + ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", name); } - ast_channel_unlock(spyee_bridge); + ast_channel_unlock(spyee_bridge_autochan->chan); } - ast_channel_unlock(spyee); - spyee = NULL; ast_channel_lock(chan); ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY); @@ -590,10 +623,13 @@ } } - if (res == '*') { + if (res == user_options->cycle) { running = 0; break; - } else if (res == '#') { + } else if (res == user_options->exit) { + running = -2; + break; + } else if (res == user_options->volume) { if (!ast_strlen_zero(inp)) { running = atoi(inp); break; @@ -633,128 +669,45 @@ ast_audiohook_detach(&csth.spy_audiohook); ast_audiohook_unlock(&csth.spy_audiohook); ast_audiohook_destroy(&csth.spy_audiohook); - + + if (spyee_bridge_autochan) { + ast_autochan_destroy(spyee_bridge_autochan); + } + ast_verb(2, "Done Spying on channel %s\n", name); manager_event(EVENT_FLAG_CALL, "ChanSpyStop", "SpyeeChannel: %s\r\n", name); return running; } -/*! - * \note This relies on the embedded lock to be recursive, as it may be called - * due to a call to chanspy_ds_free with the lock held there. - */ -static void chanspy_ds_destroy(void *data) +static struct ast_autochan *next_channel(struct ast_channel_iterator *iter, + struct ast_autochan *autochan, struct ast_channel *chan) { - struct chanspy_ds *chanspy_ds = data; + struct ast_channel *next; + const size_t pseudo_len = strlen("DAHDI/pseudo"); - /* Setting chan to be NULL is an atomic operation, but we don't want this - * value to change while this lock is held. The lock is held elsewhere - * while it performs non-atomic operations with this channel pointer */ - - ast_mutex_lock(&chanspy_ds->lock); - chanspy_ds->chan = NULL; - ast_mutex_unlock(&chanspy_ds->lock); -} - -static void chanspy_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) -{ - struct chanspy_ds *chanspy_ds = data; - - ast_mutex_lock(&chanspy_ds->lock); - chanspy_ds->chan = new_chan; - ast_mutex_unlock(&chanspy_ds->lock); -} - -static const struct ast_datastore_info chanspy_ds_info = { - .type = "chanspy", - .destroy = chanspy_ds_destroy, - .chan_fixup = chanspy_ds_chan_fixup, -}; - -static struct chanspy_ds *chanspy_ds_free(struct chanspy_ds *chanspy_ds) -{ - if (!chanspy_ds) + if (!iter) { return NULL; - - ast_mutex_lock(&chanspy_ds->lock); - if (chanspy_ds->chan) { - struct ast_datastore *datastore; - struct ast_channel *chan; - - chan = chanspy_ds->chan; - - ast_channel_lock(chan); - if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) { - ast_channel_datastore_remove(chan, datastore); - /* chanspy_ds->chan is NULL after this call */ - chanspy_ds_destroy(datastore->data); - datastore->data = NULL; - ast_datastore_free(datastore); - } - ast_channel_unlock(chan); } - ast_mutex_unlock(&chanspy_ds->lock); - return NULL; -} - -/*! \note Returns the channel in the chanspy_ds locked as well as the chanspy_ds locked */ -static struct chanspy_ds *setup_chanspy_ds(struct ast_channel *chan, struct chanspy_ds *chanspy_ds) -{ - struct ast_datastore *datastore = NULL; - - ast_mutex_lock(&chanspy_ds->lock); - - if (!(datastore = ast_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) { - ast_mutex_unlock(&chanspy_ds->lock); - chanspy_ds = chanspy_ds_free(chanspy_ds); - ast_channel_unlock(chan); +redo: + if (!(next = ast_channel_iterator_next(iter))) { return NULL; } - - chanspy_ds->chan = chan; - datastore->data = chanspy_ds; - ast_channel_datastore_add(chan, datastore); - return chanspy_ds; -} - -static struct chanspy_ds *next_channel(struct ast_channel *chan, - const struct ast_channel *last, const char *spec, - const char *exten, const char *context, struct chanspy_ds *chanspy_ds) -{ - struct ast_channel *next; - const size_t pseudo_len = strlen("DAHDI/pseudo"); - -redo: - if (!ast_strlen_zero(spec)) - next = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec)); - else if (!ast_strlen_zero(exten)) - next = ast_walk_channel_by_exten_locked(last, exten, context); - else - next = ast_channel_walk_locked(last); - - if (!next) - return NULL; - if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) { - last = next; - ast_channel_unlock(next); goto redo; } else if (next == chan) { - last = next; - ast_channel_unlock(next); goto redo; } - return setup_chanspy_ds(next, chanspy_ds); + return ast_autochan_setup(next); } static int common_exec(struct ast_channel *chan, struct ast_flags *flags, - int volfactor, const int fd, const char *mygroup, const char *myenforced, - const char *spec, const char *exten, const char *context, const char *mailbox, - const char *name_context) + int volfactor, const int fd, struct spy_dtmf_options *user_options, + const char *mygroup, const char *myenforced, const char *spec, const char *exten, + const char *context, const char *mailbox, const char *name_context) { char nameprefix[AST_NAME_STRLEN]; char peer_name[AST_NAME_STRLEN + 5]; @@ -765,7 +718,7 @@ char *ptr; int num; int num_spyed_upon = 1; - struct chanspy_ds chanspy_ds = { 0, }; + struct ast_channel_iterator *iter = NULL; if (ast_test_flag(flags, OPTION_EXIT)) { const char *c; @@ -780,10 +733,6 @@ ast_channel_unlock(chan); } - ast_mutex_init(&chanspy_ds.lock); - - snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1)); - if (chan->_state != AST_STATE_UP) ast_answer(chan); @@ -792,8 +741,8 @@ waitms = 100; for (;;) { - struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL; - struct ast_channel *prev = NULL, *peer = NULL; + struct ast_autochan *autochan = NULL, *next_autochan = NULL; + struct ast_channel *prev = NULL; if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) { res = ast_streamfile(chan, "beep", chan->language); @@ -814,6 +763,19 @@ } } + /* Set up the iterator we'll be using during this call */ + if (!ast_strlen_zero(spec)) { + iter = ast_channel_iterator_by_name_new(0, spec, strlen(spec)); + } else if (!ast_strlen_zero(exten)) { + iter = ast_channel_iterator_by_exten_new(0, exten, context); + } else { + iter = ast_channel_iterator_all_new(0); + } + + if (!iter) { + return -1; + } + res = ast_waitfordigit(chan, waitms); if (res < 0) { ast_clear_flag(chan, AST_FLAG_SPYING); @@ -833,38 +795,30 @@ waitms = 100; num_spyed_upon = 0; - for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds); - peer_chanspy_ds; - chanspy_ds_free(peer_chanspy_ds), prev = peer, - peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : - next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) { + for (autochan = next_channel(iter, autochan, chan); + autochan; + prev = autochan->chan, ast_autochan_destroy(autochan), + autochan = next_autochan ? next_autochan : + next_channel(iter, autochan, chan), next_autochan = NULL) { int igrp = !mygroup; int ienf = !myenforced; char *s; - peer = peer_chanspy_ds->chan; - - ast_mutex_unlock(&peer_chanspy_ds->lock); - - if (peer == prev) { - ast_channel_unlock(peer); - chanspy_ds_free(peer_chanspy_ds); + if (autochan->chan == prev) { + ast_autochan_destroy(autochan); break; } if (ast_check_hangup(chan)) { - ast_channel_unlock(peer); - chanspy_ds_free(peer_chanspy_ds); + ast_autochan_destroy(autochan); break; } - if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) { - ast_channel_unlock(peer); + if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(autochan->chan)) { continue; } - if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) { - ast_channel_unlock(peer); + if (ast_check_hangup(autochan->chan) || ast_test_flag(autochan->chan, AST_FLAG_SPYING)) { continue; } @@ -875,14 +829,22 @@ char dup_mygroup[512]; char *groups[NUM_SPYGROUPS]; char *mygroups[NUM_SPYGROUPS]; - const char *group; + const char *group = NULL; int x; int y; ast_copy_string(dup_mygroup, mygroup, sizeof(dup_mygroup)); num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups, ARRAY_LEN(mygroups)); - if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) { + /* Before dahdi scan was part of chanspy, it would use the "GROUP" variable + * rather than "SPYGROUP", this check is done to preserve expected behavior */ + if (ast_test_flag(flags, OPTION_DAHDI_SCAN)) { + group = pbx_builtin_getvar_helper(autochan->chan, "GROUP"); + } else { + group = pbx_builtin_getvar_helper(autochan->chan, "SPYGROUP"); + } + + if (!ast_strlen_zero(group)) { ast_copy_string(dup_group, group, sizeof(dup_group)); num_groups = ast_app_separate_args(dup_group, ':', groups, ARRAY_LEN(groups)); @@ -899,10 +861,8 @@ } if (!igrp) { - ast_channel_unlock(peer); continue; } - if (myenforced) { char ext[AST_CHANNEL_NAME + 3]; char buffer[512]; @@ -910,7 +870,7 @@ snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced); - ast_copy_string(ext + 1, peer->name, sizeof(ext) - 1); + ast_copy_string(ext + 1, autochan->chan->name, sizeof(ext) - 1); if ((end = strchr(ext, '-'))) { *end++ = ':'; *end = '\0'; @@ -928,18 +888,13 @@ } strcpy(peer_name, "spy-"); - strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1); + strncat(peer_name, autochan->chan->name, AST_NAME_STRLEN - 4 - 1); ptr = strchr(peer_name, '/'); *ptr++ = '\0'; ptr = strsep(&ptr, "-"); for (s = peer_name; s < ptr; s++) *s = tolower(*s); - /* We have to unlock the peer channel here to avoid a deadlock. - * So, when we need to dereference it again, we have to lock the - * datastore and get the pointer from there to see if the channel - * is still valid. */ - ast_channel_unlock(peer); if (!ast_test_flag(flags, OPTION_QUIET)) { if (ast_test_flag(flags, OPTION_NAME)) { @@ -955,7 +910,7 @@ res = ast_waitstream(chan, ""); } if (res) { - chanspy_ds_free(peer_chanspy_ds); + ast_autochan_destroy(autochan); break; } } else { @@ -967,42 +922,38 @@ } } - res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext); - num_spyed_upon++; + res = channel_spy(chan, autochan, &volfactor, fd, user_options, flags, exitcontext); + num_spyed_upon++; if (res == -1) { - chanspy_ds_free(peer_chanspy_ds); + ast_autochan_destroy(autochan); goto exit; } else if (res == -2) { res = 0; - chanspy_ds_free(peer_chanspy_ds); + ast_autochan_destroy(autochan); goto exit; } else if (res > 1 && spec) { struct ast_channel *next; snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res); - if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) { - peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds); - next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds); + if ((next = ast_channel_get_by_name_prefix(nameprefix, strlen(nameprefix)))) { + next_autochan = ast_autochan_setup(next); + next = ast_channel_unref(next); } else { /* stay on this channel, if it is still valid */ - - ast_mutex_lock(&peer_chanspy_ds->lock); - if (peer_chanspy_ds->chan) { - ast_channel_lock(peer_chanspy_ds->chan); - next_chanspy_ds = peer_chanspy_ds; - peer_chanspy_ds = NULL; + if (!ast_check_hangup(autochan->chan)) { + next_autochan = ast_autochan_setup(autochan->chan); } else { /* the channel is gone */ - ast_mutex_unlock(&peer_chanspy_ds->lock); - next_chanspy_ds = NULL; + next_autochan = NULL; } } - - peer = NULL; } } + + iter = ast_channel_iterator_destroy(iter); + if (res == -1 || ast_check_hangup(chan)) break; } @@ -1012,20 +963,21 @@ ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0); - ast_mutex_lock(&chanspy_ds.lock); - ast_mutex_unlock(&chanspy_ds.lock); - ast_mutex_destroy(&chanspy_ds.lock); - return res; } -static int chanspy_exec(struct ast_channel *chan, void *data) +static int chanspy_exec(struct ast_channel *chan, const char *data) { char *myenforced = NULL; char *mygroup = NULL; char *recbase = NULL; int fd = 0; struct ast_flags flags; + struct spy_dtmf_options user_options = { + .cycle = '*', + .volume = '#', + .exit = '\0', + }; int oldwf = 0; int volfactor = 0; int res; @@ -1036,14 +988,15 @@ AST_APP_ARG(options); ); char *opts[OPT_ARG_ARRAY_SIZE]; + char *parse = ast_strdupa(data); - data = ast_strdupa(data); - AST_STANDARD_APP_ARGS(args, data); + AST_STANDARD_APP_ARGS(args, parse); if (args.spec && !strcmp(args.spec, "all")) args.spec = NULL; if (args.options) { + char tmp; ast_app_parse_options(spy_opts, &flags, opts, args.options); if (ast_test_flag(&flags, OPTION_GROUP)) mygroup = opts[OPT_ARG_GROUP]; @@ -1052,6 +1005,24 @@ !(recbase = opts[OPT_ARG_RECORD])) recbase = "chanspy"; + if (ast_test_flag(&flags, OPTION_DTMF_EXIT) && opts[OPT_ARG_EXIT]) { + tmp = opts[OPT_ARG_EXIT][0]; + if (strchr("0123456789*#", tmp) && tmp != '\0') { + user_options.exit = tmp; + } else { + ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit."); + } + } + + if (ast_test_flag(&flags, OPTION_DTMF_CYCLE) && opts[OPT_ARG_CYCLE]) { + tmp = opts[OPT_ARG_CYCLE][0]; + if (strchr("0123456789*#", tmp) && tmp != '\0') { + user_options.cycle = tmp; + } else { + ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit."); + } + } + if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) { int vol; @@ -1066,7 +1037,7 @@ if (ast_test_flag(&flags, OPTION_ENFORCED)) myenforced = opts[OPT_ARG_ENFORCED]; - + if (ast_test_flag(&flags, OPTION_NAME)) { if (!ast_strlen_zero(opts[OPT_ARG_NAME])) { char *delimiter; @@ -1079,10 +1050,9 @@ } } } - - - } else + } else { ast_clear_flag(&flags, AST_FLAGS_ALL); + } oldwf = chan->writeformat; if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { @@ -1100,7 +1070,7 @@ } } - res = common_exec(chan, &flags, volfactor, fd, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context); + res = common_exec(chan, &flags, volfactor, fd, &user_options, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context); if (fd) close(fd); @@ -1111,13 +1081,18 @@ return res; } -static int extenspy_exec(struct ast_channel *chan, void *data) +static int extenspy_exec(struct ast_channel *chan, const char *data) { char *ptr, *exten = NULL; char *mygroup = NULL; char *recbase = NULL; int fd = 0; struct ast_flags flags; + struct spy_dtmf_options user_options = { + .cycle = '*', + .volume = '#', + .exit = '\0', + }; int oldwf = 0; int volfactor = 0; int res; @@ -1127,10 +1102,9 @@ AST_APP_ARG(context); AST_APP_ARG(options); ); + char *parse = ast_strdupa(data); - data = ast_strdupa(data); - - AST_STANDARD_APP_ARGS(args, data); + AST_STANDARD_APP_ARGS(args, parse); if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) { exten = args.context; *ptr++ = '\0'; @@ -1142,6 +1116,7 @@ if (args.options) { char *opts[OPT_ARG_ARRAY_SIZE]; + char tmp; ast_app_parse_options(spy_opts, &flags, opts, args.options); if (ast_test_flag(&flags, OPTION_GROUP)) @@ -1151,6 +1126,24 @@ !(recbase = opts[OPT_ARG_RECORD])) recbase = "chanspy"; + if (ast_test_flag(&flags, OPTION_DTMF_EXIT) && opts[OPT_ARG_EXIT]) { + tmp = opts[OPT_ARG_EXIT][0]; + if (strchr("0123456789*#", tmp) && tmp != '\0') { + user_options.exit = tmp; + } else { + ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit."); + } + } + + if (ast_test_flag(&flags, OPTION_DTMF_CYCLE) && opts[OPT_ARG_CYCLE]) { + tmp = opts[OPT_ARG_CYCLE][0]; + if (strchr("0123456789*#", tmp) && tmp != '\0') { + user_options.cycle = tmp; + } else { + ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit."); + } + } + if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) { int vol; @@ -1163,7 +1156,6 @@ if (ast_test_flag(&flags, OPTION_PRIVATE)) ast_set_flag(&flags, OPTION_WHISPER); - if (ast_test_flag(&flags, OPTION_NAME)) { if (!ast_strlen_zero(opts[OPT_ARG_NAME])) { char *delimiter; @@ -1177,8 +1169,9 @@ } } - } else + } else { ast_clear_flag(&flags, AST_FLAGS_ALL); + } oldwf = chan->writeformat; if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { @@ -1197,7 +1190,7 @@ } - res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, exten, args.context, mailbox, name_context); + res = common_exec(chan, &flags, volfactor, fd, &user_options, mygroup, NULL, NULL, exten, args.context, mailbox, name_context); if (fd) close(fd); @@ -1208,12 +1201,49 @@ return res; } +static int dahdiscan_exec(struct ast_channel *chan, const char *data) +{ + const char *spec = "DAHDI"; + struct ast_flags flags; + struct spy_dtmf_options user_options = { + .cycle = '#', + .volume = '\0', + .exit = '*', + }; + int oldwf = 0; + int res; + char *mygroup = NULL; + + ast_clear_flag(&flags, AST_FLAGS_ALL); + + if (!ast_strlen_zero(data)) { + mygroup = ast_strdupa(data); + } + ast_set_flag(&flags, OPTION_DTMF_EXIT); + ast_set_flag(&flags, OPTION_DTMF_CYCLE); + ast_set_flag(&flags, OPTION_DAHDI_SCAN); + + oldwf = chan->writeformat; + if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { + ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); + return -1; + } + + res = common_exec(chan, &flags, 0, 0, &user_options, mygroup, NULL, spec, NULL, NULL, NULL, NULL); + + if (oldwf && ast_set_write_format(chan, oldwf) < 0) + ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); + + return res; +} + static int unload_module(void) { int res = 0; res |= ast_unregister_application(app_chan); res |= ast_unregister_application(app_ext); + res |= ast_unregister_application(app_dahdiscan); return res; } @@ -1224,6 +1254,7 @@ res |= ast_register_application_xml(app_chan, chanspy_exec); res |= ast_register_application_xml(app_ext, extenspy_exec); + res |= ast_register_application_xml(app_dahdiscan, dahdiscan_exec); return res; } Index: apps/app_jack.c =================================================================== --- a/apps/app_jack.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_jack.c (.../trunk) (revision 202568) @@ -26,7 +26,7 @@ * and output jack port so that the audio can be processed through * another application, or to play audio from another application. * - * \arg http://www.jackaudio.org/ + * \extref http://www.jackaudio.org/ * * \note To install libresample, check it out of the following repository: * $ svn co http://svn.digium.com/svn/thirdparty/libresample/trunk @@ -106,14 +106,15 @@ - When executing this application, two jack ports will be created; - one input and one output. Other applications can be hooked up to + When executing this application, two jack ports will be created; + one input and one output. Other applications can be hooked up to these ports to access audio coming from, or being send to the channel. ***/ -static char *jack_app = "JACK"; +static const char jack_app[] = "JACK"; + struct jack_data { AST_DECLARE_STRING_FIELDS( AST_STRING_FIELD(server_name); @@ -181,7 +182,7 @@ } else ast_str_append(&str, 0, ", %s", jack_status_to_str((1 << i))); } - + ast_log(LOG_NOTICE, "%s: %s\n", prefix, ast_str_buffer(str)); } @@ -201,10 +202,10 @@ /* XXX Hard coded 8 kHz */ - to_srate = input ? 8000.0 : jack_srate; + to_srate = input ? 8000.0 : jack_srate; from_srate = input ? jack_srate : 8000.0; - resample_factor = input ? &jack_data->input_resample_factor : + resample_factor = input ? &jack_data->input_resample_factor : &jack_data->output_resample_factor; if (from_srate == to_srate) { @@ -219,9 +220,9 @@ resampler = input ? &jack_data->input_resampler : &jack_data->output_resampler; - if (!(*resampler = resample_open(RESAMPLE_QUALITY, + if (!(*resampler = resample_open(RESAMPLE_QUALITY, *resample_factor, *resample_factor))) { - ast_log(LOG_ERROR, "Failed to open %s resampler\n", + ast_log(LOG_ERROR, "Failed to open %s resampler\n", input ? "input" : "output"); return -1; } @@ -233,9 +234,9 @@ * \brief Handle jack input port * * Read nframes number of samples from the input buffer, resample it - * if necessary, and write it into the appropriate ringbuffer. + * if necessary, and write it into the appropriate ringbuffer. */ -static void handle_input(void *buf, jack_nframes_t nframes, +static void handle_input(void *buf, jack_nframes_t nframes, struct jack_data *jack_data) { short s_buf[nframes]; @@ -266,7 +267,7 @@ total_out_buf_used += out_buf_used; total_in_buf_used += in_buf_used; - + if (total_out_buf_used == ARRAY_LEN(f_buf)) { ast_log(LOG_ERROR, "Output buffer filled ... need to increase its size, " "nframes '%d', total_out_buf_used '%d'\n", nframes, total_out_buf_used); @@ -276,7 +277,7 @@ for (i = 0; i < total_out_buf_used; i++) s_buf[i] = f_buf[i] * (SHRT_MAX / 1.0); - + write_len = total_out_buf_used * sizeof(int16_t); } else { /* No resampling needed */ @@ -298,7 +299,7 @@ * Read nframes number of samples from the ringbuffer and write it out to the * output port buffer. */ -static void handle_output(void *buf, jack_nframes_t nframes, +static void handle_output(void *buf, jack_nframes_t nframes, struct jack_data *jack_data) { size_t res, len; @@ -368,7 +369,7 @@ resample_close(jack_data->output_resampler); jack_data->output_resampler = NULL; } - + if (jack_data->input_resampler) { resample_close(jack_data->input_resampler); jack_data->input_resampler = NULL; @@ -539,10 +540,10 @@ int in_buf_used; int out_buf_used; - out_buf_used = resample_process(jack_data->output_resampler, + out_buf_used = resample_process(jack_data->output_resampler, jack_data->output_resample_factor, - &in_buf[total_in_buf_used], ARRAY_LEN(in_buf) - total_in_buf_used, - 0, &in_buf_used, + &in_buf[total_in_buf_used], ARRAY_LEN(in_buf) - total_in_buf_used, + 0, &in_buf_used, &f_buf[total_out_buf_used], ARRAY_LEN(f_buf) - total_out_buf_used); if (out_buf_used < 0) @@ -599,7 +600,7 @@ */ static void handle_jack_audio(struct ast_channel *chan, struct jack_data *jack_data, struct ast_frame *out_frame) -{ +{ short buf[160]; struct ast_frame f = { .frametype = AST_FRAME_VOICE, @@ -677,7 +678,7 @@ if (!(jack_data = ast_calloc(1, sizeof(*jack_data)))) return NULL; - + if (ast_string_field_init(jack_data, 32)) { ast_free(jack_data); return NULL; @@ -740,19 +741,14 @@ return 0; } -static int jack_exec(struct ast_channel *chan, void *data) +static int jack_exec(struct ast_channel *chan, const char *data) { struct jack_data *jack_data; - AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(options); - ); if (!(jack_data = jack_data_alloc())) return -1; - args.options = data; - - if (!ast_strlen_zero(args.options) && handle_options(jack_data, args.options)) { + if (!ast_strlen_zero(data) && handle_options(jack_data, data)) { destroy_jack_data(jack_data); return -1; } @@ -816,7 +812,7 @@ .destroy = jack_hook_ds_destroy, }; -static int jack_hook_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, +static int jack_hook_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction) { struct ast_datastore *datastore; @@ -875,7 +871,7 @@ } if (ast_strlen_zero(args.mode) || strcasecmp(args.mode, "manipulate")) { - ast_log(LOG_ERROR, "'%s' is not a supported mode. Only manipulate is supported.\n", + ast_log(LOG_ERROR, "'%s' is not a supported mode. Only manipulate is supported.\n", S_OR(args.mode, "")); goto return_error; } @@ -945,7 +941,7 @@ return 0; } -static int jack_hook_write(struct ast_channel *chan, const char *cmd, char *data, +static int jack_hook_write(struct ast_channel *chan, const char *cmd, char *data, const char *value) { int res; @@ -955,7 +951,7 @@ else if (!strcasecmp(value, "off")) res = disable_jack_hook(chan); else { - ast_log(LOG_ERROR, "'%s' is not a valid value for JACK_HOOK()\n", value); + ast_log(LOG_ERROR, "'%s' is not a valid value for JACK_HOOK()\n", value); res = -1; } Index: apps/app_cdr.c =================================================================== --- a/apps/app_cdr.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_cdr.c (.../trunk) (revision 202568) @@ -44,9 +44,9 @@ ***/ -static char *nocdr_app = "NoCDR"; +static const char nocdr_app[] = "NoCDR"; -static int nocdr_exec(struct ast_channel *chan, void *data) +static int nocdr_exec(struct ast_channel *chan, const char *data) { if (chan->cdr) ast_set_flag(chan->cdr, AST_CDR_FLAG_POST_DISABLED); Index: apps/app_adsiprog.c =================================================================== --- a/apps/app_adsiprog.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_adsiprog.c (.../trunk) (revision 202568) @@ -45,7 +45,7 @@ #include "asterisk/utils.h" #include "asterisk/lock.h" -static char *app = "ADSIProg"; +static const char app[] = "ADSIProg"; /*** DOCUMENTATION @@ -71,10 +71,10 @@ struct adsi_event { int id; - char *name; + const char *name; }; -static struct adsi_event events[] = { +static const struct adsi_event events[] = { { 1, "CALLERID" }, { 2, "VMWI" }, { 3, "NEARANSWER" }, @@ -101,7 +101,7 @@ { 24, "CPEID" }, }; -static struct adsi_event justify[] = { +static const struct adsi_event justify[] = { { 0, "CENTER" }, { 1, "RIGHT" }, { 2, "LEFT" }, @@ -232,7 +232,7 @@ return 0; } -static char *get_token(char **buf, char *script, int lineno) +static char *get_token(char **buf, const char *script, int lineno) { char *tmp = *buf, *keyword; int quoted = 0; @@ -264,7 +264,7 @@ static char *validdtmf = "123456789*0#ABCD"; -static int send_dtmf(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno) +static int send_dtmf(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno) { char dtmfstr[80], *a; int bytes = 0; @@ -294,7 +294,7 @@ return bytes; } -static int goto_line(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno) +static int goto_line(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno) { char *page = get_token(&args, script, lineno); char *gline = get_token(&args, script, lineno); @@ -327,7 +327,7 @@ return 2; } -static int goto_line_rel(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno) +static int goto_line_rel(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno) { char *dir = get_token(&args, script, lineno); char *gline = get_token(&args, script, lineno); @@ -360,7 +360,7 @@ return 2; } -static int send_delay(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno) +static int send_delay(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno) { char *gtime = get_token(&args, script, lineno); int ms; @@ -385,7 +385,7 @@ return 2; } -static int set_state(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno) +static int set_state(char *buf, char *name, int id, char *args, struct adsi_script *istate, const char *script, int lineno) { char *gstate = get_token(&args, script, lineno); int state; @@ -406,7 +406,7 @@ return 2; } -static int cleartimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno) +static int cleartimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, const char *script, int lineno) { char *tok = get_token(&args, script, lineno); @@ -424,7 +424,7 @@ return 2; } -static struct adsi_flag *getflagbyname(struct adsi_script *state, char *name, char *script, int lineno, int create) +static struct adsi_flag *getflagbyname(struct adsi_script *state, char *name, const char *script, int lineno, int create) { int x; @@ -449,7 +449,7 @@ return &state->flags[state->numflags-1]; } -static int setflag(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno) +static int setflag(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno) { char *tok = get_token(&args, script, lineno); char sname[80]; @@ -476,7 +476,7 @@ return 2; } -static int clearflag(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno) +static int clearflag(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno) { char *tok = get_token(&args, script, lineno); struct adsi_flag *flag; @@ -503,7 +503,7 @@ return 2; } -static int starttimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno) +static int starttimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, const char *script, int lineno) { char *tok = get_token(&args, script, lineno); int secs; @@ -549,7 +549,7 @@ return -1; } -static struct adsi_soft_key *getkeybyname(struct adsi_script *state, char *name, char *script, int lineno) +static struct adsi_soft_key *getkeybyname(struct adsi_script *state, char *name, const char *script, int lineno) { int x; @@ -570,7 +570,7 @@ return &state->keys[state->numkeys-1]; } -static struct adsi_subscript *getsubbyname(struct adsi_script *state, char *name, char *script, int lineno) +static struct adsi_subscript *getsubbyname(struct adsi_script *state, char *name, const char *script, int lineno) { int x; @@ -591,7 +591,7 @@ return &state->subs[state->numsubs-1]; } -static struct adsi_state *getstatebyname(struct adsi_script *state, char *name, char *script, int lineno, int create) +static struct adsi_state *getstatebyname(struct adsi_script *state, char *name, const char *script, int lineno, int create) { int x; @@ -616,7 +616,7 @@ return &state->states[state->numstates-1]; } -static struct adsi_display *getdisplaybyname(struct adsi_script *state, char *name, char *script, int lineno, int create) +static struct adsi_display *getdisplaybyname(struct adsi_script *state, char *name, const char *script, int lineno, int create) { int x; @@ -641,7 +641,7 @@ return &state->displays[state->numdisplays-1]; } -static int showkeys(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno) +static int showkeys(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno) { char *tok, newkey[80]; int bytes, x, flagid = 0; @@ -688,7 +688,7 @@ return 2 + x; } -static int showdisplay(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno) +static int showdisplay(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno) { char *tok, dispname[80]; int line = 0, flag = 0, cmd = 3; @@ -739,7 +739,7 @@ return 3; } -static int cleardisplay(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno) +static int cleardisplay(char *buf, char *name, int id, char *args, struct adsi_script *istate, const char *script, int lineno) { char *tok = get_token(&args, script, lineno); @@ -751,7 +751,7 @@ return 2; } -static int digitdirect(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno) +static int digitdirect(char *buf, char *name, int id, char *args, struct adsi_script *istate, const char *script, int lineno) { char *tok = get_token(&args, script, lineno); @@ -763,7 +763,7 @@ return 2; } -static int clearcbone(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno) +static int clearcbone(char *buf, char *name, int id, char *args, struct adsi_script *istate, const char *script, int lineno) { char *tok = get_token(&args, script, lineno); @@ -775,7 +775,7 @@ return 2; } -static int digitcollect(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno) +static int digitcollect(char *buf, char *name, int id, char *args, struct adsi_script *istate, const char *script, int lineno) { char *tok = get_token(&args, script, lineno); @@ -787,7 +787,7 @@ return 2; } -static int subscript(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno) +static int subscript(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno) { char *tok = get_token(&args, script, lineno); char subscr[80]; @@ -812,7 +812,7 @@ return 2; } -static int onevent(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno) +static int onevent(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno) { char *tok = get_token(&args, script, lineno); char subscr[80], sname[80]; @@ -879,10 +879,10 @@ struct adsi_key_cmd { char *name; int id; - int (*add_args)(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno); + int (*add_args)(char *buf, char *name, int id, char *args, struct adsi_script *state, const char *script, int lineno); }; -static struct adsi_key_cmd kcmds[] = { +static const struct adsi_key_cmd kcmds[] = { { "SENDDTMF", 0, send_dtmf }, /* Encoded DTMF would go here */ { "ONHOOK", 0x81 }, @@ -924,7 +924,7 @@ { "EXIT", 0xa0 }, }; -static struct adsi_key_cmd opcmds[] = { +static const struct adsi_key_cmd opcmds[] = { /* 1 - Branch on event -- handled specially */ { "SHOWKEYS", 2, showkeys }, @@ -944,7 +944,7 @@ }; -static int process_returncode(struct adsi_soft_key *key, char *code, char *args, struct adsi_script *state, char *script, int lineno) +static int process_returncode(struct adsi_soft_key *key, char *code, char *args, struct adsi_script *state, const char *script, int lineno) { int x, res; char *unused; @@ -973,7 +973,7 @@ return -1; } -static int process_opcode(struct adsi_subscript *sub, char *code, char *args, struct adsi_script *state, char *script, int lineno) +static int process_opcode(struct adsi_subscript *sub, char *code, char *args, struct adsi_script *state, const char *script, int lineno) { int x, res, max = sub->id ? MAX_SUB_LEN : MAX_MAIN_LEN; char *unused; @@ -1010,7 +1010,7 @@ return -1; } -static int adsi_process(struct adsi_script *state, char *buf, char *script, int lineno) +static int adsi_process(struct adsi_script *state, char *buf, const char *script, int lineno) { char *keyword = get_token(&buf, script, lineno); char *args, vname[256], tmp[80], tmp2[80]; @@ -1358,7 +1358,7 @@ return 0; } -static struct adsi_script *compile_script(char *script) +static struct adsi_script *compile_script(const char *script) { FILE *f; char fn[256], buf[256], *c; @@ -1451,7 +1451,7 @@ } #endif -static int adsi_prog(struct ast_channel *chan, char *script) +static int adsi_prog(struct ast_channel *chan, const char *script) { struct adsi_script *scr; int x, bytes; @@ -1562,7 +1562,7 @@ return 0; } -static int adsi_exec(struct ast_channel *chan, void *data) +static int adsi_exec(struct ast_channel *chan, const char *data) { int res = 0; Index: apps/app_read.c =================================================================== --- a/apps/app_read.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_read.c (.../trunk) (revision 202568) @@ -108,11 +108,11 @@ ***/ -enum { +enum read_option_flags { OPT_SKIP = (1 << 0), OPT_INDICATION = (1 << 1), OPT_NOANSWER = (1 << 2), -} read_option_flags; +}; AST_APP_OPTIONS(read_app_options, { AST_APP_OPTION('s', OPT_SKIP), @@ -122,9 +122,7 @@ static char *app = "Read"; -#define ast_next_data(instr,ptr,delim) if((ptr=strchr(instr,delim))) { *(ptr) = '\0' ; ptr++;} - -static int read_exec(struct ast_channel *chan, void *data) +static int read_exec(struct ast_channel *chan, const char *data) { int res = 0; char tmp[256] = ""; Index: apps/app_dictate.c =================================================================== --- a/apps/app_dictate.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_dictate.c (.../trunk) (revision 202568) @@ -55,7 +55,7 @@ ***/ -static char *app = "Dictate"; +static const char app[] = "Dictate"; typedef enum { DFLAG_RECORD = (1 << 0), @@ -81,7 +81,7 @@ return res; } -static int dictate_exec(struct ast_channel *chan, void *data) +static int dictate_exec(struct ast_channel *chan, const char *data) { char *path = NULL, filein[256], *filename = ""; char *parse; Index: apps/app_festival.c =================================================================== --- a/apps/app_festival.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_festival.c (.../trunk) (revision 202568) @@ -264,7 +264,7 @@ return res; } -static int festival_exec(struct ast_channel *chan, void *vdata) +static int festival_exec(struct ast_channel *chan, const char *vdata) { int usecache; int res = 0; Index: apps/app_softhangup.c =================================================================== --- a/apps/app_softhangup.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_softhangup.c (.../trunk) (revision 202568) @@ -69,7 +69,7 @@ AST_APP_OPTION('a', OPTION_ALL), }); -static int softhangup_exec(struct ast_channel *chan, void *data) +static int softhangup_exec(struct ast_channel *chan, const char *data) { struct ast_channel *c = NULL; char *cut, *opts[0]; @@ -80,6 +80,7 @@ AST_APP_ARG(channel); AST_APP_ARG(options); ); + struct ast_channel_iterator *iter; if (ast_strlen_zero(data)) { ast_log(LOG_WARNING, "SoftHangup requires an argument (Technology/resource)\n"); @@ -93,9 +94,12 @@ ast_app_parse_options(app_opts, &flags, opts, args.options); lenmatch = strlen(args.channel); - for (c = ast_walk_channel_by_name_prefix_locked(NULL, args.channel, lenmatch); - c; - c = ast_walk_channel_by_name_prefix_locked(c, args.channel, lenmatch)) { + if (!(iter = ast_channel_iterator_by_name_new(0, args.channel, lenmatch))) { + return -1; + } + + while ((c = ast_channel_iterator_next(iter))) { + ast_channel_lock(c); ast_copy_string(name, c->name, sizeof(name)); if (ast_test_flag(&flags, OPTION_ALL)) { /* CAPI is set up like CAPI[foo/bar]/clcnt */ @@ -113,12 +117,16 @@ ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT); if (!ast_test_flag(&flags, OPTION_ALL)) { ast_channel_unlock(c); + c = ast_channel_unref(c); break; } } ast_channel_unlock(c); + c = ast_channel_unref(c); } + ast_channel_iterator_destroy(iter); + return 0; } Index: apps/app_playtones.c =================================================================== --- a/apps/app_playtones.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_playtones.c (.../trunk) (revision 202568) @@ -72,7 +72,7 @@ ***/ -static int handle_playtones(struct ast_channel *chan, void *data) +static int handle_playtones(struct ast_channel *chan, const char *data) { struct ast_tone_zone_sound *ts; int res; @@ -99,7 +99,7 @@ return res; } -static int handle_stopplaytones(struct ast_channel *chan, void *data) +static int handle_stopplaytones(struct ast_channel *chan, const char *data) { ast_playtones_stop(chan); Index: apps/app_alarmreceiver.c =================================================================== --- a/apps/app_alarmreceiver.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_alarmreceiver.c (.../trunk) (revision 202568) @@ -62,7 +62,7 @@ typedef struct event_node event_node_t; -static char *app = "AlarmReceiver"; +static const char app[] = "AlarmReceiver"; /*** DOCUMENTATION @@ -416,7 +416,7 @@ * * The function will return 0 when the caller hangs up, else a -1 if there was a problem. */ -static int receive_ademco_contact_id( struct ast_channel *chan, void *data, int fdto, int sdto, int tldn, event_node_t **ehead) +static int receive_ademco_contact_id(struct ast_channel *chan, const void *data, int fdto, int sdto, int tldn, event_node_t **ehead) { int i, j; int res = 0; @@ -564,7 +564,7 @@ * This is the main function called by Asterisk Core whenever the App is invoked in the extension logic. * This function will always return 0. */ -static int alarmreceiver_exec(struct ast_channel *chan, void *data) +static int alarmreceiver_exec(struct ast_channel *chan, const char *data) { int res = 0; event_node_t *elp, *efree; Index: apps/app_image.c =================================================================== --- a/apps/app_image.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_image.c (.../trunk) (revision 202568) @@ -69,7 +69,7 @@ ***/ -static int sendimage_exec(struct ast_channel *chan, void *data) +static int sendimage_exec(struct ast_channel *chan, const char *data) { if (ast_strlen_zero(data)) { Index: apps/app_getcpeid.c =================================================================== --- a/apps/app_getcpeid.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_getcpeid.c (.../trunk) (revision 202568) @@ -61,7 +61,7 @@ return ast_adsi_print(chan, tmp, justify, voice); } -static int cpeid_exec(struct ast_channel *chan, void *idata) +static int cpeid_exec(struct ast_channel *chan, const char *idata) { int res=0; unsigned char cpeid[4]; Index: apps/app_talkdetect.c =================================================================== --- a/apps/app_talkdetect.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_talkdetect.c (.../trunk) (revision 202568) @@ -73,7 +73,7 @@ static char *app = "BackgroundDetect"; -static int background_detect_exec(struct ast_channel *chan, void *data) +static int background_detect_exec(struct ast_channel *chan, const char *data) { int res = 0; char *tmp; Index: apps/app_db.c =================================================================== --- a/apps/app_db.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_db.c (.../trunk) (revision 202568) @@ -79,11 +79,10 @@ ***/ -/*! \todo XXX Remove this application after 1.4 is relased */ -static char *d_app = "DBdel"; -static char *dt_app = "DBdeltree"; +static const char d_app[] = "DBdel"; +static const char dt_app[] = "DBdeltree"; -static int deltree_exec(struct ast_channel *chan, void *data) +static int deltree_exec(struct ast_channel *chan, const char *data) { char *argv, *family, *keytree; @@ -114,7 +113,7 @@ return 0; } -static int del_exec(struct ast_channel *chan, void *data) +static int del_exec(struct ast_channel *chan, const char *data) { char *argv, *family, *key; static int deprecation_warning = 0; Index: apps/app_controlplayback.c =================================================================== --- a/apps/app_controlplayback.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_controlplayback.c (.../trunk) (revision 202568) @@ -92,7 +92,7 @@ ***/ -static const char *app = "ControlPlayback"; +static const char app[] = "ControlPlayback"; enum { OPT_OFFSET = (1 << 1), @@ -125,7 +125,7 @@ return 0; } -static int controlplayback_exec(struct ast_channel *chan, void *data) +static int controlplayback_exec(struct ast_channel *chan, const char *data) { int res = 0; int skipms = 0; Index: apps/app_setcallerid.c =================================================================== --- a/apps/app_setcallerid.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_setcallerid.c (.../trunk) (revision 202568) @@ -84,7 +84,7 @@ static char *app2 = "SetCallerPres"; -static int setcallerid_pres_exec(struct ast_channel *chan, void *data) +static int setcallerid_pres_exec(struct ast_channel *chan, const char *data) { int pres = -1; static int deprecated = 0; Index: apps/Makefile =================================================================== --- a/apps/Makefile (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/Makefile (.../trunk) (revision 202568) @@ -18,11 +18,9 @@ MENUSELECT_OPTS_app_directory:=$(MENUSELECT_OPTS_app_voicemail) ifneq ($(findstring ODBC_STORAGE,$(MENUSELECT_OPTS_app_voicemail)),) MENUSELECT_DEPENDS_app_voicemail+=$(MENUSELECT_DEPENDS_ODBC_STORAGE) - MENUSELECT_DEPENDS_app_directory+=$(MENUSELECT_DEPENDS_ODBC_STORAGE) endif ifneq ($(findstring IMAP_STORAGE,$(MENUSELECT_OPTS_app_voicemail)),) MENUSELECT_DEPENDS_app_voicemail+=$(MENUSELECT_DEPENDS_IMAP_STORAGE) - MENUSELECT_DEPENDS_app_directory+=$(MENUSELECT_DEPENDS_IMAP_STORAGE) endif all: _all Index: apps/app_mp3.c =================================================================== --- a/apps/app_mp3.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_mp3.c (.../trunk) (revision 202568) @@ -64,7 +64,7 @@ ***/ static char *app = "MP3Player"; -static int mp3play(char *filename, int fd) +static int mp3play(const char *filename, int fd) { int res; @@ -117,7 +117,7 @@ } -static int mp3_exec(struct ast_channel *chan, void *data) +static int mp3_exec(struct ast_channel *chan, const char *data) { int res=0; int fds[2]; @@ -152,8 +152,8 @@ return -1; } - res = mp3play((char *)data, fds[1]); - if (!strncasecmp((char *)data, "http://", 7)) { + res = mp3play(data, fds[1]); + if (!strncasecmp(data, "http://", 7)) { timeout = 10000; } /* Wait 1000 ms first */ Index: apps/app_zapateller.c =================================================================== --- a/apps/app_zapateller.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_zapateller.c (.../trunk) (revision 202568) @@ -74,7 +74,7 @@ static char *app = "Zapateller"; -static int zapateller_exec(struct ast_channel *chan, void *data) +static int zapateller_exec(struct ast_channel *chan, const char *data) { int res = 0; int i, answer = 0, nocallerid = 0; Index: apps/app_senddtmf.c =================================================================== --- a/apps/app_senddtmf.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_senddtmf.c (.../trunk) (revision 202568) @@ -59,10 +59,27 @@ Read + + + Play DTMF signal on a specific channel. + + + + + Channel name to send digit to. + + + The DTMF digit to play. + + + + Plays a dtmf digit on the specified channel. + + ***/ static char *app = "SendDTMF"; -static int senddtmf_exec(struct ast_channel *chan, void *vdata) +static int senddtmf_exec(struct ast_channel *chan, const char *vdata) { int res = 0; char *data; @@ -90,33 +107,29 @@ return res; } -static char mandescr_playdtmf[] = -"Description: Plays a dtmf digit on the specified channel.\n" -"Variables: (all are required)\n" -" Channel: Channel name to send digit to\n" -" Digit: The dtmf digit to play\n"; - static int manager_play_dtmf(struct mansession *s, const struct message *m) { const char *channel = astman_get_header(m, "Channel"); const char *digit = astman_get_header(m, "Digit"); - struct ast_channel *chan = ast_get_channel_by_name_locked(channel); - - if (!chan) { - astman_send_error(s, m, "Channel not specified"); + struct ast_channel *chan; + + if (!(chan = ast_channel_get_by_name(channel))) { + astman_send_error(s, m, "Channel not found"); return 0; } + if (ast_strlen_zero(digit)) { astman_send_error(s, m, "No digit specified"); - ast_channel_unlock(chan); + chan = ast_channel_unref(chan); return 0; } ast_senddigit(chan, *digit, 0); - ast_channel_unlock(chan); + chan = ast_channel_unref(chan); + astman_send_ack(s, m, "DTMF successfully queued"); - + return 0; } @@ -134,7 +147,7 @@ { int res; - res = ast_manager_register2( "PlayDTMF", EVENT_FLAG_CALL, manager_play_dtmf, "Play DTMF signal on a specific channel.", mandescr_playdtmf ); + res = ast_manager_register_xml("PlayDTMF", EVENT_FLAG_CALL, manager_play_dtmf); res |= ast_register_application_xml(app, senddtmf_exec); return res; Index: apps/app_rpt.c =================================================================== --- a/apps/app_rpt.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_rpt.c (.../trunk) (revision 202568) @@ -439,7 +439,7 @@ static int debug = 0; /* Set this >0 for extra debug output */ static int nrpts = 0; -static char remdtmfstr[] = "0123456789*#ABCD"; +static const char remdtmfstr[] = "0123456789*#ABCD"; enum {TOP_TOP,TOP_WON,WON_BEFREAD,BEFREAD_AFTERREAD}; @@ -1957,7 +1957,11 @@ if (!myrpt->p.statpost_url) return; str = ast_malloc(strlen(pairs) + strlen(myrpt->p.statpost_url) + 200); astr = ast_strdup(myrpt->p.statpost_program); - if ((!str) || (!astr)) return; + if ((!str) || (!astr)) { + ast_free(str); + ast_free(astr); + return; + } n = finddelim(astr,astrs,100); if (n < 1) return; ast_mutex_lock(&myrpt->statpost_lock); @@ -2895,12 +2899,8 @@ for(j = 0; j < numoflinks; j++){ /* ast_free() all link names */ ast_free(listoflinks[j]); } - if(called_number){ - ast_free(called_number); - } - if(lastdtmfcommand){ - ast_free(lastdtmfcommand); - } + ast_free(called_number); + ast_free(lastdtmfcommand); return RESULT_SUCCESS; } } @@ -3646,8 +3646,7 @@ if(res) break; } - if(p) - ast_free(p); + ast_free(p); if(!res) res = play_tone_pair(chan, 0, 0, 100, 0); /* This is needed to ensure the last tone segment is timed correctly */ @@ -3828,8 +3827,7 @@ else{ res = -1; } - if(telemetry_save) - ast_free(telemetry_save); + ast_free(telemetry_save); return res; } @@ -3907,8 +3905,7 @@ interval = 0; break; } - if(wait_times_save) - ast_free(wait_times_save); + ast_free(wait_times_save); return interval; } @@ -3940,7 +3937,7 @@ struct rpt *myrpt; struct rpt_link *l,*l1,linkbase; struct ast_channel *mychannel; -int id_malloc, vmajor, vminor, m; +int vmajor, vminor, m; char *p,*ct,*ct_copy,*ident, *nodename,*cp; time_t t; #ifdef NEW_ASTERISK @@ -3987,14 +3984,10 @@ ast_free(mytele); pthread_exit(NULL); } - else{ - id_malloc = 1; - } } else { ident = ""; - id_malloc = 0; } rpt_mutex_unlock(&myrpt->lock); @@ -4010,8 +4003,7 @@ ast_log(LOG_NOTICE,"Telemetry thread aborted at line %d, mode: %d\n",__LINE__, mytele->mode); /*@@@@@@@@@@@*/ rpt_mutex_unlock(&myrpt->lock); ast_free(nodename); - if(id_malloc) - ast_free(ident); + ast_free(ident); ast_free(mytele); pthread_exit(NULL); } @@ -4057,8 +4049,7 @@ rpt_mutex_unlock(&myrpt->lock); ast_log(LOG_NOTICE,"Telemetry thread aborted at line %d, mode: %d\n",__LINE__, mytele->mode); /*@@@@@@@@@@@*/ ast_free(nodename); - if(id_malloc) - ast_free(ident); + ast_free(ident); ast_free(mytele); ast_hangup(mychannel); pthread_exit(NULL); @@ -4252,8 +4243,7 @@ rpt_mutex_unlock(&myrpt->lock); ast_log(LOG_NOTICE,"Telemetry thread aborted at line %d, mode: %d\n",__LINE__, mytele->mode); /*@@@@@@@@@@@*/ ast_free(nodename); - if(id_malloc) - ast_free(ident); + ast_free(ident); ast_free(mytele); ast_hangup(mychannel); pthread_exit(NULL); @@ -4293,8 +4283,7 @@ rpt_mutex_unlock(&myrpt->lock); ast_log(LOG_NOTICE,"Telemetry thread aborted at line %d, mode: %d\n",__LINE__, mytele->mode); /*@@@@@@@@@@@*/ ast_free(nodename); - if(id_malloc) - ast_free(ident); + ast_free(ident); ast_free(mytele); ast_hangup(mychannel); pthread_exit(NULL); @@ -4816,8 +4805,7 @@ rpt_mutex_unlock(&myrpt->lock); ast_log(LOG_NOTICE,"Telemetry thread aborted at line %d, mode: %d\n",__LINE__, mytele->mode); /*@@@@@@@@@@@*/ ast_free(nodename); - if(id_malloc) - ast_free(ident); + ast_free(ident); ast_free(mytele); ast_hangup(mychannel); pthread_exit(NULL); @@ -5193,8 +5181,7 @@ myrpt->active_telem = NULL; rpt_mutex_unlock(&myrpt->lock); ast_free(nodename); - if(id_malloc) - ast_free(ident); + ast_free(ident); ast_free(mytele); ast_hangup(mychannel); #ifdef APP_RPT_LOCK_DEBUG @@ -13114,7 +13101,7 @@ pthread_exit(NULL); } -static int rpt_exec(struct ast_channel *chan, void *data) +static int rpt_exec(struct ast_channel *chan, const void *data) { int res=-1,i,rem_totx,rem_rx,remkeyed,n,phone_mode = 0; int iskenwood_pci4,authtold,authreq,setting,notremming,reming; Index: apps/app_mixmonitor.c =================================================================== --- a/apps/app_mixmonitor.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_mixmonitor.c (.../trunk) (revision 202568) @@ -45,11 +45,13 @@ #include "asterisk/cli.h" #include "asterisk/app.h" #include "asterisk/channel.h" +#include "asterisk/autochan.h" /*** DOCUMENTATION - Record a call and mix the audio during the recording. + Record a call and mix the audio during the recording. Use of StopMixMonitor is required + to guarantee the audio file is available for processing during dialplan execution. @@ -111,7 +113,7 @@ - Stop recording a call through MixMonitor. + Stop recording a call through MixMonitor, and free the recording's file handle. @@ -127,37 +129,36 @@ #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0 -static const char *app = "MixMonitor"; +static const char * const app = "MixMonitor"; -static const char *stop_app = "StopMixMonitor"; +static const char * const stop_app = "StopMixMonitor"; -struct module_symbols *me; +static const char * const mixmonitor_spy_type = "MixMonitor"; -static const char *mixmonitor_spy_type = "MixMonitor"; - struct mixmonitor { struct ast_audiohook audiohook; char *filename; char *post_process; char *name; unsigned int flags; + struct ast_autochan *autochan; struct mixmonitor_ds *mixmonitor_ds; }; -enum { +enum mixmonitor_flags { MUXFLAG_APPEND = (1 << 1), MUXFLAG_BRIDGED = (1 << 2), MUXFLAG_VOLUME = (1 << 3), MUXFLAG_READVOLUME = (1 << 4), MUXFLAG_WRITEVOLUME = (1 << 5), -} mixmonitor_flags; +}; -enum { +enum mixmonitor_args { OPT_ARG_READVOLUME = 0, OPT_ARG_WRITEVOLUME, OPT_ARG_VOLUME, OPT_ARG_ARRAY_SIZE, -} mixmonitor_args; +}; AST_APP_OPTIONS(mixmonitor_opts, { AST_APP_OPTION('a', MUXFLAG_APPEND), @@ -167,48 +168,43 @@ AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME), }); -/* This structure is used as a means of making sure that our pointer to - * the channel we are monitoring remains valid. This is very similar to - * what is used in app_chanspy.c. - */ struct mixmonitor_ds { - struct ast_channel *chan; - /* These condition variables are used to be sure that the channel - * hangup code completes before the mixmonitor thread attempts to - * free this structure. The combination of a bookean flag and a - * ast_cond_t ensure that no matter what order the threads run in, - * we are guaranteed to never have the waiting thread block forever - * in the case that the signaling thread runs first. - */ unsigned int destruction_ok; ast_cond_t destruction_condition; ast_mutex_t lock; + + /* The filestream is held in the datastore so it can be stopped + * immediately during stop_mixmonitor or channel destruction. */ + int fs_quit; + struct ast_filestream *fs; + }; -static void mixmonitor_ds_destroy(void *data) +static void mixmonitor_ds_close_fs(struct mixmonitor_ds *mixmonitor_ds) { - struct mixmonitor_ds *mixmonitor_ds = data; - ast_mutex_lock(&mixmonitor_ds->lock); - mixmonitor_ds->chan = NULL; - mixmonitor_ds->destruction_ok = 1; - ast_cond_signal(&mixmonitor_ds->destruction_condition); + if (mixmonitor_ds->fs) { + ast_closestream(mixmonitor_ds->fs); + mixmonitor_ds->fs = NULL; + mixmonitor_ds->fs_quit = 1; + ast_verb(2, "MixMonitor close filestream\n"); + } ast_mutex_unlock(&mixmonitor_ds->lock); } -static void mixmonitor_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) +static void mixmonitor_ds_destroy(void *data) { struct mixmonitor_ds *mixmonitor_ds = data; ast_mutex_lock(&mixmonitor_ds->lock); - mixmonitor_ds->chan = new_chan; + mixmonitor_ds->destruction_ok = 1; + ast_cond_signal(&mixmonitor_ds->destruction_condition); ast_mutex_unlock(&mixmonitor_ds->lock); } static struct ast_datastore_info mixmonitor_ds_info = { .type = "mixmonitor", .destroy = mixmonitor_ds_destroy, - .chan_fixup = mixmonitor_ds_chan_fixup, }; static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook) @@ -229,19 +225,32 @@ #define SAMPLES_PER_FRAME 160 +static void mixmonitor_free(struct mixmonitor *mixmonitor) +{ + if (mixmonitor) { + if (mixmonitor->mixmonitor_ds) { + ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock); + ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition); + ast_free(mixmonitor->mixmonitor_ds); + } + ast_free(mixmonitor); + } +} static void *mixmonitor_thread(void *obj) { struct mixmonitor *mixmonitor = obj; - struct ast_filestream *fs = NULL; + struct ast_filestream **fs = NULL; unsigned int oflags; char *ext; int errflag = 0; ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name); - + ast_audiohook_lock(&mixmonitor->audiohook); - while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) { + fs = &mixmonitor->mixmonitor_ds->fs; + + while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING && !mixmonitor->mixmonitor_ds->fs_quit) { struct ast_frame *fr = NULL; ast_audiohook_trigger_wait(&mixmonitor->audiohook); @@ -252,32 +261,34 @@ if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR))) continue; - ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock); - if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->mixmonitor_ds->chan && ast_bridged_channel(mixmonitor->mixmonitor_ds->chan))) { - ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock); + if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->autochan->chan && ast_bridged_channel(mixmonitor->autochan->chan))) { + ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock); /* Initialize the file if not already done so */ - if (!fs && !errflag) { + if (!*fs && !errflag && !mixmonitor->mixmonitor_ds->fs_quit) { oflags = O_CREAT | O_WRONLY; oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC; - + if ((ext = strrchr(mixmonitor->filename, '.'))) *(ext++) = '\0'; else ext = "raw"; - - if (!(fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0666))) { + + if (!(*fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0666))) { ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext); errflag = 1; } } - - /* Write out frame */ - if (fs) - ast_writestream(fs, fr); - } else { + + /* Write out the frame(s) */ + if (*fs) { + struct ast_frame *cur; + + for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) { + ast_writestream(*fs, cur); + } + } ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock); } - /* All done! free it. */ ast_frame_free(fr, 0); @@ -287,26 +298,25 @@ ast_audiohook_unlock(&mixmonitor->audiohook); ast_audiohook_destroy(&mixmonitor->audiohook); - ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name); + mixmonitor_ds_close_fs(mixmonitor->mixmonitor_ds); - if (fs) - ast_closestream(fs); if (mixmonitor->post_process) { ast_verb(2, "Executing [%s]\n", mixmonitor->post_process); ast_safe_system(mixmonitor->post_process); } + ast_autochan_destroy(mixmonitor->autochan); + ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock); if (!mixmonitor->mixmonitor_ds->destruction_ok) { ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock); } ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock); - ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock); - ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition); - ast_free(mixmonitor->mixmonitor_ds); - ast_free(mixmonitor); + + ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name); + mixmonitor_free(mixmonitor); return NULL; } @@ -318,17 +328,17 @@ if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) { return -1; } - + ast_mutex_init(&mixmonitor_ds->lock); ast_cond_init(&mixmonitor_ds->destruction_condition, NULL); if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, NULL))) { + ast_mutex_destroy(&mixmonitor_ds->lock); + ast_cond_destroy(&mixmonitor_ds->destruction_condition); ast_free(mixmonitor_ds); return -1; } - /* No need to lock mixmonitor_ds since this is still operating in the channel's thread */ - mixmonitor_ds->chan = chan; datastore->data = mixmonitor_ds; ast_channel_lock(chan); @@ -372,7 +382,14 @@ /* Copy over flags and channel name */ mixmonitor->flags = flags; + if (!(mixmonitor->autochan = ast_autochan_setup(chan))) { + mixmonitor_free(mixmonitor); + return; + } + if (setup_mixmonitor_ds(mixmonitor, chan)) { + ast_autochan_destroy(mixmonitor->autochan); + mixmonitor_free(mixmonitor); return; } mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor); @@ -409,7 +426,7 @@ ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor); } -static int mixmonitor_exec(struct ast_channel *chan, void *data) +static int mixmonitor_exec(struct ast_channel *chan, const char *data) { int x, readvol = 0, writevol = 0; struct ast_flags flags = {0}; @@ -490,8 +507,16 @@ return 0; } -static int stop_mixmonitor_exec(struct ast_channel *chan, void *data) +static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data) { + struct ast_datastore *datastore = NULL; + + /* closing the filestream here guarantees the file is avaliable to the dialplan + * after calling StopMixMonitor */ + if ((datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, NULL))) { + mixmonitor_ds_close_fs(datastore->data); + } + ast_audiohook_detach_source(chan, mixmonitor_spy_type); return 0; } @@ -515,12 +540,14 @@ if (a->argc < 3) return CLI_SHOWUSAGE; - if (!(chan = ast_get_channel_by_name_prefix_locked(a->argv[2], strlen(a->argv[2])))) { + if (!(chan = ast_channel_get_by_name_prefix(a->argv[2], strlen(a->argv[2])))) { ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]); /* Technically this is a failure, but we don't want 2 errors printing out */ return CLI_SUCCESS; } + ast_channel_lock(chan); + if (!strcasecmp(a->argv[1], "start")) { mixmonitor_exec(chan, a->argv[3]); ast_channel_unlock(chan); @@ -529,6 +556,8 @@ ast_audiohook_detach_source(chan, mixmonitor_spy_type); } + chan = ast_channel_unref(chan); + return CLI_SUCCESS; } Index: apps/app_ivrdemo.c =================================================================== --- a/apps/app_ivrdemo.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_ivrdemo.c (.../trunk) (revision 202568) @@ -59,7 +59,7 @@ static int ivr_demo_func(struct ast_channel *chan, void *data) { - ast_verbose("IVR Demo, data is %s!\n", (char *)data); + ast_verbose("IVR Demo, data is %s!\n", (char *) data); return 0; } @@ -93,22 +93,24 @@ { NULL }, }); - -static int skel_exec(struct ast_channel *chan, void *data) +static int skel_exec(struct ast_channel *chan, const char *data) { int res=0; + char *tmp; if (ast_strlen_zero(data)) { ast_log(LOG_WARNING, "skel requires an argument (filename)\n"); return -1; } + tmp = ast_strdupa(data); + /* Do our thing here */ if (chan->_state != AST_STATE_UP) res = ast_answer(chan); if (!res) - res = ast_ivr_menu_run(chan, &ivr_demo, data); + res = ast_ivr_menu_run(chan, &ivr_demo, tmp); return res; } Index: apps/app_milliwatt.c =================================================================== --- a/apps/app_milliwatt.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_milliwatt.c (.../trunk) (revision 202568) @@ -55,9 +55,9 @@ ***/ -static char *app = "Milliwatt"; +static const char app[] = "Milliwatt"; -static char digital_milliwatt[] = {0x1e,0x0b,0x0b,0x1e,0x9e,0x8b,0x8b,0x9e} ; +static const char digital_milliwatt[] = {0x1e,0x0b,0x0b,0x1e,0x9e,0x8b,0x8b,0x9e} ; static void *milliwatt_alloc(struct ast_channel *chan, void *params) { @@ -139,7 +139,7 @@ return -1; } -static int milliwatt_exec(struct ast_channel *chan, void *data) +static int milliwatt_exec(struct ast_channel *chan, const char *data) { const char *options = data; struct ast_app *playtones_app; Index: apps/app_parkandannounce.c =================================================================== --- a/apps/app_parkandannounce.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_parkandannounce.c (.../trunk) (revision 202568) @@ -85,7 +85,7 @@ static char *app = "ParkAndAnnounce"; -static int parkandannounce_exec(struct ast_channel *chan, void *data) +static int parkandannounce_exec(struct ast_channel *chan, const char *data) { int res = -1; int lot, timeout = 0, dres; Index: apps/app_readfile.c =================================================================== --- a/apps/app_readfile.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_readfile.c (.../trunk) (revision 202568) @@ -67,7 +67,7 @@ static char *app_readfile = "ReadFile"; -static int readfile_exec(struct ast_channel *chan, void *data) +static int readfile_exec(struct ast_channel *chan, const char *data) { int res=0; char *s, *varname=NULL, *file=NULL, *length=NULL, *returnvar=NULL; Index: apps/app_meetme.c =================================================================== --- a/apps/app_meetme.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_meetme.c (.../trunk) (revision 202568) @@ -420,6 +420,80 @@ + + + Query a given conference of various properties. + + + + Options: + + + Boolean of whether the corresponding conference is locked. + + + Number of parties in a given conference + + + Duration of conference in seconds. + + + Boolean of whether the corresponding conference is dynamic. + + + + + Conference number to retrieve information from. + + + + + MeetMe + MeetMeCount + MeetMeAdmin + MeetMeChannelAdmin + + + + + Mute a Meetme user. + + + + + + + + + + + + Unmute a Meetme user. + + + + + + + + + + + + List participants in a conference. + + + + + Conference number. + + + + Lists all users in a particular MeetMe conference. + MeetmeList will follow as separate events, followed by a final event called + MeetmeListComplete. + + ***/ #define CONFIG_FILE_NAME "meetme.conf" @@ -565,12 +639,12 @@ AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT), END_OPTIONS ); -static const char *app = "MeetMe"; -static const char *app2 = "MeetMeCount"; -static const char *app3 = "MeetMeAdmin"; -static const char *app4 = "MeetMeChannelAdmin"; -static const char *slastation_app = "SLAStation"; -static const char *slatrunk_app = "SLATrunk"; +static const char * const app = "MeetMe"; +static const char * const app2 = "MeetMeCount"; +static const char * const app3 = "MeetMeAdmin"; +static const char * const app4 = "MeetMeChannelAdmin"; +static const char * const slastation_app = "SLAStation"; +static const char * const slatrunk_app = "SLATrunk"; /* Lookup RealTime conferences based on confno and current time */ static int rt_schedule; @@ -861,7 +935,7 @@ * conversion... the numbers have been modified * to give the user a better level of adjustability */ -static char const gain_map[] = { +static const char gain_map[] = { -15, -13, -10, @@ -876,7 +950,7 @@ }; -static int admin_exec(struct ast_channel *chan, void *data); +static int admin_exec(struct ast_channel *chan, const char *data); static void *recordthread(void *args); static char *istalking(int x) @@ -1144,7 +1218,7 @@ static char *complete_meetmecmd(const char *line, const char *word, int pos, int state) { - static char *cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL}; + static const char * const cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL}; int len = strlen(word); int which = 0; @@ -3195,9 +3269,18 @@ } } if (conf->transframe[idx]) { - if (conf->transframe[idx]->frametype != AST_FRAME_NULL) { - if (can_write(chan, confflags) && ast_write(chan, conf->transframe[idx])) { - ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name); + if ((conf->transframe[idx]->frametype != AST_FRAME_NULL) && + can_write(chan, confflags)) { + struct ast_frame *cur; + + /* the translator may have returned a list of frames, so + write each one onto the channel + */ + for (cur = conf->transframe[idx]; cur; cur = AST_LIST_NEXT(cur, frame_list)) { + if (ast_write(chan, cur)) { + ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name); + break; + } } } } else { @@ -3600,7 +3683,7 @@ } /*! \brief The MeetmeCount application */ -static int count_exec(struct ast_channel *chan, void *data) +static int count_exec(struct ast_channel *chan, const char *data) { int res = 0; struct ast_conference *conf; @@ -3646,7 +3729,7 @@ } /*! \brief The meetme() application */ -static int conf_exec(struct ast_channel *chan, void *data) +static int conf_exec(struct ast_channel *chan, const char *data) { int res = -1; char confno[MAX_CONFNUM] = ""; @@ -3657,7 +3740,8 @@ int dynamic = 0; int empty = 0, empty_no_pin = 0; int always_prompt = 0; - char *notdata, *info, the_pin[MAX_PIN] = ""; + const char *notdata; + char *info, the_pin[MAX_PIN] = ""; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(confno); AST_APP_ARG(options); @@ -3920,7 +4004,7 @@ /*! \brief The MeetMeadmin application */ /* MeetMeAdmin(confno, command, caller) */ -static int admin_exec(struct ast_channel *chan, void *data) { +static int admin_exec(struct ast_channel *chan, const char *data) { char *params; struct ast_conference *cnf; struct ast_conf_user *user = NULL; @@ -4102,7 +4186,7 @@ /*--- channel_admin_exec: The MeetMeChannelAdmin application */ /* MeetMeChannelAdmin(channel, command) */ -static int channel_admin_exec(struct ast_channel *chan, void *data) { +static int channel_admin_exec(struct ast_channel *chan, const char *data) { char *params; struct ast_conference *conf = NULL; struct ast_conf_user *user = NULL; @@ -4235,14 +4319,6 @@ return meetmemute(s, m, 0); } -static char mandescr_meetmelist[] = -"Description: Lists all users in a particular MeetMe conference.\n" -"MeetmeList will follow as separate events, followed by a final event called\n" -"MeetmeListComplete.\n" -"Variables:\n" -" *ActionId: \n" -" *Conference: \n"; - static int action_meetmelist(struct mansession *s, const struct message *m) { const char *actionid = astman_get_header(m, "ActionID"); @@ -5552,7 +5628,7 @@ return trunk_ref; } -static int sla_station_exec(struct ast_channel *chan, void *data) +static int sla_station_exec(struct ast_channel *chan, const char *data) { char *station_name, *trunk_name; struct sla_station *station; @@ -5759,7 +5835,7 @@ AST_APP_OPTION_ARG('M', SLA_TRUNK_OPT_MOH, SLA_TRUNK_OPT_ARG_MOH_CLASS), END_OPTIONS ); -static int sla_trunk_exec(struct ast_channel *chan, void *data) +static int sla_trunk_exec(struct ast_channel *chan, const char *data) { char conf_name[MAX_CONFNUM]; struct ast_conference *conf; @@ -6357,16 +6433,7 @@ static struct ast_custom_function meetme_info_acf = { .name = "MEETME_INFO", - .synopsis = "Query a given conference of various properties.", - .syntax = "MEETME_INFO(,)", .read = acf_meetme_info, - .desc = -"Returns information from a given keyword. (For booleans 1-true, 0-false)\n" -" Options:\n" -" lock - boolean of whether the corresponding conference is locked\n" -" parties - number of parties in a given conference\n" -" activity - duration of conference in seconds\n" -" dynamic - boolean of whether the corresponding coference is dynamic\n", }; @@ -6417,12 +6484,9 @@ res |= load_config(0); ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme)); - res |= ast_manager_register("MeetmeMute", EVENT_FLAG_CALL, - action_meetmemute, "Mute a Meetme user"); - res |= ast_manager_register("MeetmeUnmute", EVENT_FLAG_CALL, - action_meetmeunmute, "Unmute a Meetme user"); - res |= ast_manager_register2("MeetmeList", EVENT_FLAG_REPORTING, - action_meetmelist, "List participants in a conference", mandescr_meetmelist); + res |= ast_manager_register_xml("MeetmeMute", EVENT_FLAG_CALL, action_meetmemute); + res |= ast_manager_register_xml("MeetmeUnmute", EVENT_FLAG_CALL, action_meetmeunmute); + res |= ast_manager_register_xml("MeetmeList", EVENT_FLAG_REPORTING, action_meetmelist); res |= ast_register_application_xml(app4, channel_admin_exec); res |= ast_register_application_xml(app3, admin_exec); res |= ast_register_application_xml(app2, count_exec); Index: apps/app_test.c =================================================================== --- a/apps/app_test.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_test.c (.../trunk) (revision 202568) @@ -132,7 +132,7 @@ return (noise / samples); } -static int sendnoise(struct ast_channel *chan, int ms) +static int sendnoise(struct ast_channel *chan, int ms) { int res; res = ast_tonepair_start(chan, 1537, 2195, ms, 8192); @@ -140,51 +140,51 @@ res = ast_waitfordigit(chan, ms); ast_tonepair_stop(chan); } - return res; + return res; } -static int testclient_exec(struct ast_channel *chan, void *data) +static int testclient_exec(struct ast_channel *chan, const char *data) { int res = 0; - char *testid=data; + const char *testid=data; char fn[80]; char serverver[80]; FILE *f; - + /* Check for test id */ if (ast_strlen_zero(testid)) { ast_log(LOG_WARNING, "TestClient requires an argument - the test id\n"); return -1; } - + if (chan->_state != AST_STATE_UP) res = ast_answer(chan); - + /* Wait a few just to be sure things get started */ res = ast_safe_sleep(chan, 3000); /* Transmit client version */ if (!res) res = ast_dtmf_stream(chan, NULL, "8378*1#", 0, 0); ast_debug(1, "Transmit client version\n"); - + /* Read server version */ ast_debug(1, "Read server version\n"); - if (!res) + if (!res) res = ast_app_getdata(chan, NULL, serverver, sizeof(serverver) - 1, 0); if (res > 0) res = 0; ast_debug(1, "server version: %s\n", serverver); - + if (res > 0) res = 0; if (!res) res = ast_safe_sleep(chan, 1000); /* Send test id */ - if (!res) - res = ast_dtmf_stream(chan, NULL, testid, 0, 0); - if (!res) - res = ast_dtmf_stream(chan, NULL, "#", 0, 0); + if (!res) + res = ast_dtmf_stream(chan, NULL, testid, 0, 0); + if (!res) + res = ast_dtmf_stream(chan, NULL, "#", 0, 0); ast_debug(1, "send test identifier: %s\n", testid); if ((res >=0) && (!ast_strlen_zero(testid))) { @@ -198,7 +198,7 @@ fprintf(f, "CLIENTTEST ID: %s\n", testid); fprintf(f, "ANSWER: PASS\n"); res = 0; - + if (!res) { /* Step 1: Wait for "1" */ ast_debug(1, "TestClient: 2. Wait DTMF 1\n"); @@ -209,8 +209,9 @@ else res = -1; } - if (!res) + if (!res) { res = ast_safe_sleep(chan, 1000); + } if (!res) { /* Step 2: Send "2" */ ast_debug(1, "TestClient: 2. Send DTMF 2\n"); @@ -226,7 +227,7 @@ fprintf(f, "WAIT 1 SEC: %s\n", (res < 0) ? "FAIL" : "PASS"); if (res > 0) res = 0; - } + } if (!res) { /* Step 4: Measure noise */ ast_debug(1, "TestClient: 4. Measure noise\n"); @@ -272,7 +273,7 @@ } if (!res) { /* Step 9: Measure noise */ - ast_debug(1, "TestClient: 6. Measure tone\n"); + ast_debug(1, "TestClient: 9. Measure tone\n"); res = measurenoise(chan, 4000, "TestClient"); fprintf(f, "MEASURETONE: %s (%d)\n", (res < 0) ? "FAIL" : "PASS", res); if (res > 0) @@ -280,7 +281,7 @@ } if (!res) { /* Step 10: Send "7" */ - ast_debug(1, "TestClient: 7. Send DTMF 7\n"); + ast_debug(1, "TestClient: 10. Send DTMF 7\n"); res = ast_dtmf_stream(chan, NULL, "7", 0, 0); fprintf(f, "SEND DTMF 7: %s\n", (res < 0) ? "FAIL" : "PASS"); if (res > 0) @@ -297,6 +298,9 @@ res = -1; } if (!res) { + res = ast_safe_sleep(chan, 1000); + } + if (!res) { /* Step 12: Hangup! */ ast_debug(1, "TestClient: 12. Hangup\n"); } @@ -314,7 +318,7 @@ return res; } -static int testserver_exec(struct ast_channel *chan, void *data) +static int testserver_exec(struct ast_channel *chan, const char *data) { int res = 0; char testid[80]=""; @@ -324,7 +328,7 @@ res = ast_answer(chan); /* Read version */ ast_debug(1, "Read client version\n"); - if (!res) + if (!res) res = ast_app_getdata(chan, NULL, testid, sizeof(testid) - 1, 0); if (res > 0) res = 0; @@ -338,8 +342,8 @@ if (res > 0) res = 0; - if (!res) - res = ast_app_getdata(chan, NULL, testid, sizeof(testid) - 1, 0); + if (!res) + res = ast_app_getdata(chan, NULL, testid, sizeof(testid) - 1, 0); ast_debug(1, "read test identifier: %s\n", testid); /* Check for sneakyness */ if (strchr(testid, '/')) @@ -391,7 +395,6 @@ if (res > 0) res = 0; } - if (!res) { /* Step 5: Wait one second */ ast_debug(1, "TestServer: 5. Wait one second\n"); @@ -400,7 +403,6 @@ if (res > 0) res = 0; } - if (!res) { /* Step 6: Measure noise */ ast_debug(1, "TestServer: 6. Measure tone\n"); @@ -409,7 +411,6 @@ if (res > 0) res = 0; } - if (!res) { /* Step 7: Send "5" */ ast_debug(1, "TestServer: 7. Send DTMF 5\n"); @@ -418,14 +419,13 @@ if (res > 0) res = 0; } - if (!res) { /* Step 8: Transmit tone noise */ ast_debug(1, "TestServer: 8. Transmit tone\n"); res = sendnoise(chan, 6000); fprintf(f, "SENDTONE: %s\n", (res < 0) ? "FAIL" : "PASS"); } - + if (!res || (res == '7')) { /* Step 9: Wait for "7" */ ast_debug(1, "TestServer: 9. Wait DTMF 7\n"); @@ -437,8 +437,9 @@ else res = -1; } - if (!res) + if (!res) { res = ast_safe_sleep(chan, 1000); + } if (!res) { /* Step 10: Send "8" */ ast_debug(1, "TestServer: 10. Send DTMF 8\n"); @@ -474,7 +475,7 @@ res = ast_unregister_application(testc_app); res |= ast_unregister_application(tests_app); - return res; + return res; } static int load_module(void) Index: apps/app_morsecode.c =================================================================== --- a/apps/app_morsecode.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_morsecode.c (.../trunk) (revision 202568) @@ -63,9 +63,9 @@ ***/ -static char *app_morsecode = "Morsecode"; +static const char app_morsecode[] = "Morsecode"; -static char *morsecode[] = { +static const char * const morsecode[] = { "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", /* 0-15 */ "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", /* 16-31 */ " ", /* 32 - */ @@ -118,10 +118,10 @@ ast_playtones_stop(chan); } -static int morsecode_exec(struct ast_channel *chan, void *data) +static int morsecode_exec(struct ast_channel *chan, const char *data) { int res=0, ditlen, tone; - char *digit; + const char *digit; const char *ditlenc, *tonec; if (ast_strlen_zero(data)) { @@ -147,7 +147,7 @@ for (digit = data; *digit; digit++) { int digit2 = *digit; - char *dahdit; + const char *dahdit; if (digit2 < 0) { continue; } Index: apps/app_ices.c =================================================================== --- a/apps/app_ices.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_ices.c (.../trunk) (revision 202568) @@ -58,7 +58,7 @@ Streams to an icecast server using ices (available separately). A configuration file must be supplied for ices (see contrib/asterisk-ices.xml). - ICES version 2 cient and server required. + ICES version 2 client and server required. @@ -104,7 +104,7 @@ _exit(0); } -static int ices_exec(struct ast_channel *chan, void *data) +static int ices_exec(struct ast_channel *chan, const char *data) { int res = 0; int fds[2]; Index: apps/app_exec.c =================================================================== --- a/apps/app_exec.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_exec.c (.../trunk) (revision 202568) @@ -125,63 +125,68 @@ * affecting the dialplan. */ -static char *app_exec = "Exec"; -static char *app_tryexec = "TryExec"; -static char *app_execif = "ExecIf"; +static const char app_exec[] = "Exec"; +static const char app_tryexec[] = "TryExec"; +static const char app_execif[] = "ExecIf"; -static int exec_exec(struct ast_channel *chan, void *data) +static int exec_exec(struct ast_channel *chan, const char *data) { int res = 0; - char *s, *appname, *endargs, args[MAXRESULT]; + char *s, *appname, *endargs; struct ast_app *app; + struct ast_str *args = NULL; if (ast_strlen_zero(data)) return 0; s = ast_strdupa(data); - args[0] = 0; appname = strsep(&s, "("); if (s) { endargs = strrchr(s, ')'); if (endargs) *endargs = '\0'; - pbx_substitute_variables_helper(chan, s, args, MAXRESULT - 1); + if ((args = ast_str_create(16))) { + ast_str_substitute_variables(&args, 0, chan, s); + } } if (appname) { app = pbx_findapp(appname); if (app) { - res = pbx_exec(chan, app, args); + res = pbx_exec(chan, app, args ? ast_str_buffer(args) : NULL); } else { ast_log(LOG_WARNING, "Could not find application (%s)\n", appname); res = -1; } } + ast_free(args); return res; } -static int tryexec_exec(struct ast_channel *chan, void *data) +static int tryexec_exec(struct ast_channel *chan, const char *data) { int res = 0; - char *s, *appname, *endargs, args[MAXRESULT]; + char *s, *appname, *endargs; struct ast_app *app; + struct ast_str *args = NULL; if (ast_strlen_zero(data)) return 0; s = ast_strdupa(data); - args[0] = 0; appname = strsep(&s, "("); if (s) { endargs = strrchr(s, ')'); if (endargs) *endargs = '\0'; - pbx_substitute_variables_helper(chan, s, args, MAXRESULT - 1); + if ((args = ast_str_create(16))) { + ast_str_substitute_variables(&args, 0, chan, s); + } } if (appname) { app = pbx_findapp(appname); if (app) { - res = pbx_exec(chan, app, args); + res = pbx_exec(chan, app, args ? ast_str_buffer(args) : NULL); pbx_builtin_setvar_helper(chan, "TRYSTATUS", res ? "FAILED" : "SUCCESS"); } else { ast_log(LOG_WARNING, "Could not find application (%s)\n", appname); @@ -189,10 +194,11 @@ } } + ast_free(args); return 0; } -static int execif_exec(struct ast_channel *chan, void *data) +static int execif_exec(struct ast_channel *chan, const char *data) { int res = 0; char *truedata = NULL, *falsedata = NULL, *end, *firstcomma, *firstquestion; Index: apps/app_channelredirect.c =================================================================== --- a/apps/app_channelredirect.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_channelredirect.c (.../trunk) (revision 202568) @@ -60,9 +60,9 @@ ***/ -static char *app = "ChannelRedirect"; +static const char app[] = "ChannelRedirect"; -static int asyncgoto_exec(struct ast_channel *chan, void *data) +static int asyncgoto_exec(struct ast_channel *chan, const char *data) { int res = -1; char *info; @@ -86,8 +86,7 @@ return -1; } - chan2 = ast_get_channel_by_name_locked(args.channel); - if (!chan2) { + if (!(chan2 = ast_channel_get_by_name(args.channel))) { ast_log(LOG_WARNING, "No such channel: %s\n", args.channel); pbx_builtin_setvar_helper(chan, "CHANNELREDIRECT_STATUS", "NOCHANNEL"); return 0; @@ -96,9 +95,12 @@ if (chan2->pbx) { ast_set_flag(chan2, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */ } + res = ast_async_parseable_goto(chan2, args.label); + + chan2 = ast_channel_unref(chan2); + pbx_builtin_setvar_helper(chan, "CHANNELREDIRECT_STATUS", "SUCCESS"); - ast_channel_unlock(chan2); return res; } Index: apps/app_forkcdr.c =================================================================== --- a/apps/app_forkcdr.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_forkcdr.c (.../trunk) (revision 202568) @@ -226,7 +226,7 @@ ast_set_flag(cdr, AST_CDR_FLAG_CHILD | AST_CDR_FLAG_LOCKED); } -static int forkcdr_exec(struct ast_channel *chan, void *data) +static int forkcdr_exec(struct ast_channel *chan, const char *data) { int res = 0; char *argcopy = NULL; Index: apps/app_skel.c =================================================================== --- a/apps/app_skel.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_skel.c (.../trunk) (revision 202568) @@ -74,18 +74,18 @@ static char *app = "Skel"; -enum { +enum option_flags { OPTION_A = (1 << 0), OPTION_B = (1 << 1), OPTION_C = (1 << 2), -} option_flags; +}; -enum { +enum option_args { OPTION_ARG_B = 0, OPTION_ARG_C = 1, /* This *must* be the last value in this enum! */ OPTION_ARG_ARRAY_SIZE = 2, -} option_args; +}; AST_APP_OPTIONS(app_opts,{ AST_APP_OPTION('a', OPTION_A), @@ -94,7 +94,7 @@ }); -static int app_exec(struct ast_channel *chan, void *data) +static int app_exec(struct ast_channel *chan, const char *data) { int res = 0; struct ast_flags flags; Index: apps/app_directed_pickup.c =================================================================== --- a/apps/app_directed_pickup.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_directed_pickup.c (.../trunk) (revision 202568) @@ -40,6 +40,7 @@ #include "asterisk/lock.h" #include "asterisk/app.h" #include "asterisk/features.h" +#include "asterisk/callerid.h" #define PICKUPMARK "PICKUPMARK" @@ -83,17 +84,31 @@ ***/ -static const char *app = "Pickup"; -static const char *app2 = "PickupChan"; +static const char app[] = "Pickup"; +static const char app2[] = "PickupChan"; /*! \todo This application should return a result code, like PICKUPRESULT */ /* Perform actual pickup between two channels */ static int pickup_do(struct ast_channel *chan, struct ast_channel *target) { int res = 0; + struct ast_party_connected_line connected_caller; ast_debug(1, "Call pickup on '%s' by '%s'\n", target->name, chan->name); + connected_caller = target->connected; + connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; + if (ast_channel_connected_line_macro(NULL, chan, &connected_caller, 0, 0)) { + ast_channel_update_connected_line(chan, &connected_caller); + } + + ast_channel_lock(chan); + ast_connected_line_copy_from_caller(&connected_caller, &chan->cid); + ast_channel_unlock(chan); + connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; + ast_channel_queue_connected_line_update(chan, &connected_caller); + ast_party_connected_line_free(&connected_caller); + if ((res = ast_answer(chan))) { ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name); return -1; @@ -121,17 +136,35 @@ return 0; } +struct pickup_by_name_args { + const char *name; + size_t len; +}; + +static int pickup_by_name_cb(void *obj, void *arg, void *data, int flags) +{ + struct ast_channel *chan = obj; + struct pickup_by_name_args *args = data; + + ast_channel_lock(chan); + if (!strncasecmp(chan->name, args->name, args->len) && can_pickup(chan)) { + /* Return with the channel still locked on purpose */ + return CMP_MATCH | CMP_STOP; + } + ast_channel_unlock(chan); + + return 0; +} + /*! \brief Helper Function to walk through ALL channels checking NAME and STATE */ static struct ast_channel *my_ast_get_channel_by_name_locked(const char *channame) { - struct ast_channel *chan; char *chkchan; - size_t channame_len, chkchan_len; + struct pickup_by_name_args pickup_args; - channame_len = strlen(channame); - chkchan_len = channame_len + 2; + pickup_args.len = strlen(channame) + 2; - chkchan = alloca(chkchan_len); + chkchan = alloca(pickup_args.len); /* need to append a '-' for the comparison so we check full channel name, * i.e SIP/hgc- , use a temporary variable so original stays the same for @@ -140,15 +173,9 @@ strcpy(chkchan, channame); strcat(chkchan, "-"); - for (chan = ast_walk_channel_by_name_prefix_locked(NULL, channame, channame_len); - chan; - chan = ast_walk_channel_by_name_prefix_locked(chan, channame, channame_len)) { - if (!strncasecmp(chan->name, chkchan, chkchan_len) && can_pickup(chan)) { - return chan; - } - ast_channel_unlock(chan); - } - return NULL; + pickup_args.name = chkchan; + + return ast_channel_callback(pickup_by_name_cb, NULL, &pickup_args, 0); } /*! \brief Attempt to pick up specified channel named , does not use context */ @@ -157,80 +184,86 @@ int res = 0; struct ast_channel *target; - if (!(target = my_ast_get_channel_by_name_locked(pickup))) + if (!(target = my_ast_get_channel_by_name_locked(pickup))) { return -1; + } /* Just check that we are not picking up the SAME as target */ - if (chan->name != target->name && chan != target) { + if (chan != target) { res = pickup_do(chan, target); } + ast_channel_unlock(target); + target = ast_channel_unref(target); return res; } -struct pickup_criteria { - const char *exten; - const char *context; -}; - -static int find_by_exten(struct ast_channel *c, void *data) -{ - struct pickup_criteria *info = data; - - return (!strcasecmp(c->macroexten, info->exten) || !strcasecmp(c->exten, info->exten)) && - !strcasecmp(c->dialcontext, info->context) && - can_pickup(c); -} - /* Attempt to pick up specified extension with context */ static int pickup_by_exten(struct ast_channel *chan, const char *exten, const char *context) { struct ast_channel *target = NULL; - struct pickup_criteria search = { - .exten = exten, - .context = context, - }; + struct ast_channel_iterator *iter; + int res = -1; - target = ast_channel_search_locked(find_by_exten, &search); + if (!(iter = ast_channel_iterator_by_exten_new(0, exten, context))) { + return -1; + } + while ((target = ast_channel_iterator_next(iter))) { + ast_channel_lock(target); + if (can_pickup(target)) { + break; + } + ast_channel_unlock(target); + target = ast_channel_unref(target); + } + if (target) { - int res = pickup_do(chan, target); + res = pickup_do(chan, target); ast_channel_unlock(target); - target = NULL; - return res; + target = ast_channel_unref(target); } - return -1; + return res; } -static int find_by_mark(struct ast_channel *c, void *data) +static int find_by_mark(void *obj, void *arg, void *data, int flags) { + struct ast_channel *c = obj; const char *mark = data; const char *tmp; + int res; - return (tmp = pbx_builtin_getvar_helper(c, PICKUPMARK)) && + ast_channel_lock(c); + + res = (tmp = pbx_builtin_getvar_helper(c, PICKUPMARK)) && !strcasecmp(tmp, mark) && can_pickup(c); + + ast_channel_unlock(c); + + return res ? CMP_MATCH | CMP_STOP : 0; } /* Attempt to pick up specified mark */ static int pickup_by_mark(struct ast_channel *chan, const char *mark) { - struct ast_channel *target = ast_channel_search_locked(find_by_mark, (char *) mark); + struct ast_channel *target; + int res = -1; - if (target) { - int res = pickup_do(chan, target); + if ((target = ast_channel_callback(find_by_mark, NULL, (char *) mark, 0))) { + ast_channel_lock(target); + res = pickup_do(chan, target); ast_channel_unlock(target); - target = NULL; - return res; + target = ast_channel_unref(target); } - return -1; + return res; } /* application entry point for Pickup() */ -static int pickup_exec(struct ast_channel *chan, void *data) +static int pickup_exec(struct ast_channel *chan, const char *data) { int res = 0; char *tmp = ast_strdupa(data); @@ -259,7 +292,7 @@ } /* application entry point for PickupChan() */ -static int pickupchan_exec(struct ast_channel *chan, void *data) +static int pickupchan_exec(struct ast_channel *chan, const char *data) { int res = 0; char *tmp = ast_strdupa(data); Index: apps/app_minivm.c =================================================================== --- a/apps/app_minivm.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_minivm.c (.../trunk) (revision 202568) @@ -400,6 +400,99 @@ subscribed to the mailbox passed in the first parameter. + + + Reads or sets counters for MiniVoicemail message. + + + + If account is given and it exists, the counter is specific for the account. + If account is a domain and the domain directory exists, counters are specific for a domain. + + + The name of the counter is a string, up to 10 characters. + + + The counters never goes below zero. Valid operands for changing the value of a counter when assigning a value are: + + Increment by value. + Decrement by value. + Set to value. + + + + + The operation is atomic and the counter is locked while changing the value. The counters are stored as text files in the minivm account directories. It might be better to use realtime functions if you are using a database to operate your Asterisk. + + + MinivmRecord + MinivmGreet + MinivmNotify + MinivmDelete + MinivmAccMess + MinivmMWI + MINIVMACCOUNT + + + + + Gets MiniVoicemail account information. + + + + + Valid items are: + + + Path to account mailbox (if account exists, otherwise temporary mailbox). + + + 1 is static Minivm account exists, 0 otherwise. + + + Full name of account owner. + + + Email address used for account. + + + Email template for account (default template if none is configured). + + + Pager template for account (default template if none is configured). + + + Account code for the voicemail account. + + + Pin code for voicemail account. + + + Time zone for voicemail account. + + + Language for voicemail account. + + + Channel variable value (set in configuration for account). + + + + + + + + + MinivmRecord + MinivmGreet + MinivmNotify + MinivmDelete + MinivmAccMess + MinivmMWI + MINIVMCOUNTER + + + ***/ #ifndef TRUE @@ -455,19 +548,19 @@ -enum { +enum minivm_option_flags { OPT_SILENT = (1 << 0), OPT_BUSY_GREETING = (1 << 1), OPT_UNAVAIL_GREETING = (1 << 2), OPT_TEMP_GREETING = (1 << 3), OPT_NAME_GREETING = (1 << 4), OPT_RECORDGAIN = (1 << 5), -} minivm_option_flags; +}; -enum { +enum minivm_option_args { OPT_ARG_RECORDGAIN = 0, OPT_ARG_ARRAY_SIZE = 1, -} minivm_option_args; +}; AST_APP_OPTIONS(minivm_app_options, { AST_APP_OPTION('s', OPT_SILENT), @@ -483,11 +576,12 @@ AST_APP_OPTION('n', OPT_NAME_GREETING), }); -/*! \brief Structure for linked list of Mini-Voicemail users: \ref minivm_accounts */ +/*!\internal + * \brief Structure for linked list of Mini-Voicemail users: \ref minivm_accounts */ struct minivm_account { char username[AST_MAX_CONTEXT]; /*!< Mailbox username */ char domain[AST_MAX_CONTEXT]; /*!< Voicemail domain */ - + char pincode[10]; /*!< Secret pin code, numbers only */ char fullname[120]; /*!< Full name, for directory app */ char email[80]; /*!< E-mail address - override */ @@ -502,18 +596,20 @@ char attachfmt[80]; /*!< Format for voicemail audio file attachment */ char etemplate[80]; /*!< Pager template */ char ptemplate[80]; /*!< Voicemail format */ - unsigned int flags; /*!< MVM_ flags */ + unsigned int flags; /*!< MVM_ flags */ struct ast_variable *chanvars; /*!< Variables for e-mail template */ double volgain; /*!< Volume gain for voicemails sent via e-mail */ - AST_LIST_ENTRY(minivm_account) list; + AST_LIST_ENTRY(minivm_account) list; }; -/*! \brief The list of e-mail accounts */ +/*!\internal + * \brief The list of e-mail accounts */ static AST_LIST_HEAD_STATIC(minivm_accounts, minivm_account); -/*! \brief Linked list of e-mail templates in various languages - These are used as templates for e-mails, pager messages and jabber messages - \ref message_templates +/*!\internal + * \brief Linked list of e-mail templates in various languages + * These are used as templates for e-mails, pager messages and jabber messages + * \ref message_templates */ struct minivm_template { char name[80]; /*!< Template name */ @@ -574,7 +670,7 @@ AST_MUTEX_DEFINE_STATIC(minivmlock); /*!< Lock to protect voicemail system */ AST_MUTEX_DEFINE_STATIC(minivmloglock); /*!< Lock to protect voicemail system log file */ -FILE *minivmlogfile; /*!< The minivm log file */ +static FILE *minivmlogfile; /*!< The minivm log file */ static int global_vmminmessage; /*!< Minimum duration of messages */ static int global_vmmaxmessage; /*!< Maximum duration of message */ @@ -588,11 +684,11 @@ static struct ast_flags globalflags = {0}; /*!< Global voicemail flags */ static int global_saydurationminfo; -static char global_charset[32]; /*!< Global charset in messages */ static double global_volgain; /*!< Volume gain for voicmemail via e-mail */ -/*! \brief Default dateformat, can be overridden in configuration file */ +/*!\internal + * \brief Default dateformat, can be overridden in configuration file */ #define DEFAULT_DATEFORMAT "%A, %B %d, %Y at %r" #define DEFAULT_CHARSET "ISO-8859-1" @@ -603,7 +699,8 @@ static struct minivm_account *find_user_realtime(const char *domain, const char *username); static char *handle_minivm_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); -/*! \brief Create message template */ +/*!\internal + * \brief Create message template */ static struct minivm_template *message_template_create(const char *name) { struct minivm_template *template; @@ -622,7 +719,8 @@ return template; } -/*! \brief Release memory allocated by message template */ +/*!\internal + * \brief Release memory allocated by message template */ static void message_template_free(struct minivm_template *template) { if (template->body) @@ -631,7 +729,8 @@ ast_free (template); } -/*! \brief Build message template from configuration */ +/*!\internal + * \brief Build message template from configuration */ static int message_template_build(const char *name, struct ast_variable *var) { struct minivm_template *template; @@ -693,7 +792,8 @@ return error; } -/*! \brief Find named template */ +/*!\internal + * \brief Find named template */ static struct minivm_template *message_template_find(const char *name) { struct minivm_template *this, *res = NULL; @@ -714,18 +814,21 @@ } -/*! \brief Clear list of templates */ +/*!\internal + * \brief Clear list of templates */ static void message_destroy_list(void) { struct minivm_template *this; AST_LIST_LOCK(&message_templates); - while ((this = AST_LIST_REMOVE_HEAD(&message_templates, list))) + while ((this = AST_LIST_REMOVE_HEAD(&message_templates, list))) { message_template_free(this); - + } + AST_LIST_UNLOCK(&message_templates); } -/*! \brief read buffer from file (base64 conversion) */ +/*!\internal + * \brief read buffer from file (base64 conversion) */ static int b64_inbuf(struct b64_baseio *bio, FILE *fi) { int l; @@ -747,7 +850,8 @@ return 1; } -/*! \brief read character from file to buffer (base64 conversion) */ +/*!\internal + * \brief read character from file to buffer (base64 conversion) */ static int b64_inchar(struct b64_baseio *bio, FILE *fi) { if (bio->iocp >= bio->iolen) { @@ -758,7 +862,8 @@ return bio->iobuf[bio->iocp++]; } -/*! \brief write buffer to file (base64 conversion) */ +/*!\internal + * \brief write buffer to file (base64 conversion) */ static int b64_ochar(struct b64_baseio *bio, int c, FILE *so) { if (bio->linelength >= B64_BASELINELEN) { @@ -776,7 +881,8 @@ return 1; } -/*! \brief Encode file to base64 encoding for email attachment (base64 conversion) */ +/*!\internal + * \brief Encode file to base64 encoding for email attachment (base64 conversion) */ static int base_encode(char *filename, FILE *so) { unsigned char dtable[B64_BASEMAXINLINE]; @@ -859,7 +965,8 @@ } -/*! \brief Free user structure - if it's allocated */ +/*!\internal + * \brief Free user structure - if it's allocated */ static void free_user(struct minivm_account *vmu) { if (vmu->chanvars) @@ -869,8 +976,9 @@ -/*! \brief Prepare for voicemail template by adding channel variables - to the channel +/*!\internal + * \brief Prepare for voicemail template by adding channel variables + * to the channel */ static void prep_email_sub_vars(struct ast_channel *channel, const struct minivm_account *vmu, const char *cidnum, const char *cidname, const char *dur, const char *date, const char *counter) { @@ -899,7 +1007,8 @@ pbx_builtin_setvar_helper(channel, "MVM_COUNTER", counter); } -/*! \brief Set default values for Mini-Voicemail users */ +/*!\internal + * \brief Set default values for Mini-Voicemail users */ static void populate_defaults(struct minivm_account *vmu) { ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL); @@ -907,26 +1016,8 @@ vmu->volgain = global_volgain; } -/*! \brief Fix quote of mail headers for non-ascii characters */ -static char *mailheader_quote(const char *from, char *to, size_t len) -{ - char *ptr = to; - *ptr++ = '"'; - for (; ptr < to + len - 1; from++) { - if (*from == '"') - *ptr++ = '\\'; - else if (*from == '\0') - break; - *ptr++ = *from; - } - if (ptr < to + len - 1) - *ptr++ = '"'; - *ptr = '\0'; - return to; -} - - -/*! \brief Allocate new vm user and set default values */ +/*!\internal + * \brief Allocate new vm user and set default values */ static struct minivm_account *mvm_user_alloc(void) { struct minivm_account *new; @@ -940,7 +1031,8 @@ } -/*! \brief Clear list of users */ +/*!\internal + * \brief Clear list of users */ static void vmaccounts_destroy_list(void) { struct minivm_account *this; @@ -951,7 +1043,8 @@ } -/*! \brief Find user from static memory object list */ +/*!\internal + * \brief Find user from static memory object list */ static struct minivm_account *find_account(const char *domain, const char *username, int createtemp) { struct minivm_account *vmu = NULL, *cur; @@ -992,8 +1085,9 @@ return vmu; } -/*! \brief Find user in realtime storage - Returns pointer to minivm_account structure +/*!\internal + * \brief Find user in realtime storage + * \return pointer to minivm_account structure */ static struct minivm_account *find_user_realtime(const char *domain, const char *username) { @@ -1023,7 +1117,102 @@ return retval; } -/*! \brief Send voicemail with audio file as an attachment */ +/*!\internal + * \brief Check if the string would need encoding within the MIME standard, to + * avoid confusing certain mail software that expects messages to be 7-bit + * clean. + */ +static int check_mime(const char *str) +{ + for (; *str; str++) { + if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) { + return 1; + } + } + return 0; +} + +/*!\internal + * \brief Encode a string according to the MIME rules for encoding strings + * that are not 7-bit clean or contain control characters. + * + * Additionally, if the encoded string would exceed the MIME limit of 76 + * characters per line, then the encoding will be broken up into multiple + * sections, separated by a space character, in order to facilitate + * breaking up the associated header across multiple lines. + * + * \param end An expandable buffer for holding the result + * \param maxlen \see ast_str + * \param charset Character set in which the result should be encoded + * \param start A string to be encoded + * \param preamble The length of the first line already used for this string, + * to ensure that each line maintains a maximum length of 76 chars. + * \param postamble the length of any additional characters appended to the + * line, used to ensure proper field wrapping. + * \return The encoded string. + */ +static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *charset, const char *start, size_t preamble, size_t postamble) +{ + struct ast_str *tmp = ast_str_alloca(80); + int first_section = 1; + *end = '\0'; + + ast_str_reset(*end); + ast_str_set(&tmp, -1, "=?%s?Q?", charset); + for (; *start; start++) { + int need_encoding = 0; + if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) { + need_encoding = 1; + } + if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) || + (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) || + (!first_section && need_encoding && ast_str_strlen(tmp) > 70) || + (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) { + /* Start new line */ + ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp)); + ast_str_set(&tmp, -1, "=?%s?Q?", charset); + first_section = 0; + } + if (need_encoding && *start == ' ') { + ast_str_append(&tmp, -1, "_"); + } else if (need_encoding) { + ast_str_append(&tmp, -1, "=%hhX", *start); + } else { + ast_str_append(&tmp, -1, "%c", *start); + } + } + ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : ""); + return ast_str_buffer(*end); +} + +/*!\internal + * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string. + * \param from The string to work with. + * \param buf The destination buffer to write the modified quoted string. + * \param maxlen Always zero. \see ast_str + * + * \return The destination string with quotes wrapped on it (the to field). + */ +static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from) +{ + const char *ptr; + + /* We're only ever passing 0 to maxlen, so short output isn't possible */ + ast_str_set(buf, maxlen, "\""); + for (ptr = from; *ptr; ptr++) { + if (*ptr == '"' || *ptr == '\\') { + ast_str_append(buf, maxlen, "\\%c", *ptr); + } else { + ast_str_append(buf, maxlen, "%c", *ptr); + } + } + ast_str_append(buf, maxlen, "\""); + + return ast_str_buffer(*buf); +} + +/*!\internal + * \brief Send voicemail with audio file as an attachment */ static int sendmail(struct minivm_template *template, struct minivm_account *vmu, char *cidnum, char *cidname, const char *filename, char *format, int duration, int attach_user_voicemail, enum mvm_messagetype type, const char *counter) { FILE *p = NULL; @@ -1039,14 +1228,18 @@ struct timeval now; struct ast_tm tm; struct minivm_zone *the_zone = NULL; - int len_passdata; struct ast_channel *ast; char *finalfilename; - char *passdata = NULL; - char *passdata2 = NULL; + struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16); char *fromaddress; char *fromemail; + if (!str1 || !str2) { + ast_free(str1); + ast_free(str2); + return -1; + } + if (type == MVM_MESSAGE_EMAIL) { if (vmu && !ast_strlen_zero(vmu->email)) { ast_copy_string(email, vmu->email, sizeof(email)); @@ -1160,51 +1353,71 @@ if (ast_strlen_zero(fromaddress)) { fprintf(p, "From: Asterisk PBX <%s>\n", who); } else { - /* Allocate a buffer big enough for variable substitution */ - int vmlen = strlen(fromaddress) * 3 + 200; - ast_debug(4, "Fromaddress template: %s\n", fromaddress); - if ((passdata = alloca(vmlen))) { - pbx_substitute_variables_helper(ast, fromaddress, passdata, vmlen); - len_passdata = strlen(passdata) * 2 + 3; - passdata2 = alloca(len_passdata); - fprintf(p, "From: %s <%s>\n", mailheader_quote(passdata, passdata2, len_passdata), who); - } else { - ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n"); - fclose(p); - return -1; + ast_str_substitute_variables(&str1, 0, ast, fromaddress); + if (check_mime(ast_str_buffer(str1))) { + int first_line = 1; + char *ptr; + ast_str_encode_mime(&str2, 0, template->charset, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3); + while ((ptr = strchr(ast_str_buffer(str2), ' '))) { + *ptr = '\0'; + fprintf(p, "%s %s\n", first_line ? "From:" : "", ast_str_buffer(str2)); + first_line = 0; + /* Substring is smaller, so this will never grow */ + ast_str_set(&str2, 0, "%s", ptr + 1); + } + fprintf(p, "%s %s <%s>\n", first_line ? "From:" : "", ast_str_buffer(str2), who); + } else { + fprintf(p, "From: %s <%s>\n", ast_str_quote(&str2, 0, ast_str_buffer(str1)), who); } } - ast_debug(4, "Fromstring now: %s\n", ast_strlen_zero(passdata) ? "-default-" : passdata); fprintf(p, "Message-ID: \n", (unsigned int)ast_random(), vmu->username, (int)getpid(), who); - len_passdata = strlen(vmu->fullname) * 2 + 3; - passdata2 = alloca(len_passdata); - if (!ast_strlen_zero(vmu->email)) - fprintf(p, "To: %s <%s>\n", mailheader_quote(vmu->fullname, passdata2, len_passdata), vmu->email); - else - fprintf(p, "To: %s <%s@%s>\n", mailheader_quote(vmu->fullname, passdata2, len_passdata), vmu->username, vmu->domain); + if (ast_strlen_zero(vmu->email)) { + snprintf(email, sizeof(email), "%s@%s", vmu->username, vmu->domain); + } else { + ast_copy_string(email, vmu->email, sizeof(email)); + } + + if (check_mime(vmu->fullname)) { + int first_line = 1; + char *ptr; + ast_str_encode_mime(&str2, 0, template->charset, vmu->fullname, strlen("To: "), strlen(email) + 3); + while ((ptr = strchr(ast_str_buffer(str2), ' '))) { + *ptr = '\0'; + fprintf(p, "%s %s\n", first_line ? "To:" : "", ast_str_buffer(str2)); + first_line = 0; + /* Substring is smaller, so this will never grow */ + ast_str_set(&str2, 0, "%s", ptr + 1); + } + fprintf(p, "%s %s <%s>\n", first_line ? "To:" : "", ast_str_buffer(str2), email); + } else { + fprintf(p, "To: %s <%s>\n", ast_str_quote(&str2, 0, vmu->fullname), email); + } + if (!ast_strlen_zero(template->subject)) { - char *pass_data; - int vmlen = strlen(template->subject) * 3 + 200; - if ((pass_data = alloca(vmlen))) { - pbx_substitute_variables_helper(ast, template->subject, pass_data, vmlen); - fprintf(p, "Subject: %s\n", pass_data); + ast_str_substitute_variables(&str1, 0, ast, template->subject); + if (check_mime(ast_str_buffer(str1))) { + int first_line = 1; + char *ptr; + ast_str_encode_mime(&str2, 0, template->charset, ast_str_buffer(str1), strlen("Subject: "), 0); + while ((ptr = strchr(ast_str_buffer(str2), ' '))) { + *ptr = '\0'; + fprintf(p, "%s %s\n", first_line ? "Subject:" : "", ast_str_buffer(str2)); + first_line = 0; + /* Substring is smaller, so this will never grow */ + ast_str_set(&str2, 0, "%s", ptr + 1); + } + fprintf(p, "%s %s\n", first_line ? "Subject:" : "", ast_str_buffer(str2)); } else { - ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n"); - fclose(p); - return -1; + fprintf(p, "Subject: %s\n", ast_str_buffer(str1)); } - - ast_debug(4, "Subject now: %s\n", pass_data); - - } else { + } else { fprintf(p, "Subject: New message in mailbox %s@%s\n", vmu->username, vmu->domain); ast_debug(1, "Using default subject for this email \n"); } - if (option_debug > 2) fprintf(p, "X-Asterisk-debug: template %s user account %s@%s\n", template->name, vmu->username, vmu->domain); fprintf(p, "MIME-Version: 1.0\n"); @@ -1215,19 +1428,13 @@ fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound); fprintf(p, "--%s\n", bound); - fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", global_charset); + fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", template->charset); if (!ast_strlen_zero(template->body)) { - char *pass_data; - int vmlen = strlen(template->body)*3 + 200; - if ((pass_data = alloca(vmlen))) { - pbx_substitute_variables_helper(ast, template->body, pass_data, vmlen); - ast_debug(3, "Message now: %s\n-----\n", pass_data); - fprintf(p, "%s\n", pass_data); - } else - ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n"); + ast_str_substitute_variables(&str1, 0, ast, template->body); + ast_debug(3, "Message now: %s\n-----\n", ast_str_buffer(str1)); + fprintf(p, "%s\n", ast_str_buffer(str1)); } else { fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message \n" - "in mailbox %s from %s, on %s so you might\n" "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname, dur, vmu->username, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date); @@ -1239,7 +1446,7 @@ ast_debug(3, "Attaching file to message: %s\n", fname); if (!strcasecmp(format, "ogg")) ctype = "application/"; - + fprintf(p, "--%s\n", bound); fprintf(p, "Content-Type: %s%s; name=\"voicemailmsg.%s\"\n", ctype, format, format); fprintf(p, "Content-Transfer-Encoding: base64\n"); @@ -1254,18 +1461,23 @@ ast_safe_system(tmp2); ast_debug(1, "Sent message to %s with command '%s' - %s\n", vmu->email, global_mailcmd, template->attachment ? "(media attachment)" : ""); ast_debug(3, "Actual command used: %s\n", tmp2); - if (ast) - ast_channel_free(ast); + if (ast) { + ast = ast_channel_release(ast); + } + ast_free(str1); + ast_free(str2); return 0; } -/*! \brief Create directory based on components */ +/*!\internal + * \brief Create directory based on components */ static int make_dir(char *dest, int len, const char *domain, const char *username, const char *folder) { return snprintf(dest, len, "%s%s/%s%s%s", MVM_SPOOL_DIR, domain, username, ast_strlen_zero(folder) ? "" : "/", folder ? folder : ""); } -/*! \brief Checks if directory exists. Does not create directory, but builds string in dest +/*!\internal + * \brief Checks if directory exists. Does not create directory, but builds string in dest * \param dest String. base directory. * \param len Int. Length base directory string. * \param domain String. Ignored if is null or empty string. @@ -1283,11 +1495,12 @@ return TRUE; } -/*! \brief basically mkdir -p $dest/$domain/$username/$folder +/*!\internal + * \brief basically mkdir -p $dest/$domain/$username/$folder * \param dest String. base directory. * \param len Length of directory string * \param domain String. Ignored if is null or empty string. - * \param folder String. Ignored if is null or empty string. + * \param folder String. Ignored if is null or empty string. * \param username String. Ignored if is null or empty string. * \return -1 on failure, 0 on success. */ @@ -1304,8 +1517,9 @@ } -/*! \brief Play intro message before recording voicemail -*/ +/*!\internal + * \brief Play intro message before recording voicemail + */ static int invent_message(struct ast_channel *chan, char *domain, char *username, int busy, char *ecodes) { int res; @@ -1327,7 +1541,7 @@ char *i = username; ast_debug(2, "No personal prompts. Using default prompt set for language\n"); - + while (*i) { ast_debug(2, "Numeric? Checking %c\n", *i); if (!isdigit(*i)) { @@ -1338,16 +1552,16 @@ } if (numericusername) { - if(ast_streamfile(chan, "vm-theperson", chan->language)) + if (ast_streamfile(chan, "vm-theperson", chan->language)) return -1; if ((res = ast_waitstream(chan, ecodes))) return res; - + res = ast_say_digit_str(chan, username, ecodes, chan->language); if (res) return res; } else { - if(ast_streamfile(chan, "vm-theextensionis", chan->language)) + if (ast_streamfile(chan, "vm-theextensionis", chan->language)) return -1; if ((res = ast_waitstream(chan, ecodes))) return res; @@ -1361,7 +1575,8 @@ return res; } -/*! \brief Delete media files and attribute file */ +/*!\internal + * \brief Delete media files and attribute file */ static int vm_delete(char *file) { int res; @@ -1374,30 +1589,31 @@ } -/*! \brief Record voicemail message & let caller review or re-record it, or set options if applicable */ +/*!\internal + * \brief Record voicemail message & let caller review or re-record it, or set options if applicable */ static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int outsidecaller, struct minivm_account *vmu, int *duration, const char *unlockdir, signed char record_gain) { - int cmd = 0; - int max_attempts = 3; - int attempts = 0; - int recorded = 0; - int message_exists = 0; + int cmd = 0; + int max_attempts = 3; + int attempts = 0; + int recorded = 0; + int message_exists = 0; signed char zero_gain = 0; char *acceptdtmf = "#"; char *canceldtmf = ""; - /* Note that urgent and private are for flagging messages as such in the future */ - + /* Note that urgent and private are for flagging messages as such in the future */ + /* barf if no pointer passed to store duration in */ if (duration == NULL) { ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n"); return -1; } - cmd = '3'; /* Want to start by recording */ - + cmd = '3'; /* Want to start by recording */ + while ((cmd >= 0) && (cmd != 't')) { switch (cmd) { case '1': @@ -1405,23 +1621,23 @@ ast_stream_and_wait(chan, "vm-msgsaved", ""); cmd = 't'; break; - case '2': - /* Review */ + case '2': + /* Review */ ast_verb(3, "Reviewing the message\n"); - ast_streamfile(chan, recordfile, chan->language); - cmd = ast_waitstream(chan, AST_DIGIT_ANY); - break; - case '3': - message_exists = 0; - /* Record */ - if (recorded == 1) + ast_streamfile(chan, recordfile, chan->language); + cmd = ast_waitstream(chan, AST_DIGIT_ANY); + break; + case '3': + message_exists = 0; + /* Record */ + if (recorded == 1) ast_verb(3, "Re-recording the message\n"); - else + else ast_verb(3, "Recording the message\n"); if (recorded && outsidecaller) - cmd = ast_play_and_wait(chan, "beep"); - recorded = 1; - /* After an attempt has been made to record message, we have to take care of INTRO and beep for incoming messages, but not for greetings */ + cmd = ast_play_and_wait(chan, "beep"); + recorded = 1; + /* After an attempt has been made to record message, we have to take care of INTRO and beep for incoming messages, but not for greetings */ if (record_gain) ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0); if (ast_test_flag(vmu, MVM_OPERATOR)) @@ -1429,10 +1645,10 @@ cmd = ast_play_and_record_full(chan, playfile, recordfile, maxtime, fmt, duration, global_silencethreshold, global_maxsilence, unlockdir, acceptdtmf, canceldtmf); if (record_gain) ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0); - if (cmd == -1) /* User has hung up, no options to give */ - return cmd; - if (cmd == '0') - break; + if (cmd == -1) /* User has hung up, no options to give */ + return cmd; + if (cmd == '0') + break; else if (cmd == '*') break; else { @@ -1483,7 +1699,7 @@ if (!cmd) cmd = ast_waitfordigit(chan, 600); } - + if (!cmd && outsidecaller && ast_test_flag(vmu, MVM_OPERATOR)) { cmd = ast_play_and_wait(chan, "vm-reachoper"); if (!cmd) @@ -1499,7 +1715,7 @@ } } } - if (outsidecaller) + if (outsidecaller) ast_play_and_wait(chan, "vm-goodbye"); if (cmd == 't') cmd = 0; @@ -1520,10 +1736,11 @@ chan->cid.cid_name, chan->cid.cid_num); ast_debug(1, "Executing: %s\n", arguments); - ast_safe_system(arguments); + ast_safe_system(arguments); } -/*! \brief Send message to voicemail account owner */ +/*!\internal + * \brief Send message to voicemail account owner */ static int notify_new_message(struct ast_channel *chan, const char *templatename, struct minivm_account *vmu, const char *filename, long duration, const char *format, char *cidnum, char *cidname) { char *stringp; @@ -1536,8 +1753,9 @@ if (!ast_strlen_zero(vmu->attachfmt)) { if (strstr(format, vmu->attachfmt)) { format = vmu->attachfmt; - } else + } else { ast_log(LOG_WARNING, "Attachment format '%s' is not one of the recorded formats '%s'. Falling back to default format for '%s@%s'.\n", vmu->attachfmt, format, vmu->username, vmu->domain); + } } etemplate = message_template_find(vmu->etemplate); @@ -1594,13 +1812,15 @@ run_externnotify(chan, vmu); /* Run external notification */ - if (etemplate->locale) + if (etemplate->locale) { setlocale(LC_TIME, oldlocale); /* Rest to old locale */ + } return res; } -/*! \brief Record voicemail message, store into file prepared for sending e-mail */ +/*!\internal + * \brief Record voicemail message, store into file prepared for sending e-mail */ static int leave_voicemail(struct ast_channel *chan, char *username, struct leave_vm_options *options) { char tmptxtfile[PATH_MAX]; @@ -1662,7 +1882,6 @@ snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir); - /* XXX This file needs to be in temp directory */ txtdes = mkstemp(tmptxtfile); @@ -1699,7 +1918,7 @@ get_date(date, sizeof(date)); ast_localtime(&now, &tm, NULL); ast_strftime(timebuf, sizeof(timebuf), "%H:%M:%S", &tm); - + snprintf(logbuf, sizeof(logbuf), /* "Mailbox:domain:macrocontext:exten:priority:callerchan:callerid:origdate:origtime:duration:durationstatus:accountcode" */ "%s:%s:%s:%s:%d:%s:%s:%s:%s:%d:%s:%s\n", @@ -1750,12 +1969,14 @@ } global_stats.lastreceived = ast_tvnow(); global_stats.receivedmessages++; -// /* Go ahead and delete audio files from system, they're not needed any more */ -// if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) { -// ast_filedelete(tmptxtfile, NULL); -// /* Even not being used at the moment, it's better to convert ast_log to ast_debug anyway */ -// ast_debug(2, "-_-_- Deleted audio file after notification :: %s \n", tmptxtfile); -// } +#if 0 + /* Go ahead and delete audio files from system, they're not needed any more */ + if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) { + ast_filedelete(tmptxtfile, NULL); + /* Even not being used at the moment, it's better to convert ast_log to ast_debug anyway */ + ast_debug(2, "-_-_- Deleted audio file after notification :: %s \n", tmptxtfile); + } +#endif if (res > 0) res = 0; @@ -1767,7 +1988,8 @@ return res; } -/*! \brief Queue a message waiting event */ +/*!\internal + * \brief Queue a message waiting event */ static void queue_mwi_event(const char *mbx, const char *ctx, int urgent, int new, int old) { struct ast_event *event; @@ -1791,8 +2013,9 @@ ast_event_queue_and_cache(event); } -/*! \brief Send MWI using interal Asterisk event subsystem */ -static int minivm_mwi_exec(struct ast_channel *chan, void *data) +/*!\internal + * \brief Send MWI using interal Asterisk event subsystem */ +static int minivm_mwi_exec(struct ast_channel *chan, const char *data) { int argc; char *argv[4]; @@ -1802,38 +2025,39 @@ char *mailbox; char *domain; if (ast_strlen_zero(data)) { - ast_log(LOG_ERROR, "Minivm needs at least an account argument \n"); - return -1; - } - tmpptr = ast_strdupa((char *)data); - if (!tmpptr) { - ast_log(LOG_ERROR, "Out of memory\n"); - return -1; - } - argc = ast_app_separate_args(tmpptr, ',', argv, ARRAY_LEN(argv)); + ast_log(LOG_ERROR, "Minivm needs at least an account argument \n"); + return -1; + } + tmpptr = ast_strdupa((char *)data); + if (!tmpptr) { + ast_log(LOG_ERROR, "Out of memory\n"); + return -1; + } + argc = ast_app_separate_args(tmpptr, ',', argv, ARRAY_LEN(argv)); if (argc < 4) { ast_log(LOG_ERROR, "%d arguments passed to MiniVM_MWI, need 4.\n", argc); return -1; } - ast_copy_string(tmp, argv[0], sizeof(tmp)); - mailbox = tmp; - domain = strchr(tmp, '@'); - if (domain) { - *domain = '\0'; - domain++; - } - if (ast_strlen_zero(domain) || ast_strlen_zero(mailbox)) { - ast_log(LOG_ERROR, "Need mailbox@context as argument. Sorry. Argument 0 %s\n", argv[0]); - return -1; - } + ast_copy_string(tmp, argv[0], sizeof(tmp)); + mailbox = tmp; + domain = strchr(tmp, '@'); + if (domain) { + *domain = '\0'; + domain++; + } + if (ast_strlen_zero(domain) || ast_strlen_zero(mailbox)) { + ast_log(LOG_ERROR, "Need mailbox@context as argument. Sorry. Argument 0 %s\n", argv[0]); + return -1; + } queue_mwi_event(mailbox, domain, atoi(argv[1]), atoi(argv[2]), atoi(argv[3])); return res; } -/*! \brief Notify voicemail account owners - either generic template or user specific */ -static int minivm_notify_exec(struct ast_channel *chan, void *data) +/*!\internal + * \brief Notify voicemail account owners - either generic template or user specific */ +static int minivm_notify_exec(struct ast_channel *chan, const char *data) { int argc; char *argv[2]; @@ -1847,7 +2071,7 @@ const char *filename; const char *format; const char *duration_string; - + if (ast_strlen_zero(data)) { ast_log(LOG_ERROR, "Minivm needs at least an account argument \n"); return -1; @@ -1911,8 +2135,9 @@ } -/*! \brief Dialplan function to record voicemail */ -static int minivm_record_exec(struct ast_channel *chan, void *data) +/*!\internal + * \brief Dialplan function to record voicemail */ +static int minivm_record_exec(struct ast_channel *chan, const char *data) { int res = 0; char *tmp; @@ -1921,7 +2146,7 @@ char *argv[2]; struct ast_flags flags = { 0 }; char *opts[OPT_ARG_ARRAY_SIZE]; - + memset(&leave_options, 0, sizeof(leave_options)); /* Answer channel if it's not already answered */ @@ -1967,8 +2192,9 @@ return res; } -/*! \brief Play voicemail prompts - either generic or user specific */ -static int minivm_greet_exec(struct ast_channel *chan, void *data) +/*!\internal + * \brief Play voicemail prompts - either generic or user specific */ +static int minivm_greet_exec(struct ast_channel *chan, const char *data) { struct leave_vm_options leave_options = { 0, '\0'}; int argc; @@ -2153,12 +2379,13 @@ } -/*! \brief Dialplan application to delete voicemail */ -static int minivm_delete_exec(struct ast_channel *chan, void *data) +/*!\internal + * \brief Dialplan application to delete voicemail */ +static int minivm_delete_exec(struct ast_channel *chan, const char *data) { int res = 0; char filename[BUFSIZ]; - + if (!ast_strlen_zero(data)) { ast_copy_string(filename, (char *) data, sizeof(filename)); } else { @@ -2192,7 +2419,7 @@ } /*! \brief Record specific messages for voicemail account */ -static int minivm_accmess_exec(struct ast_channel *chan, void *data) +static int minivm_accmess_exec(struct ast_channel *chan, const char *data) { int argc = 0; char *argv[2]; @@ -2608,7 +2835,6 @@ ast_copy_string(default_vmformat, "wav", sizeof(default_vmformat)); ast_set2_flag((&globalflags), FALSE, MVM_REVIEW); ast_set2_flag((&globalflags), FALSE, MVM_OPERATOR); - strcpy(global_charset, "ISO-8859-1"); /* Reset statistics */ memset(&global_stats, 0, sizeof(global_stats)); global_stats.reset = ast_tvnow(); @@ -3228,41 +3454,13 @@ static struct ast_custom_function minivm_counter_function = { .name = "MINIVMCOUNTER", - .synopsis = "Reads or sets counters for MiniVoicemail message", - .syntax = "MINIVMCOUNTER(:name[:operand])", .read = minivm_counter_func_read, .write = minivm_counter_func_write, - .desc = "Valid operands for changing the value of a counter when assigning a value are:\n" - "- i Increment by value\n" - "- d Decrement by value\n" - "- s Set to value\n" - "\nThe counters never goes below zero.\n" - "- The name of the counter is a string, up to 10 characters\n" - "- If account is given and it exists, the counter is specific for the account\n" - "- If account is a domain and the domain directory exists, counters are specific for a domain\n" - "The operation is atomic and the counter is locked while changing the value\n" - "\nThe counters are stored as text files in the minivm account directories. It might be better to use\n" - "realtime functions if you are using a database to operate your Asterisk\n", }; static struct ast_custom_function minivm_account_function = { .name = "MINIVMACCOUNT", - .synopsis = "Gets MiniVoicemail account information", - .syntax = "MINIVMACCOUNT(:item)", .read = minivm_account_func_read, - .desc = "Valid items are:\n" - "- path Path to account mailbox (if account exists, otherwise temporary mailbox)\n" - "- hasaccount 1 if static Minivm account exists, 0 otherwise\n" - "- fullname Full name of account owner\n" - "- email Email address used for account\n" - "- etemplate E-mail template for account (default template if none is configured)\n" - "- ptemplate Pager template for account (default template if none is configured)\n" - "- accountcode Account code for voicemail account\n" - "- pincode Pin code for voicemail account\n" - "- timezone Time zone for voicemail account\n" - "- language Language for voicemail account\n" - "- Channel variable value (set in configuration for account)\n" - "\n", }; /*! \brief Load mini voicemail module */ Index: apps/app_dumpchan.c =================================================================== --- a/apps/app_dumpchan.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_dumpchan.c (.../trunk) (revision 202568) @@ -60,7 +60,7 @@ ***/ -static char *app = "DumpChan"; +static const char app[] = "DumpChan"; static int serialize_showchan(struct ast_channel *c, char *buf, size_t size) { @@ -147,7 +147,7 @@ return 0; } -static int dumpchan_exec(struct ast_channel *chan, void *data) +static int dumpchan_exec(struct ast_channel *chan, const char *data) { struct ast_str *vars = ast_str_thread_get(&ast_str_thread_global_buf, 16); char info[1024]; Index: apps/app_macro.c =================================================================== --- a/apps/app_macro.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_macro.c (.../trunk) (revision 202568) @@ -36,6 +36,7 @@ #include "asterisk/config.h" #include "asterisk/utils.h" #include "asterisk/lock.h" +#include "asterisk/app.h" /*** DOCUMENTATION @@ -158,7 +159,7 @@ static void macro_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan); -struct ast_datastore_info macro_ds_info = { +static struct ast_datastore_info macro_ds_info = { .type = "MACRO", .chan_fixup = macro_fixup, }; @@ -216,7 +217,7 @@ return NULL; } -static int _macro_exec(struct ast_channel *chan, void *data, int exclusive) +static int _macro_exec(struct ast_channel *chan, const char *data, int exclusive) { const char *s; char *tmp; @@ -236,13 +237,14 @@ int offset, depth = 0, maxdepth = 7; int setmacrocontext=0; int autoloopflag, inhangup = 0; + struct ast_str *tmp_subst = NULL; char *save_macro_exten; char *save_macro_context; char *save_macro_priority; char *save_macro_offset; struct ast_datastore *macro_store = ast_channel_datastore_find(chan, ¯o_ds_info, NULL); - + if (ast_strlen_zero(data)) { ast_log(LOG_WARNING, "Macro() requires arguments. See \"core show application macro\" for help.\n"); return -1; @@ -286,7 +288,6 @@ return 0; } snprintf(depthc, sizeof(depthc), "%d", depth + 1); - pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc); tmp = ast_strdupa(data); rest = tmp; @@ -316,7 +317,11 @@ } ast_autoservice_stop(chan); } - + + if (!(tmp_subst = ast_str_create(16))) { + return -1; + } + /* Save old info */ oldpriority = chan->priority; ast_copy_string(oldexten, chan->exten, sizeof(oldexten)); @@ -342,6 +347,8 @@ save_macro_offset = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_OFFSET")); pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL); + pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc); + /* Setup environment for new run */ chan->exten[0] = 's'; chan->exten[1] = '\0'; @@ -420,8 +427,10 @@ gosub_level++; ast_debug(1, "Incrementing gosub_level\n"); } else if (!strcasecmp(runningapp, "GOSUBIF")) { - char tmp2[1024], *cond, *app_arg, *app2 = tmp2; - pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1); + char *cond, *app_arg; + char *app2; + ast_str_substitute_variables(&tmp_subst, 0, chan, runningdata); + app2 = ast_str_buffer(tmp_subst); cond = strsep(&app2, "?"); app_arg = strsep(&app2, ":"); if (pbx_checkcondition(cond)) { @@ -443,19 +452,23 @@ ast_debug(1, "Decrementing gosub_level\n"); } else if (!strncasecmp(runningapp, "EXEC", 4)) { /* Must evaluate args to find actual app */ - char tmp2[1024], *tmp3 = NULL; - pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1); + char *tmp2, *tmp3 = NULL; + ast_str_substitute_variables(&tmp_subst, 0, chan, runningdata); + tmp2 = ast_str_buffer(tmp_subst); if (!strcasecmp(runningapp, "EXECIF")) { - tmp3 = strchr(tmp2, '|'); - if (tmp3) + if ((tmp3 = strchr(tmp2, '|'))) { *tmp3++ = '\0'; - if (!pbx_checkcondition(tmp2)) + } + if (!pbx_checkcondition(tmp2)) { tmp3 = NULL; - } else + } + } else { tmp3 = tmp2; + } - if (tmp3) + if (tmp3) { ast_debug(1, "Last app: %s\n", tmp3); + } if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) { gosub_level++; @@ -552,21 +565,22 @@ } } ast_channel_unlock(chan); + ast_free(tmp_subst); return res; } -static int macro_exec(struct ast_channel *chan, void *data) +static int macro_exec(struct ast_channel *chan, const char *data) { return _macro_exec(chan, data, 0); } -static int macroexclusive_exec(struct ast_channel *chan, void *data) +static int macroexclusive_exec(struct ast_channel *chan, const char *data) { return _macro_exec(chan, data, 1); } -static int macroif_exec(struct ast_channel *chan, void *data) +static int macroif_exec(struct ast_channel *chan, const char *data) { char *expr = NULL, *label_a = NULL, *label_b = NULL; int res = 0; @@ -591,7 +605,7 @@ return res; } -static int macro_exit_exec(struct ast_channel *chan, void *data) +static int macro_exit_exec(struct ast_channel *chan, const char *data) { return MACRO_EXIT_RESULT; } Index: apps/app_sms.c =================================================================== --- a/apps/app_sms.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_sms.c (.../trunk) (revision 202568) @@ -123,7 +123,7 @@ * To pick the two carriers (1300Hz for '1' and 2100 Hz for '0') used by * the modulation, we should take one every 13 and 21 samples respectively. */ -static signed short wave[] = { +static const signed short wave[] = { 0, 392, 782, 1167, 1545, 1913, 2270, 2612, 2939, 3247, 3536, 3802, 4045, 4263, 4455, 4619, 4755, 4862, 4938, 4985, 5000, 4985, 4938, 4862, 4755, 4619, 4455, 4263, 4045, 3802, 3536, 3247, 2939, 2612, 2270, 1913, 1545, 1167, 782, 392, 0, -392, -782, -1167, @@ -815,7 +815,7 @@ char line[1000]; FILE *s; char dcsset = 0; /* if DSC set */ - ast_log(LOG_EVENT, "Sending %s\n", fn); + ast_log(LOG_NOTICE, "Sending %s\n", fn); h->rx = h->udl = *h->oa = *h->da = h->pid = h->srr = h->udhi = h->rp = h->vp = h->udhl = 0; h->mr = -1; h->dcs = 0xF1; /* normal messages class 1 */ @@ -1080,7 +1080,7 @@ if (rename(fn, fn2)) { unlink(fn); } else { - ast_log(LOG_EVENT, "Received to %s\n", fn2); + ast_log(LOG_NOTICE, "Received to %s\n", fn2); } } @@ -1832,19 +1832,19 @@ * - AST_APP_OPTIONS() to drive the parsing routine * - in the function, AST_DECLARE_APP_ARGS(...) for the arguments. */ -enum { +enum sms_flags { OPTION_BE_SMSC = (1 << 0), /* act as sms center */ OPTION_ANSWER = (1 << 1), /* answer on incoming calls */ OPTION_TWO = (1 << 2), /* Use Protocol Two */ OPTION_PAUSE = (1 << 3), /* pause before sending data, in ms */ OPTION_SRR = (1 << 4), /* set srr */ OPTION_DCS = (1 << 5), /* set dcs */ -} sms_flags; +}; -enum { +enum sms_opt_args { OPTION_ARG_PAUSE = 0, OPTION_ARG_ARRAY_SIZE -} sms_opt_args; +}; AST_APP_OPTIONS(sms_options, { AST_APP_OPTION('s', OPTION_BE_SMSC), @@ -1855,7 +1855,7 @@ AST_APP_OPTION_ARG('p', OPTION_PAUSE, OPTION_ARG_PAUSE), } ); -static int sms_exec(struct ast_channel *chan, void *data) +static int sms_exec(struct ast_channel *chan, const char *data) { int res = -1; sms_t h = { 0 }; Index: apps/app_confbridge.c =================================================================== --- a/apps/app_confbridge.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_confbridge.c (.../trunk) (revision 202568) @@ -111,7 +111,7 @@ * bridge lock if it is important. */ -static const char *app = "ConfBridge"; +static const char app[] = "ConfBridge"; enum { OPTION_ADMIN = (1 << 0), /*!< Set if the caller is an administrator */ @@ -683,7 +683,7 @@ } /*! \brief The ConfBridge application */ -static int confbridge_exec(struct ast_channel *chan, void *data) +static int confbridge_exec(struct ast_channel *chan, const char *data) { int res = 0, volume_adjustments[2]; char *parse; Index: apps/app_verbose.c =================================================================== --- a/apps/app_verbose.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_verbose.c (.../trunk) (revision 202568) @@ -72,7 +72,7 @@ ***/ -static int verbose_exec(struct ast_channel *chan, void *data) +static int verbose_exec(struct ast_channel *chan, const char *data) { int vsize; char *parse; @@ -118,7 +118,7 @@ return 0; } -static int log_exec(struct ast_channel *chan, void *data) +static int log_exec(struct ast_channel *chan, const char *data) { char *parse; int lnum = -1; @@ -146,8 +146,6 @@ lnum = __LOG_VERBOSE; } else if (!strcasecmp(args.level, "DTMF")) { lnum = __LOG_DTMF; - } else if (!strcasecmp(args.level, "EVENT")) { - lnum = __LOG_EVENT; } else { ast_log(LOG_ERROR, "Unknown log level: '%s'\n", args.level); } Index: apps/app_voicemail.c =================================================================== --- a/apps/app_voicemail.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_voicemail.c (.../trunk) (revision 202568) @@ -46,15 +46,22 @@ /*** MAKEOPTS + + ODBC_STORAGE + IMAP_STORAGE + yes + generic_odbc ltdl IMAP_STORAGE + FILE_STORAGE no imap_tk ODBC_STORAGE + FILE_STORAGE openssl no @@ -298,6 +305,16 @@ context. + + + List All Voicemail User Information. + + + + + + + ***/ #ifdef IMAP_STORAGE @@ -415,16 +432,16 @@ #define ERROR_LOCK_PATH -100 -enum { +enum vm_box { NEW_FOLDER, OLD_FOLDER, WORK_FOLDER, FAMILY_FOLDER, FRIENDS_FOLDER, GREETINGS_FOLDER -} vm_box; +}; -enum { +enum vm_option_flags { OPT_SILENT = (1 << 0), OPT_BUSY_GREETING = (1 << 1), OPT_UNAVAIL_GREETING = (1 << 2), @@ -434,15 +451,15 @@ OPT_DTMFEXIT = (1 << 7), OPT_MESSAGE_Urgent = (1 << 8), OPT_MESSAGE_PRIORITY = (1 << 9) -} vm_option_flags; +}; -enum { +enum vm_option_args { OPT_ARG_RECORDGAIN = 0, OPT_ARG_PLAYFOLDER = 1, OPT_ARG_DTMFEXIT = 2, /* This *must* be the last value in this enum! */ OPT_ARG_ARRAY_SIZE = 3, -} vm_option_args; +}; AST_APP_OPTIONS(vm_app_options, { AST_APP_OPTION('s', OPT_SILENT), @@ -1417,7 +1434,7 @@ static const char *mbox(int id) { - static const char *msgs[] = { + static const char * const msgs[] = { #ifdef IMAP_STORAGE imapfolder, #else @@ -3332,7 +3349,8 @@ char *c; struct ast_config *cfg=NULL; struct odbc_obj *obj; - struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext }; + struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext, + .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" }; struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE }; delete_file(dir, msgnum); @@ -3846,9 +3864,10 @@ return 1; } -static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize, const char *category, const char *flag) +static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *dur, char *date, const char *category, const char *flag) { char callerid[256]; + char num[12]; char fromdir[256], fromfile[256]; struct ast_config *msg_cfg; const char *origcallerid, *origtime; @@ -3859,8 +3878,8 @@ /* Prepare variables for substitution in email body and subject */ pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname); pbx_builtin_setvar_helper(ast, "VM_DUR", dur); - snprintf(passdata, passdatasize, "%d", msgnum); - pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata); + snprintf(num, sizeof(num), "%d", msgnum); + pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num); pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context); pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox); pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ? @@ -3904,30 +3923,32 @@ /*! * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string. * \param from The string to work with. - * \param to The string to write the modified quoted string. This buffer should be sufficiently larger than the from string, so as to allow it to be expanded by the surrounding quotes and escaping of internal quotes. + * \param buf The buffer into which to write the modified quoted string. + * \param maxlen Always zero, but see \see ast_str * * \return The destination string with quotes wrapped on it (the to field). */ -static char *quote(const char *from, char *to, size_t len) +static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from) { - char *ptr = to; - *ptr++ = '"'; - for (; ptr < to + len - 1; from++) { - if (*from == '"') - *ptr++ = '\\'; - else if (*from == '\0') - break; - *ptr++ = *from; + const char *ptr; + + /* We're only ever passing 0 to maxlen, so short output isn't possible */ + ast_str_set(buf, maxlen, "\""); + for (ptr = from; *ptr; ptr++) { + if (*ptr == '"' || *ptr == '\\') { + ast_str_append(buf, maxlen, "\\%c", *ptr); + } else { + ast_str_append(buf, maxlen, "%c", *ptr); + } } - if (ptr < to + len - 1) - *ptr++ = '"'; - *ptr = '\0'; - return to; + ast_str_append(buf, maxlen, "\""); + + return ast_str_buffer(*buf); } /*! \brief * fill in *tm for current time according to the proper timezone, if any. - * Return tm so it can be used as a function argument. + * \return tm so it can be used as a function argument. */ static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm) { @@ -3970,46 +3991,46 @@ * sections, separated by a space character, in order to facilitate * breaking up the associated header across multiple lines. * + * \param end An expandable buffer for holding the result + * \param maxlen Always zero, but see \see ast_str * \param start A string to be encoded - * \param end An expandable buffer for holding the result * \param preamble The length of the first line already used for this string, * to ensure that each line maintains a maximum length of 76 chars. * \param postamble the length of any additional characters appended to the * line, used to ensure proper field wrapping. * \retval The encoded string. */ -static char *encode_mime_str(const char *start, char *end, size_t endsize, size_t preamble, size_t postamble) +static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble) { - char tmp[80]; + struct ast_str *tmp = ast_str_alloca(80); int first_section = 1; - size_t endlen = 0, tmplen = 0; - *end = '\0'; - tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset); + ast_str_reset(*end); + ast_str_set(&tmp, -1, "=?%s?Q?", charset); for (; *start; start++) { int need_encoding = 0; if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) { need_encoding = 1; } - if ((first_section && need_encoding && preamble + tmplen > 70) || - (first_section && !need_encoding && preamble + tmplen > 72) || - (!first_section && need_encoding && tmplen > 70) || - (!first_section && !need_encoding && tmplen > 72)) { + if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) || + (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) || + (!first_section && need_encoding && ast_str_strlen(tmp) > 70) || + (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) { /* Start new line */ - endlen += snprintf(end + endlen, endsize - endlen, "%s%s?=", first_section ? "" : " ", tmp); - tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset); + ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp)); + ast_str_set(&tmp, -1, "=?%s?Q?", charset); first_section = 0; } if (need_encoding && *start == ' ') { - tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "_"); + ast_str_append(&tmp, -1, "_"); } else if (need_encoding) { - tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "=%hhX", *start); + ast_str_append(&tmp, -1, "=%hhX", *start); } else { - tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "%c", *start); + ast_str_append(&tmp, -1, "%c", *start); } } - snprintf(end + endlen, endsize - endlen, "%s%s?=%s", first_section ? "" : " ", tmp, endlen + postamble > 74 ? " " : ""); - return end; + ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : ""); + return ast_str_buffer(*end); } /*! @@ -4041,28 +4062,21 @@ char dur[256]; struct ast_tm tm; char enc_cidnum[256] = "", enc_cidname[256] = ""; - char *passdata = NULL, *passdata2; - size_t len_passdata = 0, len_passdata2, tmplen; + struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16); char *greeting_attachment; char filename[256]; + if (!str1 || !str2) { + ast_free(str1); + ast_free(str2); + return; + } #ifdef IMAP_STORAGE #define ENDL "\r\n" #else #define ENDL "\n" #endif - /* One alloca for multiple fields */ - len_passdata2 = strlen(vmu->fullname); - if (emailsubject && (tmplen = strlen(emailsubject)) > len_passdata2) { - len_passdata2 = tmplen; - } - if ((tmplen = strlen(fromstring)) > len_passdata2) { - len_passdata2 = tmplen; - } - len_passdata2 = len_passdata2 * 3 + 200; - passdata2 = alloca(len_passdata2); - if (cidnum) { strip_control(cidnum, enc_cidnum, sizeof(enc_cidnum)); } @@ -4071,14 +4085,16 @@ } gethostname(host, sizeof(host) - 1); - if (strchr(srcemail, '@')) + if (strchr(srcemail, '@')) { ast_copy_string(who, srcemail, sizeof(who)); - else + } else { snprintf(who, sizeof(who), "%s@%s", srcemail, host); + } greeting_attachment = strrchr(ast_strdupa(attach), '/'); - if (greeting_attachment) + if (greeting_attachment) { *greeting_attachment++ = '\0'; + } snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60); ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm)); @@ -4091,25 +4107,24 @@ struct ast_channel *ast; if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) { char *ptr; - memset(passdata2, 0, len_passdata2); - prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, passdata2, len_passdata2, category, flag); - pbx_substitute_variables_helper(ast, fromstring, passdata2, len_passdata2); - len_passdata = strlen(passdata2) * 3 + 300; - passdata = alloca(len_passdata); - if (check_mime(passdata2)) { + prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag); + ast_str_substitute_variables(&str1, 0, ast, fromstring); + + if (check_mime(ast_str_buffer(str1))) { int first_line = 1; - encode_mime_str(passdata2, passdata, len_passdata, strlen("From: "), strlen(who) + 3); - while ((ptr = strchr(passdata, ' '))) { + ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3); + while ((ptr = strchr(ast_str_buffer(str2), ' '))) { *ptr = '\0'; - fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", passdata); + fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2)); first_line = 0; - passdata = ptr + 1; + /* Substring is smaller, so this will never grow */ + ast_str_set(&str2, 0, "%s", ptr + 1); } - fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", passdata, who); + fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who); } else { - fprintf(p, "From: %s <%s>" ENDL, quote(passdata2, passdata, len_passdata), who); + fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who); } - ast_channel_free(ast); + ast = ast_channel_release(ast); } else { ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n"); } @@ -4120,46 +4135,41 @@ if (check_mime(vmu->fullname)) { int first_line = 1; char *ptr; - encode_mime_str(vmu->fullname, passdata2, len_passdata2, strlen("To: "), strlen(vmu->email) + 3); - while ((ptr = strchr(passdata2, ' '))) { + ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3); + while ((ptr = strchr(ast_str_buffer(str2), ' '))) { *ptr = '\0'; - fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", passdata2); + fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2)); first_line = 0; - passdata2 = ptr + 1; + /* Substring is smaller, so this will never grow */ + ast_str_set(&str2, 0, "%s", ptr + 1); } - fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", passdata2, vmu->email); + fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email); } else { - fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata2), vmu->email); + fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email); } + if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) { char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject; struct ast_channel *ast; if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) { - int vmlen = strlen(e_subj) * 3 + 200; - /* Only allocate more space if the previous was not large enough */ - if (vmlen > len_passdata) { - passdata = alloca(vmlen); - len_passdata = vmlen; - } - - memset(passdata, 0, len_passdata); - prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, len_passdata, category, flag); - pbx_substitute_variables_helper(ast, e_subj, passdata, len_passdata); - if (check_mime(passdata)) { + prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag); + ast_str_substitute_variables(&str1, 0, ast, e_subj); + if (check_mime(ast_str_buffer(str1))) { int first_line = 1; char *ptr; - encode_mime_str(passdata, passdata2, len_passdata2, strlen("Subject: "), 0); - while ((ptr = strchr(passdata2, ' '))) { + ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0); + while ((ptr = strchr(ast_str_buffer(str2), ' '))) { *ptr = '\0'; - fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2); + fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2)); first_line = 0; - passdata2 = ptr + 1; + /* Substring is smaller, so this will never grow */ + ast_str_set(&str2, 0, "%s", ptr + 1); } - fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2); + fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2)); } else { - fprintf(p, "Subject: %s" ENDL, passdata); + fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1)); } - ast_channel_free(ast); + ast = ast_channel_release(ast); } else { ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n"); } @@ -4225,16 +4235,13 @@ char* e_body = vmu->emailbody ? vmu->emailbody : emailbody; struct ast_channel *ast; if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) { - char *passdata; - int vmlen = strlen(e_body) * 3 + 200; - passdata = alloca(vmlen); - memset(passdata, 0, vmlen); - prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag); - pbx_substitute_variables_helper(ast, e_body, passdata, vmlen); - fprintf(p, "%s" ENDL, passdata); - ast_channel_free(ast); - } else + prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag); + ast_str_substitute_variables(&str1, 0, ast, e_body); + fprintf(p, "%s" ENDL, ast_str_buffer(str1)); + ast = ast_channel_release(ast); + } else { ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n"); + } } else if (msgnum > -1) { if (strcmp(vmu->mailbox, mailbox)) { /* Forwarded type */ @@ -4299,6 +4306,8 @@ add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum); } } + ast_free(str1); + ast_free(str2); } static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum) @@ -4352,7 +4361,6 @@ } return 0; } -#undef ENDL static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, const char *flag) { @@ -4384,6 +4392,7 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category, const char *flag) { + char enc_cidnum[256], enc_cidname[256]; char date[256]; char host[MAXHOSTNAMELEN] = ""; char who[256]; @@ -4392,49 +4401,106 @@ char tmp2[PATH_MAX]; struct ast_tm tm; FILE *p; + struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16); + if (!str1 || !str2) { + ast_free(str1); + ast_free(str2); + return -1; + } + + if (cidnum) { + strip_control(cidnum, enc_cidnum, sizeof(enc_cidnum)); + } + if (cidname) { + strip_control(cidname, enc_cidname, sizeof(enc_cidname)); + } + if ((p = vm_mkftemp(tmp)) == NULL) { ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd); + ast_free(str1); + ast_free(str2); return -1; } gethostname(host, sizeof(host)-1); - if (strchr(srcemail, '@')) + if (strchr(srcemail, '@')) { ast_copy_string(who, srcemail, sizeof(who)); - else + } else { snprintf(who, sizeof(who), "%s@%s", srcemail, host); + } snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60); ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm)); fprintf(p, "Date: %s\n", date); - if (*pagerfromstring) { + if (!ast_strlen_zero(pagerfromstring)) { struct ast_channel *ast; if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) { - char *passdata; - int vmlen = strlen(fromstring)*3 + 200; - passdata = alloca(vmlen); - memset(passdata, 0, vmlen); - prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag); - pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen); - fprintf(p, "From: %s <%s>\n", passdata, who); - ast_channel_free(ast); - } else + char *ptr; + prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag); + ast_str_substitute_variables(&str1, 0, ast, pagerfromstring); + + if (check_mime(ast_str_buffer(str1))) { + int first_line = 1; + ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3); + while ((ptr = strchr(ast_str_buffer(str2), ' '))) { + *ptr = '\0'; + fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2)); + first_line = 0; + /* Substring is smaller, so this will never grow */ + ast_str_set(&str2, 0, "%s", ptr + 1); + } + fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who); + } else { + fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who); + } + ast = ast_channel_release(ast); + } else { ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n"); - } else - fprintf(p, "From: Asterisk PBX <%s>\n", who); - fprintf(p, "To: %s\n", pager); - if (pagersubject) { + } + } else { + fprintf(p, "From: Asterisk PBX <%s>" ENDL, who); + } + + if (check_mime(vmu->fullname)) { + int first_line = 1; + char *ptr; + ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3); + while ((ptr = strchr(ast_str_buffer(str2), ' '))) { + *ptr = '\0'; + fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2)); + first_line = 0; + /* Substring is smaller, so this will never grow */ + ast_str_set(&str2, 0, "%s", ptr + 1); + } + fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager); + } else { + fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager); + } + + if (!ast_strlen_zero(pagersubject)) { struct ast_channel *ast; if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) { - char *passdata; - int vmlen = strlen(pagersubject) * 3 + 200; - passdata = alloca(vmlen); - memset(passdata, 0, vmlen); - prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag); - pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen); - fprintf(p, "Subject: %s\n\n", passdata); - ast_channel_free(ast); - } else + prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag); + ast_str_substitute_variables(&str1, 0, ast, pagersubject); + if (check_mime(ast_str_buffer(str1))) { + int first_line = 1; + char *ptr; + ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0); + while ((ptr = strchr(ast_str_buffer(str2), ' '))) { + *ptr = '\0'; + fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2)); + first_line = 0; + /* Substring is smaller, so this will never grow */ + ast_str_set(&str2, 0, "%s", ptr + 1); + } + fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2)); + } else { + fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1)); + } + ast = ast_channel_release(ast); + } else { ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n"); + } } else { if (ast_strlen_zero(flag)) { fprintf(p, "Subject: New VM\n\n"); @@ -4447,26 +4513,27 @@ if (pagerbody) { struct ast_channel *ast; if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) { - char *passdata; - int vmlen = strlen(pagerbody) * 3 + 200; - passdata = alloca(vmlen); - memset(passdata, 0, vmlen); - prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag); - pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen); - fprintf(p, "%s\n", passdata); - ast_channel_free(ast); - } else + prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag); + ast_str_substitute_variables(&str1, 0, ast, pagerbody); + fprintf(p, "%s" ENDL, ast_str_buffer(str1)); + ast = ast_channel_release(ast); + } else { ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n"); + } } else { fprintf(p, "New %s long %s msg in box %s\n" "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date); } + fclose(p); snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp); ast_safe_system(tmp2); ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd); + ast_free(str1); + ast_free(str2); return 0; } +#undef ENDL /*! * \brief Gets the current date and time, as formatted string. @@ -5037,8 +5104,11 @@ const char *alldtmf = "0123456789ABCD*#"; char flag[80]; - ast_str_set(&tmp, 0, "%s", ext); - ext = ast_str_buffer(tmp); + if (!tmp) { + return -1; + } + + ext = ast_strdupa(ext); if ((context = strchr(ext, '@'))) { *context++ = '\0'; tmpptr = strchr(context, '&'); @@ -8894,7 +8964,7 @@ return 0; } -static int vm_execmain(struct ast_channel *chan, void *data) +static int vm_execmain(struct ast_channel *chan, const char *data) { /* XXX This is, admittedly, some pretty horrendous code. For some reason it just seemed a lot easier to do with GOTO's. I feel @@ -9602,7 +9672,7 @@ return res; } -static int vm_exec(struct ast_channel *chan, void *data) +static int vm_exec(struct ast_channel *chan, const char *data) { int res = 0; char *tmp; @@ -9734,7 +9804,7 @@ return 0; } -static int vm_box_exists(struct ast_channel *chan, void *data) +static int vm_box_exists(struct ast_channel *chan, const char *data) { struct ast_vm_user svm; char *context, *box; @@ -9798,16 +9868,16 @@ .read = acf_mailbox_exists, }; -static int vmauthenticate(struct ast_channel *chan, void *data) +static int vmauthenticate(struct ast_channel *chan, const char *data) { - char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = ""; + char *s, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = ""; struct ast_vm_user vmus; char *options = NULL; int silent = 0, skipuser = 0; int res = -1; - if (s) { - s = ast_strdupa(s); + if (data) { + s = ast_strdupa(data); user = strsep(&s, ","); options = strsep(&s, ","); if (user) { @@ -10637,10 +10707,10 @@ if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) { ast_debug(1, "Enabled SMDI voicemail notification\n"); if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) { - smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find(val) : NULL; + smdi_iface = ast_smdi_interface_find(val); } else { ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n"); - smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find("/dev/ttyS0") : NULL; + smdi_iface = ast_smdi_interface_find("/dev/ttyS0"); } if (!smdi_iface) { ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n"); @@ -11098,7 +11168,7 @@ res |= ast_register_application_xml(app3, vm_box_exists); res |= ast_register_application_xml(app4, vmauthenticate); res |= ast_custom_function_register(&mailbox_exists_acf); - res |= ast_manager_register("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users, "List All Voicemail User Information"); + res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users); if (res) return res; Index: apps/app_dial.c =================================================================== --- a/apps/app_dial.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_dial.c (.../trunk) (revision 202568) @@ -54,7 +54,7 @@ #include "asterisk/utils.h" #include "asterisk/app.h" #include "asterisk/causes.h" -#include "asterisk/rtp.h" +#include "asterisk/rtp_engine.h" #include "asterisk/cdr.h" #include "asterisk/manager.h" #include "asterisk/privacy.h" @@ -109,11 +109,13 @@ + + + @@ -383,7 +396,6 @@ This application will report normal termination if the originating channel hangs up, or if the call is bridged and either of the parties in the bridge ends the call. - If the OUTBOUND_GROUP variable is set, all peer channels created by this application will be put into that group (as in Set(GROUP()=...). If the OUTBOUND_GROUP_ONCE variable is set, all peer channels created by this @@ -453,8 +465,8 @@ ***/ -static char *app = "Dial"; -static char *rapp = "RetryDial"; +static const char app[] = "Dial"; +static const char rapp[] = "RetryDial"; enum { OPT_ANNOUNCE = (1 << 0), @@ -465,12 +477,13 @@ OPT_GO_ON = (1 << 5), OPT_CALLEE_HANGUP = (1 << 6), OPT_CALLER_HANGUP = (1 << 7), + OPT_ORIGINAL_CLID = (1 << 8), OPT_DURATION_LIMIT = (1 << 9), OPT_MUSICBACK = (1 << 10), OPT_CALLEE_MACRO = (1 << 11), OPT_SCREEN_NOINTRO = (1 << 12), - OPT_SCREEN_NOCLID = (1 << 13), - OPT_ORIGINAL_CLID = (1 << 14), + OPT_SCREEN_NOCALLERID = (1 << 13), + OPT_IGNORE_CONNECTEDLINE = (1 << 14), OPT_SCREENING = (1 << 15), OPT_PRIVACY = (1 << 16), OPT_RINGBACK = (1 << 17), @@ -491,9 +504,11 @@ #define DIAL_STILLGOING (1 << 31) #define DIAL_NOFORWARDHTML ((uint64_t)1 << 32) /* flags are now 64 bits, so keep it up! */ -#define OPT_CANCEL_ELSEWHERE ((uint64_t)1 << 33) -#define OPT_PEER_H ((uint64_t)1 << 34) -#define OPT_CALLEE_GO_ON ((uint64_t)1 << 35) +#define DIAL_NOCONNECTEDLINE ((uint64_t)1 << 33) +#define OPT_CANCEL_ELSEWHERE ((uint64_t)1 << 34) +#define OPT_PEER_H ((uint64_t)1 << 35) +#define OPT_CALLEE_GO_ON ((uint64_t)1 << 36) +#define OPT_CANCEL_TIMEOUT ((uint64_t)1 << 37) enum { OPT_ARG_ANNOUNCE = 0, @@ -525,13 +540,14 @@ AST_APP_OPTION('h', OPT_CALLEE_HANGUP), AST_APP_OPTION('H', OPT_CALLER_HANGUP), AST_APP_OPTION('i', OPT_IGNORE_FORWARDING), + AST_APP_OPTION('I', OPT_IGNORE_CONNECTEDLINE), AST_APP_OPTION('k', OPT_CALLEE_PARK), AST_APP_OPTION('K', OPT_CALLER_PARK), AST_APP_OPTION_ARG('L', OPT_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT), AST_APP_OPTION_ARG('m', OPT_MUSICBACK, OPT_ARG_MUSICBACK), AST_APP_OPTION_ARG('M', OPT_CALLEE_MACRO, OPT_ARG_CALLEE_MACRO), AST_APP_OPTION('n', OPT_SCREEN_NOINTRO), - AST_APP_OPTION('N', OPT_SCREEN_NOCLID), + AST_APP_OPTION('N', OPT_SCREEN_NOCALLERID), AST_APP_OPTION('o', OPT_ORIGINAL_CLID), AST_APP_OPTION_ARG('O', OPT_OPERMODE, OPT_ARG_OPERMODE), AST_APP_OPTION('p', OPT_SCREENING), @@ -545,6 +561,7 @@ AST_APP_OPTION('W', OPT_CALLER_MONITOR), AST_APP_OPTION('x', OPT_CALLEE_MIXMONITOR), AST_APP_OPTION('X', OPT_CALLER_MIXMONITOR), + AST_APP_OPTION('z', OPT_CANCEL_TIMEOUT), END_OPTIONS ); #define CAN_EARLY_BRIDGE(flags,chan,peer) (!ast_test_flag64(flags, OPT_CALLEE_HANGUP | \ @@ -559,8 +576,10 @@ struct chanlist *next; struct ast_channel *chan; uint64_t flags; + struct ast_party_connected_line connected; }; +static int detect_disconnect(struct ast_channel *chan, char code, struct ast_str *featurecode); static void hanguptree(struct chanlist *outgoing, struct ast_channel *exception, int answered_elsewhere) { @@ -575,6 +594,7 @@ /* This is for the channel drivers */ outgoing->chan->hangupcause = AST_CAUSE_ANSWERED_ELSEWHERE; } + ast_party_connected_line_free(&outgoing->connected); ast_hangup(outgoing->chan); } oo = outgoing; @@ -659,12 +679,17 @@ return 0; } - +/* do not call with chan lock held */ static const char *get_cid_name(char *name, int namelen, struct ast_channel *chan) { - const char *context = S_OR(chan->macrocontext, chan->context); - const char *exten = S_OR(chan->macroexten, chan->exten); + const char *context; + const char *exten; + ast_channel_lock(chan); + context = ast_strdupa(S_OR(chan->macrocontext, chan->context)); + exten = ast_strdupa(S_OR(chan->macroexten, chan->exten)); + ast_channel_unlock(chan); + return ast_get_hint(NULL, 0, name, namelen, chan, context, exten) ? name : ""; } @@ -699,14 +724,18 @@ * * XXX this code is highly suspicious, as it essentially overwrites * the outgoing channel without properly deleting it. + * + * \todo eventually this function should be intergrated into and replaced by ast_call_forward() */ static void do_forward(struct chanlist *o, - struct cause_args *num, struct ast_flags64 *peerflags, int single) + struct cause_args *num, struct ast_flags64 *peerflags, int single, int *to) { char tmpchan[256]; struct ast_channel *original = o->chan; struct ast_channel *c = o->chan; /* the winner */ struct ast_channel *in = num->chan; /* the input channel */ + struct ast_party_redirecting *apr = &o->chan->redirecting; + struct ast_party_connected_line *apc = &o->chan->connected; char *stuff; char *tech; int cause; @@ -750,28 +779,42 @@ handle_cause(cause, num); ast_hangup(original); } else { - char *new_cid_num, *new_cid_name; - struct ast_channel *src; + if (single) { + ast_rtp_instance_early_bridge_make_compatible(c, in); + } - ast_rtp_make_compatible(c, in, single); + c->cdrflags = in->cdrflags; + + ast_channel_set_redirecting(c, apr); + ast_channel_lock(c); + while (ast_channel_trylock(in)) { + CHANNEL_DEADLOCK_AVOIDANCE(c); + } + S_REPLACE(c->cid.cid_rdnis, ast_strdup(S_OR(original->cid.cid_rdnis, S_OR(in->macroexten, in->exten)))); + + c->cid.cid_tns = in->cid.cid_tns; + if (ast_test_flag64(o, OPT_FORCECLID)) { - new_cid_num = ast_strdup(S_OR(in->macroexten, in->exten)); - new_cid_name = NULL; /* XXX no name ? */ - src = c; /* XXX possible bug in previous code, which used 'winner' ? it may have changed */ + S_REPLACE(c->cid.cid_num, ast_strdupa(S_OR(in->macroexten, in->exten))); + S_REPLACE(c->cid.cid_name, NULL); + ast_string_field_set(c, accountcode, c->accountcode); } else { - new_cid_num = ast_strdup(in->cid.cid_num); - new_cid_name = ast_strdup(in->cid.cid_name); - src = in; + ast_party_caller_copy(&c->cid, &in->cid); + ast_string_field_set(c, accountcode, in->accountcode); } - ast_string_field_set(c, accountcode, src->accountcode); - c->cdrflags = src->cdrflags; - S_REPLACE(c->cid.cid_num, new_cid_num); - S_REPLACE(c->cid.cid_name, new_cid_name); + ast_party_connected_line_copy(&c->connected, apc); - if (in->cid.cid_ani) { /* XXX or maybe unconditional ? */ - S_REPLACE(c->cid.cid_ani, ast_strdup(in->cid.cid_ani)); + S_REPLACE(in->cid.cid_rdnis, ast_strdup(c->cid.cid_rdnis)); + ast_channel_update_redirecting(in, apr); + + ast_clear_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE); + if (ast_test_flag64(peerflags, OPT_CANCEL_TIMEOUT)) { + *to = -1; } - S_REPLACE(c->cid.cid_rdnis, ast_strdup(S_OR(in->macroexten, in->exten))); + + ast_channel_unlock(in); + ast_channel_unlock(c); + if (ast_call(c, tmpchan, 0)) { ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan); ast_clear_flag64(o, DIAL_STILLGOING); @@ -780,11 +823,21 @@ c = o->chan = NULL; num->nochan++; } else { + ast_channel_lock(c); + while (ast_channel_trylock(in)) { + CHANNEL_DEADLOCK_AVOIDANCE(c); + } senddialevent(in, c, stuff); - /* After calling, set callerid to extension */ if (!ast_test_flag64(peerflags, OPT_ORIGINAL_CLID)) { char cidname[AST_MAX_EXTENSION] = ""; - ast_set_callerid(c, S_OR(in->macroexten, in->exten), get_cid_name(cidname, sizeof(cidname), in), NULL); + const char *tmpexten; + tmpexten = ast_strdupa(S_OR(in->macroexten, in->exten)); + ast_channel_unlock(in); + ast_channel_unlock(c); + ast_set_callerid(c, tmpexten, get_cid_name(cidname, sizeof(cidname), in), NULL); + } else { + ast_channel_unlock(in); + ast_channel_unlock(c); } /* Hangup the original channel now, in case we needed it */ ast_hangup(original); @@ -807,23 +860,37 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct chanlist *outgoing, int *to, struct ast_flags64 *peerflags, struct privacy_args *pa, - const struct cause_args *num_in, int *result) + const struct cause_args *num_in, int *result, char *dtmf_progress) { struct cause_args num = *num_in; int prestart = num.busy + num.congestion + num.nochan; int orig = *to; struct ast_channel *peer = NULL; /* single is set if only one destination is enabled */ - int single = outgoing && !outgoing->next && !ast_test_flag64(outgoing, OPT_MUSICBACK | OPT_RINGBACK); + int single = outgoing && !outgoing->next; #ifdef HAVE_EPOLL struct chanlist *epollo; #endif + struct ast_party_connected_line connected_caller; + struct ast_str *featurecode = ast_str_alloca(FEATURE_MAX_LEN + 1); + ast_party_connected_line_init(&connected_caller); if (single) { /* Turn off hold music, etc */ - ast_deactivate_generator(in); + if (!ast_test_flag64(outgoing, OPT_MUSICBACK | OPT_RINGBACK)) + ast_deactivate_generator(in); + /* If we are calling a single channel, make them compatible for in-band tone purpose */ ast_channel_make_compatible(outgoing->chan, in); + + if (!ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE) && !ast_test_flag64(outgoing, DIAL_NOCONNECTEDLINE)) { + ast_channel_lock(outgoing->chan); + ast_connected_line_copy_from_caller(&connected_caller, &outgoing->chan->cid); + ast_channel_unlock(outgoing->chan); + connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; + ast_channel_update_connected_line(in, &connected_caller); + ast_party_connected_line_free(&connected_caller); + } } #ifdef HAVE_EPOLL @@ -870,6 +937,20 @@ if (ast_test_flag64(o, DIAL_STILLGOING) && c->_state == AST_STATE_UP) { if (!peer) { ast_verb(3, "%s answered %s\n", c->name, in->name); + if (!single && !ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) { + if (o->connected.id.number) { + if (ast_channel_connected_line_macro(c, in, &o->connected, 1, 0)) { + ast_channel_update_connected_line(in, &o->connected); + } + } else if (!ast_test_flag64(o, DIAL_NOCONNECTEDLINE)) { + ast_channel_lock(c); + ast_connected_line_copy_from_caller(&connected_caller, &c->cid); + ast_channel_unlock(c); + connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; + ast_channel_update_connected_line(in, &connected_caller); + ast_party_connected_line_free(&connected_caller); + } + } peer = c; ast_copy_flags64(peerflags, o, OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER | @@ -887,7 +968,7 @@ continue; /* here, o->chan == c == winner */ if (!ast_strlen_zero(c->call_forward)) { - do_forward(o, &num, peerflags, single); + do_forward(o, &num, peerflags, single, to); continue; } f = ast_read(winner); @@ -908,6 +989,20 @@ /* This is our guy if someone answered. */ if (!peer) { ast_verb(3, "%s answered %s\n", c->name, in->name); + if (!single && !ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) { + if (o->connected.id.number) { + if (ast_channel_connected_line_macro(c, in, &o->connected, 1, 0)) { + ast_channel_update_connected_line(in, &o->connected); + } + } else if (!ast_test_flag64(o, DIAL_NOCONNECTEDLINE)) { + ast_channel_lock(c); + ast_connected_line_copy_from_caller(&connected_caller, &c->cid); + ast_channel_unlock(c); + connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; + ast_channel_update_connected_line(in, &connected_caller); + ast_party_connected_line_free(&connected_caller); + } + } peer = c; if (peer->cdr) { peer->cdr->answer = ast_tvnow(); @@ -963,6 +1058,10 @@ ast_channel_early_bridge(in, c); if (!ast_test_flag64(outgoing, OPT_RINGBACK)) ast_indicate(in, AST_CONTROL_PROGRESS); + if(!ast_strlen_zero(dtmf_progress)) { + ast_verb(3, "Sending DTMF '%s' to the called party as result of receiving a PROGRESS message.\n", dtmf_progress); + ast_dtmf_stream(c, in, dtmf_progress, 250, 0); + } break; case AST_CONTROL_VIDUPDATE: ast_verb(3, "%s requested a video update, passing it to %s\n", c->name, in->name); @@ -972,6 +1071,30 @@ ast_verb(3, "%s requested a source update, passing it to %s\n", c->name, in->name); ast_indicate(in, AST_CONTROL_SRCUPDATE); break; + case AST_CONTROL_CONNECTED_LINE: + if (ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) { + ast_verb(3, "Connected line update to %s prevented.\n", in->name); + } else if (!single) { + struct ast_party_connected_line connected; + ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", c->name, in->name); + ast_party_connected_line_set_init(&connected, &o->connected); + ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected); + ast_party_connected_line_set(&o->connected, &connected); + ast_party_connected_line_free(&connected); + } else { + if (ast_channel_connected_line_macro(c, in, f, 1, 1)) { + ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen); + } + } + break; + case AST_CONTROL_REDIRECTING: + if (ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) { + ast_verb(3, "Redirecting update to %s prevented.\n", in->name); + } else { + ast_verb(3, "%s redirecting info has changed, passing it to %s\n", c->name, in->name); + ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen); + } + break; case AST_CONTROL_PROCEEDING: ast_verb(3, "%s is proceeding passing it to %s\n", c->name, in->name); if (single && CAN_EARLY_BRIDGE(peerflags, in, c)) @@ -1063,8 +1186,8 @@ } if (ast_test_flag64(peerflags, OPT_CALLER_HANGUP) && - (f->subclass == '*')) { /* hmm it it not guaranteed to be '*' anymore. */ - ast_verb(3, "User hit %c to disconnect call.\n", f->subclass); + detect_disconnect(in, f->subclass, featurecode)) { + ast_verb(3, "User requested call disconnect.\n"); *to = 0; strcpy(pa->status, "CANCEL"); ast_cdr_noanswer(in->cdr); @@ -1082,13 +1205,19 @@ if (ast_write(outgoing->chan, f)) ast_log(LOG_WARNING, "Unable to forward voice or dtmf\n"); } - if (single && (f->frametype == AST_FRAME_CONTROL) && - ((f->subclass == AST_CONTROL_HOLD) || - (f->subclass == AST_CONTROL_UNHOLD) || - (f->subclass == AST_CONTROL_VIDUPDATE) || - (f->subclass == AST_CONTROL_SRCUPDATE))) { - ast_verb(3, "%s requested special control %d, passing it to %s\n", in->name, f->subclass, outgoing->chan->name); - ast_indicate_data(outgoing->chan, f->subclass, f->data.ptr, f->datalen); + if (single && (f->frametype == AST_FRAME_CONTROL)) { + if ((f->subclass == AST_CONTROL_HOLD) || + (f->subclass == AST_CONTROL_UNHOLD) || + (f->subclass == AST_CONTROL_VIDUPDATE) || + (f->subclass == AST_CONTROL_SRCUPDATE) || + (f->subclass == AST_CONTROL_REDIRECTING)) { + ast_verb(3, "%s requested special control %d, passing it to %s\n", in->name, f->subclass, outgoing->chan->name); + ast_indicate_data(outgoing->chan, f->subclass, f->data.ptr, f->datalen); + } else if (f->subclass == AST_CONTROL_CONNECTED_LINE) { + if (ast_channel_connected_line_macro(in, outgoing->chan, f, 0, 1)) { + ast_indicate_data(outgoing->chan, f->subclass, f->data.ptr, f->datalen); + } + } } ast_frfree(f); } @@ -1108,6 +1237,26 @@ return peer; } +static int detect_disconnect(struct ast_channel *chan, char code, struct ast_str *featurecode) +{ + struct ast_flags features = { AST_FEATURE_DISCONNECT }; /* only concerned with disconnect feature */ + struct ast_call_feature feature = { 0, }; + int res; + + ast_str_append(&featurecode, 1, "%c", code); + + res = ast_feature_detect(chan, &features, ast_str_buffer(featurecode), &feature); + + if (res != AST_FEATURE_RETURN_STOREDIGITS) { + ast_str_reset(featurecode); + } + if (feature.feature_mask & AST_FEATURE_DISCONNECT) { + return 1; + } + + return 0; +} + static void replace_macro_delimiter(char *s) { for (; *s; s++) @@ -1316,7 +1465,7 @@ ast_autoservice_stop(chan); if (ast_test_flag64(opts, OPT_PRIVACY) && (res2 >= '1' && res2 <= '5')) { /* map keypresses to various things, the index is res2 - '1' */ - static const char *_val[] = { "ALLOW", "DENY", "TORTURE", "KILL", "ALLOW" }; + static const char * const _val[] = { "ALLOW", "DENY", "TORTURE", "KILL", "ALLOW" }; static const int _flag[] = { AST_PRIVACY_ALLOW, AST_PRIVACY_DENY, AST_PRIVACY_TORTURE, AST_PRIVACY_KILL, AST_PRIVACY_ALLOW}; int i = res2 - '1'; ast_verb(3, "--Set privacy database entry %s/%s to %s\n", @@ -1405,11 +1554,11 @@ ast_copy_string(pa->privcid, l, sizeof(pa->privcid)); - if (strncmp(pa->privcid, "NOCALLERID", 10) != 0 && ast_test_flag64(opts, OPT_SCREEN_NOCLID)) { - /* if callerid is set and OPT_SCREEN_NOCLID is set also */ + if (strncmp(pa->privcid, "NOCALLERID", 10) != 0 && ast_test_flag64(opts, OPT_SCREEN_NOCALLERID)) { + /* if callerid is set and OPT_SCREEN_NOCALLERID is set also */ ast_verb(3, "CallerID set (%s); N option set; Screening should be off\n", pa->privcid); pa->privdb_val = AST_PRIVACY_ALLOW; - } else if (ast_test_flag64(opts, OPT_SCREEN_NOCLID) && strncmp(pa->privcid, "NOCALLERID", 10) == 0) { + } else if (ast_test_flag64(opts, OPT_SCREEN_NOCALLERID) && strncmp(pa->privcid, "NOCALLERID", 10) == 0) { ast_verb(3, "CallerID blank; N option set; Screening should happen; dbval is %d\n", pa->privdb_val); } @@ -1499,7 +1648,7 @@ bconfig->end_bridge_callback_data = originator; } -static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags64 *peerflags, int *continue_exec) +static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast_flags64 *peerflags, int *continue_exec) { int res = -1; /* default: error */ char *rest, *cur; /* scan the list of destinations */ @@ -1513,7 +1662,7 @@ struct ast_bridge_config config = { { 0, } }; struct timeval calldurationlimit = { 0, }; - char *dtmfcalled = NULL, *dtmfcalling = NULL; + char *dtmfcalled = NULL, *dtmfcalling = NULL, *dtmf_progress=NULL; struct privacy_args pa = { .sentringing = 0, .privdb_val = 0, @@ -1564,12 +1713,11 @@ goto done; } - if (ast_test_flag64(&opts, OPT_OPERMODE)) { opermode = ast_strlen_zero(opt_args[OPT_ARG_OPERMODE]) ? 1 : atoi(opt_args[OPT_ARG_OPERMODE]); ast_verb(3, "Setting operator services mode to %d.\n", opermode); } - + if (ast_test_flag64(&opts, OPT_DURATION_STOP) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_STOP])) { calldurationlimit.tv_sec = atoi(opt_args[OPT_ARG_DURATION_STOP]); if (!calldurationlimit.tv_sec) { @@ -1581,8 +1729,9 @@ } if (ast_test_flag64(&opts, OPT_SENDDTMF) && !ast_strlen_zero(opt_args[OPT_ARG_SENDDTMF])) { - dtmfcalling = opt_args[OPT_ARG_SENDDTMF]; - dtmfcalled = strsep(&dtmfcalling, ":"); + dtmf_progress = opt_args[OPT_ARG_SENDDTMF]; + dtmfcalled = strsep(&dtmf_progress, ":"); + dtmfcalling = strsep(&dtmf_progress, ":"); } if (ast_test_flag64(&opts, OPT_DURATION_LIMIT) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])) { @@ -1602,7 +1751,7 @@ res = -1; /* reset default */ } - if (ast_test_flag64(&opts, OPT_DTMF_EXIT)) { + if (ast_test_flag64(&opts, OPT_DTMF_EXIT) || ast_test_flag64(&opts, OPT_CALLER_HANGUP)) { __ast_answer(chan, 0, 0); } @@ -1619,7 +1768,7 @@ outbound_group = ast_strdupa(outbound_group); } ast_channel_unlock(chan); - ast_copy_flags64(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING); + ast_copy_flags64(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING | OPT_IGNORE_CONNECTEDLINE | OPT_CANCEL_TIMEOUT); /* loop through the list of dial destinations */ rest = args.peers; @@ -1656,6 +1805,14 @@ ast_channel_lock(chan); datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL); + /* If the incoming channel has previously had connected line information + * set on it (perhaps through the CONNECTED_LINE dialplan function) then + * seed the calllist's connected line information with this previously + * acquired info + */ + if (chan->connected.id.number) { + ast_party_connected_line_copy(&tmp->connected, &chan->connected); + } ast_channel_unlock(chan); if (datastore) @@ -1728,8 +1885,14 @@ } pbx_builtin_setvar_helper(tc, "DIALEDPEERNUMBER", numsubst); + ast_channel_lock(tc); + while (ast_channel_trylock(chan)) { + CHANNEL_DEADLOCK_AVOIDANCE(tc); + } /* Setup outgoing SDP to match incoming one */ - ast_rtp_make_compatible(tc, chan, !outgoing && !rest); + if (!outgoing && !rest) { + ast_rtp_instance_early_bridge_make_compatible(tc, chan); + } /* Inherit specially named variables from parent channel */ ast_channel_inherit_variables(chan, tc); @@ -1739,20 +1902,31 @@ tc->data = "(Outgoing Line)"; memset(&tc->whentohangup, 0, sizeof(tc->whentohangup)); - S_REPLACE(tc->cid.cid_num, ast_strdup(chan->cid.cid_num)); - S_REPLACE(tc->cid.cid_name, ast_strdup(chan->cid.cid_name)); - S_REPLACE(tc->cid.cid_ani, ast_strdup(chan->cid.cid_ani)); + /* If the new channel has no callerid, try to guess what it should be */ + if (ast_strlen_zero(tc->cid.cid_num)) { + if (!ast_strlen_zero(chan->connected.id.number)) { + ast_set_callerid(tc, chan->connected.id.number, chan->connected.id.name, chan->connected.ani); + } else if (!ast_strlen_zero(chan->cid.cid_dnid)) { + ast_set_callerid(tc, chan->cid.cid_dnid, NULL, NULL); + } else if (!ast_strlen_zero(S_OR(chan->macroexten, chan->exten))) { + ast_set_callerid(tc, S_OR(chan->macroexten, chan->exten), NULL, NULL); + } + ast_set_flag64(tmp, DIAL_NOCONNECTEDLINE); + } + + ast_connected_line_copy_from_caller(&tc->connected, &chan->cid); + S_REPLACE(tc->cid.cid_rdnis, ast_strdup(chan->cid.cid_rdnis)); - + ast_party_redirecting_copy(&tc->redirecting, &chan->redirecting); + + tc->cid.cid_tns = chan->cid.cid_tns; + ast_string_field_set(tc, accountcode, chan->accountcode); tc->cdrflags = chan->cdrflags; if (ast_strlen_zero(tc->musicclass)) ast_string_field_set(tc, musicclass, chan->musicclass); - /* Pass callingpres, type of number, tns, ADSI CPE, transfer capability */ - tc->cid.cid_pres = chan->cid.cid_pres; - tc->cid.cid_ton = chan->cid.cid_ton; - tc->cid.cid_tns = chan->cid.cid_tns; - tc->cid.cid_ani2 = chan->cid.cid_ani2; + + /* Pass ADSI CPE and transfer capability */ tc->adsicpe = chan->adsicpe; tc->transfercapability = chan->transfercapability; @@ -1775,6 +1949,7 @@ else ast_copy_string(tc->exten, chan->exten, sizeof(tc->exten)); + ast_channel_unlock(tc); res = ast_call(tc, numsubst, 0); /* Place the call, but don't wait on the answer */ /* Save the info in cdr's that we called them */ @@ -1789,15 +1964,19 @@ if (tc->hangupcause) { chan->hangupcause = tc->hangupcause; } + ast_channel_unlock(chan); ast_hangup(tc); tc = NULL; ast_free(tmp); continue; } else { + const char *tmpexten = ast_strdupa(S_OR(chan->macroexten, chan->exten)); senddialevent(chan, tc, numsubst); ast_verb(3, "Called %s\n", numsubst); - if (!ast_test_flag64(peerflags, OPT_ORIGINAL_CLID)) - ast_set_callerid(tc, S_OR(chan->macroexten, chan->exten), get_cid_name(cidname, sizeof(cidname), chan), NULL); + ast_channel_unlock(chan); + if (!ast_test_flag64(peerflags, OPT_ORIGINAL_CLID)) { + ast_set_callerid(tc, tmpexten, get_cid_name(cidname, sizeof(cidname), chan), NULL); + } } /* Put them in the list of outgoing thingies... We're ready now. XXX If we're forcibly removed, these outgoing calls won't get @@ -1849,7 +2028,7 @@ } } - peer = wait_for_answer(chan, outgoing, &to, peerflags, &pa, &num, &result); + peer = wait_for_answer(chan, outgoing, &to, peerflags, &pa, &num, &result, dtmf_progress); /* The ast_channel_datastore_remove() function could fail here if the * datastore was moved to another channel during a masquerade. If this is @@ -2206,9 +2385,18 @@ } ast_set2_flag(peer, autoloopflag, AST_FLAG_IN_AUTOLOOP); /* set it back the way it was */ } - if (!ast_check_hangup(peer) && ast_test_flag64(&opts, OPT_CALLEE_GO_ON) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GO_ON])) { - replace_macro_delimiter(opt_args[OPT_ARG_CALLEE_GO_ON]); - ast_parseable_goto(peer, opt_args[OPT_ARG_CALLEE_GO_ON]); + if (!ast_check_hangup(peer) && ast_test_flag64(&opts, OPT_CALLEE_GO_ON)) { + if(!ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GO_ON])) { + replace_macro_delimiter(opt_args[OPT_ARG_CALLEE_GO_ON]); + ast_parseable_goto(peer, opt_args[OPT_ARG_CALLEE_GO_ON]); + } else { /* F() */ + int res; + res = ast_goto_if_exists(peer, chan->context, chan->exten, (chan->priority) + 1); + if (res == AST_PBX_GOTO_FAILED) { + ast_hangup(peer); + goto out; + } + } ast_pbx_start(peer); } else { if (!ast_check_hangup(chan)) @@ -2249,7 +2437,7 @@ return res; } -static int dial_exec(struct ast_channel *chan, void *data) +static int dial_exec(struct ast_channel *chan, const char *data) { struct ast_flags64 peerflags; @@ -2258,7 +2446,7 @@ return dial_exec_full(chan, data, &peerflags, NULL); } -static int retrydial_exec(struct ast_channel *chan, void *data) +static int retrydial_exec(struct ast_channel *chan, const char *data) { char *parse; const char *context = NULL; Index: apps/app_nbscat.c =================================================================== --- a/apps/app_nbscat.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_nbscat.c (.../trunk) (revision 202568) @@ -105,7 +105,7 @@ } -static int NBScat_exec(struct ast_channel *chan, void *data) +static int NBScat_exec(struct ast_channel *chan, const char *data) { int res=0; int fds[2]; Index: apps/app_page.c =================================================================== --- a/apps/app_page.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_page.c (.../trunk) (revision 202568) @@ -99,15 +99,15 @@ ***/ -static const char *app_page= "Page"; +static const char * const app_page= "Page"; -enum { +enum page_opt_flags { PAGE_DUPLEX = (1 << 0), PAGE_QUIET = (1 << 1), PAGE_RECORD = (1 << 2), PAGE_SKIP = (1 << 3), PAGE_IGNORE_FORWARDS = (1 << 4), -} page_opt_flags; +}; AST_APP_OPTIONS(page_opts, { AST_APP_OPTION('d', PAGE_DUPLEX), @@ -118,7 +118,7 @@ }); -static int page_exec(struct ast_channel *chan, void *data) +static int page_exec(struct ast_channel *chan, const char *data) { char *tech, *resource, *tmp; char meetmeopts[88], originator[AST_CHANNEL_NAME], *opts[0]; Index: apps/app_echo.c =================================================================== --- a/apps/app_echo.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_echo.c (.../trunk) (revision 202568) @@ -46,9 +46,9 @@ ***/ -static char *app = "Echo"; +static const char app[] = "Echo"; -static int echo_exec(struct ast_channel *chan, void *data) +static int echo_exec(struct ast_channel *chan, const char *data) { int res = -1; int format; Index: apps/app_waitforsilence.c =================================================================== --- a/apps/app_waitforsilence.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_waitforsilence.c (.../trunk) (revision 202568) @@ -202,7 +202,7 @@ return res; } -static int waitfor_exec(struct ast_channel *chan, void *data, int wait_for_silence) +static int waitfor_exec(struct ast_channel *chan, const char *data, int wait_for_silence) { int res = 1; int timereqd = 1000; @@ -232,12 +232,12 @@ return res; } -static int waitforsilence_exec(struct ast_channel *chan, void *data) +static int waitforsilence_exec(struct ast_channel *chan, const char *data) { return waitfor_exec(chan, data, 1); } -static int waitfornoise_exec(struct ast_channel *chan, void *data) +static int waitfornoise_exec(struct ast_channel *chan, const char *data) { return waitfor_exec(chan, data, 0); } Index: apps/app_sayunixtime.c =================================================================== --- a/apps/app_sayunixtime.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_sayunixtime.c (.../trunk) (revision 202568) @@ -89,7 +89,7 @@ static char *app_sayunixtime = "SayUnixTime"; static char *app_datetime = "DateTime"; -static int sayunixtime_exec(struct ast_channel *chan, void *data) +static int sayunixtime_exec(struct ast_channel *chan, const char *data) { AST_DECLARE_APP_ARGS(args, AST_APP_ARG(timeval); Index: apps/app_readexten.c =================================================================== --- a/apps/app_readexten.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_readexten.c (.../trunk) (revision 202568) @@ -111,11 +111,11 @@ ***/ -enum { +enum readexten_option_flags { OPT_SKIP = (1 << 0), OPT_INDICATION = (1 << 1), OPT_NOANSWER = (1 << 2), -} readexten_option_flags; +}; AST_APP_OPTIONS(readexten_app_options, { AST_APP_OPTION('s', OPT_SKIP), @@ -125,7 +125,7 @@ static char *app = "ReadExten"; -static int readexten_exec(struct ast_channel *chan, void *data) +static int readexten_exec(struct ast_channel *chan, const char *data) { int res = 0; char exten[256] = ""; Index: apps/app_dahdiras.c =================================================================== --- a/apps/app_dahdiras.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_dahdiras.c (.../trunk) (revision 202568) @@ -73,7 +73,7 @@ ***/ -static char *app = "DAHDIRAS"; +static const char app[] = "DAHDIRAS"; #define PPP_MAX_ARGS 32 #define PPP_EXEC "/usr/sbin/pppd" @@ -187,7 +187,7 @@ ast_safe_fork_cleanup(); } -static int dahdiras_exec(struct ast_channel *chan, void *data) +static int dahdiras_exec(struct ast_channel *chan, const char *data) { int res=-1; char *args; Index: apps/app_disa.c =================================================================== --- a/apps/app_disa.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_disa.c (.../trunk) (revision 202568) @@ -110,12 +110,12 @@ ***/ -static char *app = "DISA"; +static const char app[] = "DISA"; enum { NOANSWER_FLAG = (1 << 0), POUND_TO_END_FLAG = (1 << 1), -} option_flags; +}; AST_APP_OPTIONS(app_opts, { AST_APP_OPTION('n', NOANSWER_FLAG), @@ -140,7 +140,7 @@ } } -static int disa_exec(struct ast_channel *chan, void *data) +static int disa_exec(struct ast_channel *chan, const char *data) { int i = 0, j, k = 0, did_ignore = 0, special_noanswer = 0; int firstdigittimeout = (chan->pbx ? chan->pbx->rtimeoutms : 20000); Index: apps/app_userevent.c =================================================================== --- a/apps/app_userevent.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_userevent.c (.../trunk) (revision 202568) @@ -56,7 +56,7 @@ static char *app = "UserEvent"; -static int userevent_exec(struct ast_channel *chan, void *data) +static int userevent_exec(struct ast_channel *chan, const char *data) { char *parse; int x; Index: apps/app_chanisavail.c =================================================================== --- a/apps/app_chanisavail.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_chanisavail.c (.../trunk) (revision 202568) @@ -41,7 +41,7 @@ #include "asterisk/app.h" #include "asterisk/devicestate.h" -static char *app = "ChanIsAvail"; +static const char app[] = "ChanIsAvail"; /*** DOCUMENTATION @@ -92,7 +92,7 @@ ***/ -static int chanavail_exec(struct ast_channel *chan, void *data) +static int chanavail_exec(struct ast_channel *chan, const char *data) { int inuse=-1, option_state=0, string_compare=0, option_all_avail=0; int status; Index: apps/app_system.c =================================================================== --- a/apps/app_system.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_system.c (.../trunk) (revision 202568) @@ -100,7 +100,7 @@ static char *chanvar = "SYSTEMSTATUS"; -static int system_exec_helper(struct ast_channel *chan, void *data, int failmode) +static int system_exec_helper(struct ast_channel *chan, const char *data, int failmode) { int res = 0; struct ast_str *buf = ast_str_thread_get(&buf_buf, 16); @@ -140,12 +140,12 @@ return res; } -static int system_exec(struct ast_channel *chan, void *data) +static int system_exec(struct ast_channel *chan, const char *data) { return system_exec_helper(chan, data, -1); } -static int trysystem_exec(struct ast_channel *chan, void *data) +static int trysystem_exec(struct ast_channel *chan, const char *data) { return system_exec_helper(chan, data, 0); } Index: apps/app_transfer.c =================================================================== --- a/apps/app_transfer.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_transfer.c (.../trunk) (revision 202568) @@ -72,9 +72,9 @@ ***/ -static const char *app = "Transfer"; +static const char * const app = "Transfer"; -static int transfer_exec(struct ast_channel *chan, void *data) +static int transfer_exec(struct ast_channel *chan, const char *data) { int res; int len; Index: apps/app_playback.c =================================================================== --- a/apps/app_playback.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_playback.c (.../trunk) (revision 202568) @@ -90,8 +90,8 @@ * 'say load [new|old]' will enable the new or old method, or report status */ static const void *say_api_buf[40]; -static const char *say_old = "old"; -static const char *say_new = "new"; +static const char * const say_old = "old"; +static const char * const say_new = "new"; static void save_say_mode(const void *arg) { @@ -380,7 +380,7 @@ static char *__say_cli_init(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { const char *old_mode = say_api_buf[0] ? say_new : say_old; - char *mode; + const char *mode; switch (cmd) { case CLI_INIT: e->command = "say load [new|old]"; @@ -415,7 +415,7 @@ AST_CLI_DEFINE(__say_cli_init, "Set or show the say mode"), }; -static int playback_exec(struct ast_channel *chan, void *data) +static int playback_exec(struct ast_channel *chan, const char *data) { int res = 0; int mres = 0; Index: apps/app_speech_utils.c =================================================================== --- a/apps/app_speech_utils.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_speech_utils.c (.../trunk) (revision 202568) @@ -481,7 +481,7 @@ /*! \brief SpeechCreate() Dialplan Application */ -static int speech_create(struct ast_channel *chan, void *data) +static int speech_create(struct ast_channel *chan, const char *data) { struct ast_speech *speech = NULL; struct ast_datastore *datastore = NULL; @@ -508,7 +508,7 @@ } /*! \brief SpeechLoadGrammar(Grammar Name,Path) Dialplan Application */ -static int speech_load(struct ast_channel *chan, void *vdata) +static int speech_load(struct ast_channel *chan, const char *vdata) { int res = 0; struct ast_speech *speech = find_speech(chan); @@ -534,7 +534,7 @@ } /*! \brief SpeechUnloadGrammar(Grammar Name) Dialplan Application */ -static int speech_unload(struct ast_channel *chan, void *data) +static int speech_unload(struct ast_channel *chan, const char *data) { int res = 0; struct ast_speech *speech = find_speech(chan); @@ -549,7 +549,7 @@ } /*! \brief SpeechDeactivateGrammar(Grammar Name) Dialplan Application */ -static int speech_deactivate(struct ast_channel *chan, void *data) +static int speech_deactivate(struct ast_channel *chan, const char *data) { int res = 0; struct ast_speech *speech = find_speech(chan); @@ -564,7 +564,7 @@ } /*! \brief SpeechActivateGrammar(Grammar Name) Dialplan Application */ -static int speech_activate(struct ast_channel *chan, void *data) +static int speech_activate(struct ast_channel *chan, const char *data) { int res = 0; struct ast_speech *speech = find_speech(chan); @@ -579,7 +579,7 @@ } /*! \brief SpeechStart() Dialplan Application */ -static int speech_start(struct ast_channel *chan, void *data) +static int speech_start(struct ast_channel *chan, const char *data) { int res = 0; struct ast_speech *speech = find_speech(chan); @@ -593,7 +593,7 @@ } /*! \brief SpeechProcessingSound(Sound File) Dialplan Application */ -static int speech_processing_sound(struct ast_channel *chan, void *data) +static int speech_processing_sound(struct ast_channel *chan, const char *data) { int res = 0; struct ast_speech *speech = find_speech(chan); @@ -636,7 +636,7 @@ END_OPTIONS ); /*! \brief SpeechBackground(Sound File,Timeout) Dialplan Application */ -static int speech_background(struct ast_channel *chan, void *data) +static int speech_background(struct ast_channel *chan, const char *data) { unsigned int timeout = 0; int res = 0, done = 0, started = 0, quieted = 0, max_dtmf_len = 0; @@ -888,7 +888,7 @@ /*! \brief SpeechDestroy() Dialplan Application */ -static int speech_destroy(struct ast_channel *chan, void *data) +static int speech_destroy(struct ast_channel *chan, const char *data) { int res = 0; struct ast_speech *speech = find_speech(chan); Index: apps/app_osplookup.c =================================================================== --- a/apps/app_osplookup.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_osplookup.c (.../trunk) (revision 202568) @@ -39,6 +39,7 @@ #include #include +#include #include "asterisk/paths.h" #include "asterisk/lock.h" @@ -52,9 +53,162 @@ #include "asterisk/cli.h" #include "asterisk/astosp.h" +/*** DOCUMENTATION + + + OSP Authentication. + + + + + + + Authenticate a SIP INVITE by OSP and sets the variables: + + + The inbound call transaction handle. + + + The inbound call duration limit in seconds. + + + This application sets the following channel variable upon completion: + + + The status of the OSP Auth attempt as a text string, one of + + + + + + + + + + Lookup destination by OSP. + + + + + + + + generate H323 call id for the outbound call + + + generate SIP call id for the outbound call. + Have not been implemented + + + generate IAX call id for the outbound call. + Have not been implemented + + + + + + Looks up an extension via OSP and sets the variables, where n is the + number of the result beginning with 1: + + + The OSP Handle for anything remaining. + + + The technology to use for the call. + + + The destination to use for the call. + + + The called number to use for the call. + + + The calling number to use for the call. + + + The dial command string. + + + The actual OSP token as a string. + + + The outbound call duraction limit in seconds. + + + The outbound call id types. + + + The outbound call id. + + + The number of OSP results total remaining. + + + + + This application sets the following channel variable upon completion: + + + + + + + + + + Lookup next destination by OSP. + + + + + + + + Looks up the next OSP Destination for OSPOUTHANDLE. + This application sets the following channel variable upon completion: + + + The status of the OSP Next attempt as a text string, one of + + + + + + + + OSPLookup + + + + + Record OSP entry. + + + + + + + Records call state for OSPINHANDLE, according to status, which should + be one of BUSY, CONGESTION, ANSWER, + NOANSWER, or CHANUNAVAIL or coincidentally, just what the + Dial application stores in its DIALSTATUS. + This application sets the following channel variable upon completion: + + + The status of the OSP Finish attempt as a text string, one of + + + + + + + + ***/ + /* OSP Buffer Sizes */ #define OSP_INTSTR_SIZE ((unsigned int)16) /* OSP signed/unsigned int string buffer size */ #define OSP_NORSTR_SIZE ((unsigned int)256) /* OSP normal string buffer size */ +#define OSP_KEYSTR_SIZE ((unsigned int)1024) /* OSP certificate string buffer size */ #define OSP_TOKSTR_SIZE ((unsigned int)4096) /* OSP token string buffer size */ #define OSP_TECHSTR_SIZE ((unsigned int)32) /* OSP signed/unsigned int string buffer size */ #define OSP_UUID_SIZE ((unsigned int)16) /* UUID size */ @@ -127,7 +281,7 @@ char privatekey[OSP_NORSTR_SIZE]; /* OSP private key file name */ char localcert[OSP_NORSTR_SIZE]; /* OSP local cert file name */ unsigned int cacount; /* Number of cacerts */ - char cacerts[OSP_MAX_CERTS][OSP_NORSTR_SIZE]; /* Cacert file names */ + char cacerts[OSP_MAX_CERTS][OSP_NORSTR_SIZE]; /* Cacert file names */ unsigned int spcount; /* Number of service points */ char srvpoints[OSP_MAX_SRVS][OSP_NORSTR_SIZE]; /* Service point URLs */ int maxconnections; /* Max number of connections */ @@ -167,9 +321,15 @@ AST_MUTEX_DEFINE_STATIC(osplock); /* Lock of OSP provider list */ static int osp_initialized = 0; /* Init flag */ static int osp_hardware = 0; /* Hardware accelleration flag */ +static int osp_security = 0; /* Using security features flag */ static struct osp_provider* ospproviders = NULL; /* OSP provider list */ static unsigned int osp_tokenformat = TOKEN_ALGO_SIGNED; /* Token format supported */ +/* OSP default certificates */ +const char* B64PKey = "MIIBOgIBAAJBAK8t5l+PUbTC4lvwlNxV5lpl+2dwSZGW46dowTe6y133XyVEwNiiRma2YNk3xKs/TJ3Wl9Wpns2SYEAJsFfSTukCAwEAAQJAPz13vCm2GmZ8Zyp74usTxLCqSJZNyMRLHQWBM0g44Iuy4wE3vpi7Wq+xYuSOH2mu4OddnxswCP4QhaXVQavTAQIhAOBVCKXtppEw9UaOBL4vW0Ed/6EA/1D8hDW6St0h7EXJAiEAx+iRmZKhJD6VT84dtX5ZYNVk3j3dAcIOovpzUj9a0CECIEduTCapmZQ5xqAEsLXuVlxRtQgLTUD4ZxDElPn8x0MhAiBE2HlcND0+qDbvtwJQQOUzDgqg5xk3w8capboVdzAlQQIhAMC+lDL7+gDYkNAft5Mu+NObJmQs4Cr+DkDFsKqoxqrm"; +const char* B64LCert = "MIIBeTCCASMCEHqkOHVRRWr+1COq3CR/xsowDQYJKoZIhvcNAQEEBQAwOzElMCMGA1UEAxMcb3NwdGVzdHNlcnZlci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQU2VydmVyMB4XDTA1MDYyMzAwMjkxOFoXDTA2MDYyNDAwMjkxOFowRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCvLeZfj1G0wuJb8JTcVeZaZftncEmRluOnaME3ustd918lRMDYokZmtmDZN8SrP0yd1pfVqZ7NkmBACbBX0k7pAgMBAAEwDQYJKoZIhvcNAQEEBQADQQDnV8QNFVVJx/+7IselU0wsepqMurivXZzuxOmTEmTVDzCJx1xhA8jd3vGAj7XDIYiPub1PV23eY5a2ARJuw5w9"; +const char* B64CACert = "MIIBYDCCAQoCAQEwDQYJKoZIhvcNAQEEBQAwOzElMCMGA1UEAxMcb3NwdGVzdHNlcnZlci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQU2VydmVyMB4XDTAyMDIwNDE4MjU1MloXDTEyMDIwMzE4MjU1MlowOzElMCMGA1UEAxMcb3NwdGVzdHNlcnZlci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQU2VydmVyMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPGeGwV41EIhX0jEDFLRXQhDEr50OUQPq+f55VwQd0TQNts06BP29+UiNdRW3c3IRHdZcJdC1Cg68ME9cgeq0h8CAwEAATANBgkqhkiG9w0BAQQFAANBAGkzBSj1EnnmUxbaiG1N4xjIuLAWydun7o3bFk2tV8dBIhnuh445obYyk1EnQ27kI7eACCILBZqi2MHDOIMnoN0="; + /* OSP Client Wrapper APIs */ /*! @@ -190,7 +350,10 @@ OSPT_CERT cacerts[OSP_MAX_CERTS]; const OSPT_CERT* pcacerts[OSP_MAX_CERTS]; const char* psrvpoints[OSP_MAX_SRVS]; - int t, i, j, error = OSPC_ERR_NO_ERROR; + unsigned char privatekeydata[OSP_KEYSTR_SIZE]; + unsigned char localcertdata[OSP_KEYSTR_SIZE]; + unsigned char cacertdata[OSP_KEYSTR_SIZE]; + int i, t, error = OSPC_ERR_NO_ERROR; if (!(p = ast_calloc(1, sizeof(*p)))) { ast_log(LOG_ERROR, "Out of memory\n"); @@ -213,30 +376,36 @@ v = ast_variable_browse(cfg, provider); while(v) { if (!strcasecmp(v->name, "privatekey")) { - if (v->value[0] == '/') { - ast_copy_string(p->privatekey, v->value, sizeof(p->privatekey)); - } else { - snprintf(p->privatekey, sizeof(p->privatekey), "%s/%s", ast_config_AST_KEY_DIR, v->value); + if (osp_security) { + if (v->value[0] == '/') { + ast_copy_string(p->privatekey, v->value, sizeof(p->privatekey)); + } else { + snprintf(p->privatekey, sizeof(p->privatekey), "%s/%s", ast_config_AST_KEY_DIR, v->value); + } + ast_debug(1, "OSP: privatekey '%s'\n", p->privatekey); } - ast_debug(1, "OSP: privatekey '%s'\n", p->privatekey); } else if (!strcasecmp(v->name, "localcert")) { - if (v->value[0] == '/') { - ast_copy_string(p->localcert, v->value, sizeof(p->localcert)); - } else { - snprintf(p->localcert, sizeof(p->localcert), "%s/%s", ast_config_AST_KEY_DIR, v->value); + if (osp_security) { + if (v->value[0] == '/') { + ast_copy_string(p->localcert, v->value, sizeof(p->localcert)); + } else { + snprintf(p->localcert, sizeof(p->localcert), "%s/%s", ast_config_AST_KEY_DIR, v->value); + } + ast_debug(1, "OSP: localcert '%s'\n", p->localcert); } - ast_debug(1, "OSP: localcert '%s'\n", p->localcert); } else if (!strcasecmp(v->name, "cacert")) { - if (p->cacount < OSP_MAX_CERTS) { - if (v->value[0] == '/') { - ast_copy_string(p->cacerts[p->cacount], v->value, sizeof(p->cacerts[0])); + if (osp_security) { + if (p->cacount < OSP_MAX_CERTS) { + if (v->value[0] == '/') { + ast_copy_string(p->cacerts[p->cacount], v->value, sizeof(p->cacerts[0])); + } else { + snprintf(p->cacerts[p->cacount], sizeof(p->cacerts[0]), "%s/%s", ast_config_AST_KEY_DIR, v->value); + } + ast_debug(1, "OSP: cacerts[%d]: '%s'\n", p->cacount, p->cacerts[p->cacount]); + p->cacount++; } else { - snprintf(p->cacerts[p->cacount], sizeof(p->cacerts[0]), "%s/%s", ast_config_AST_KEY_DIR, v->value); + ast_log(LOG_WARNING, "OSP: Too many CA Certificates at line %d\n", v->lineno); } - ast_debug(1, "OSP: cacert[%d]: '%s'\n", p->cacount, p->cacerts[p->cacount]); - p->cacount++; - } else { - ast_log(LOG_WARNING, "OSP: Too many CA Certificates at line %d\n", v->lineno); } } else if (!strcasecmp(v->name, "servicepoint")) { if (p->spcount < OSP_MAX_SRVS) { @@ -307,96 +476,110 @@ v = v->next; } - error = OSPPUtilLoadPEMPrivateKey((unsigned char*)p->privatekey, &privatekey); - if (error != OSPC_ERR_NO_ERROR) { - ast_log(LOG_WARNING, "OSP: Unable to load privatekey '%s', error '%d'\n", p->privatekey, error); - ast_free(p); - return 0; + if (p->cacount == 0) { + p->cacount = 1; } - error = OSPPUtilLoadPEMCert((unsigned char*)p->localcert, &localcert); - if (error != OSPC_ERR_NO_ERROR) { - ast_log(LOG_WARNING, "OSP: Unable to load localcert '%s', error '%d'\n", p->localcert, error); - if (privatekey.PrivateKeyData) { - ast_free(privatekey.PrivateKeyData); - } - ast_free(p); - return 0; + for (i = 0; i < p->spcount; i++) { + psrvpoints[i] = p->srvpoints[i]; } - if (p->cacount < 1) { - snprintf(p->cacerts[p->cacount], sizeof(p->cacerts[0]), "%s/%s-cacert.pem", ast_config_AST_KEY_DIR, provider); - ast_debug(1, "OSP: cacert[%d]: '%s'\n", p->cacount, p->cacerts[p->cacount]); - p->cacount++; - } - for (i = 0; i < p->cacount; i++) { - error = OSPPUtilLoadPEMCert((unsigned char*)p->cacerts[i], &cacerts[i]); - if (error != OSPC_ERR_NO_ERROR) { - ast_log(LOG_WARNING, "OSP: Unable to load cacert '%s', error '%d'\n", p->cacerts[i], error); - for (j = 0; j < i; j++) { - if (cacerts[j].CertData) { - ast_free(cacerts[j].CertData); + if (osp_security) { + privatekey.PrivateKeyData = NULL; + privatekey.PrivateKeyLength = 0; + + localcert.CertData = NULL; + localcert.CertDataLength = 0; + + for (i = 0; i < p->cacount; i++) { + cacerts[i].CertData = NULL; + cacerts[i].CertDataLength = 0; + } + + if ((error = OSPPUtilLoadPEMPrivateKey((unsigned char*)p->privatekey, &privatekey)) != OSPC_ERR_NO_ERROR) { + ast_log(LOG_WARNING, "OSP: Unable to load privatekey '%s', error '%d'\n", p->privatekey, error); + } else if ((error = OSPPUtilLoadPEMCert((unsigned char*)p->localcert, &localcert)) != OSPC_ERR_NO_ERROR) { + ast_log(LOG_WARNING, "OSP: Unable to load localcert '%s', error '%d'\n", p->localcert, error); + } else { + for (i = 0; i < p->cacount; i++) { + if ((error = OSPPUtilLoadPEMCert((unsigned char*)p->cacerts[i], &cacerts[i])) != OSPC_ERR_NO_ERROR) { + ast_log(LOG_WARNING, "OSP: Unable to load cacert '%s', error '%d'\n", p->cacerts[i], error); + break; + } else { + pcacerts[i] = &cacerts[i]; } } - if (localcert.CertData) { - ast_free(localcert.CertData); - } - if (privatekey.PrivateKeyData) { - ast_free(privatekey.PrivateKeyData); - } - ast_free(p); - return 0; } - pcacerts[i] = &cacerts[i]; - } + } else { + privatekey.PrivateKeyData = privatekeydata; + privatekey.PrivateKeyLength = sizeof(privatekeydata); - for (i = 0; i < p->spcount; i++) { - psrvpoints[i] = p->srvpoints[i]; + localcert.CertData = localcertdata; + localcert.CertDataLength = sizeof(localcertdata); + + cacerts[0].CertData = cacertdata; + cacerts[0].CertDataLength = sizeof(cacertdata); + pcacerts[0] = &cacerts[0]; + + if ((error = OSPPBase64Decode(B64PKey, strlen(B64PKey), privatekey.PrivateKeyData, &privatekey.PrivateKeyLength)) != OSPC_ERR_NO_ERROR) { + ast_log(LOG_WARNING, "OSP: Unable to decode private key, error '%d'\n", error); + } else if ((error = OSPPBase64Decode(B64LCert, strlen(B64LCert), localcert.CertData, &localcert.CertDataLength)) != OSPC_ERR_NO_ERROR) { + ast_log(LOG_WARNING, "OSP: Unable to decode local cert, error '%d'\n", error); + } else if ((error = OSPPBase64Decode(B64CACert, strlen(B64CACert), cacerts[0].CertData, &cacerts[0].CertDataLength)) != OSPC_ERR_NO_ERROR) { + ast_log(LOG_WARNING, "OSP: Unable to decode cacert, error '%d'\n", error); + } } - error = OSPPProviderNew( - p->spcount, - psrvpoints, - NULL, - OSP_AUDIT_URL, - &privatekey, - &localcert, - p->cacount, - pcacerts, - OSP_LOCAL_VALIDATION, - OSP_SSL_LIFETIME, - p->maxconnections, - OSP_HTTP_PERSISTENCE, - p->retrydelay, - p->retrylimit, - p->timeout, - OSP_CUSTOMER_ID, - OSP_DEVICE_ID, - &p->handle); - if (error != OSPC_ERR_NO_ERROR) { - ast_log(LOG_WARNING, "OSP: Unable to create provider '%s', error '%d'\n", provider, error); - ast_free(p); - res = -1; - } else { - ast_debug(1, "OSP: provider '%s'\n", provider); - ast_mutex_lock(&osplock); - p->next = ospproviders; - ospproviders = p; - ast_mutex_unlock(&osplock); - res = 1; + if (error == OSPC_ERR_NO_ERROR) { + error = OSPPProviderNew( + p->spcount, + psrvpoints, + NULL, + OSP_AUDIT_URL, + &privatekey, + &localcert, + p->cacount, + pcacerts, + OSP_LOCAL_VALIDATION, + OSP_SSL_LIFETIME, + p->maxconnections, + OSP_HTTP_PERSISTENCE, + p->retrydelay, + p->retrylimit, + p->timeout, + OSP_CUSTOMER_ID, + OSP_DEVICE_ID, + &p->handle); + if (error != OSPC_ERR_NO_ERROR) { + ast_log(LOG_WARNING, "OSP: Unable to create provider '%s', error '%d'\n", provider, error); + res = -1; + } else { + ast_debug(1, "OSP: provider '%s'\n", provider); + ast_mutex_lock(&osplock); + p->next = ospproviders; + ospproviders = p; + ast_mutex_unlock(&osplock); + res = 1; + } } - for (i = 0; i < p->cacount; i++) { - if (cacerts[i].CertData) { - ast_free(cacerts[i].CertData); + if (osp_security) { + for (i = 0; i < p->cacount; i++) { + if (cacerts[i].CertData) { + ast_free(cacerts[i].CertData); + } } + if (localcert.CertData) { + ast_free(localcert.CertData); + } + if (privatekey.PrivateKeyData) { + ast_free(privatekey.PrivateKeyData); + } } - if (localcert.CertData) { - ast_free(localcert.CertData); + + if (res != 1) { + ast_free(p); } - if (privatekey.PrivateKeyData) { - ast_free(privatekey.PrivateKeyData); - } return res; } @@ -849,6 +1032,8 @@ * \param srcdev Source device of outbound call * \param calling Calling number * \param called Called number + * \param snetid Source network ID + * \param rnumber Routing number * \param callidtypes Call ID types * \param result Lookup results * \return 1 Found , 0 No route, -1 Error @@ -858,6 +1043,8 @@ const char* srcdev, const char* calling, const char* called, + const char* snetid, + const char* rnumber, unsigned int callidtypes, struct osp_result* result) { @@ -903,6 +1090,14 @@ return -1; } + if (!ast_strlen_zero(snetid)) { + OSPPTransactionSetNetworkIds(result->outhandle, snetid, ""); + } + + if (!ast_strlen_zero(rnumber)) { + OSPPTransactionSetRoutingNumber(result->outhandle, rnumber); + } + callidnum = 0; callids[0] = NULL; for (i = 0; i < OSP_CALLID_MAXNUM; i++) { @@ -1253,8 +1448,8 @@ * \return 0 Success, -1 Failed */ static int ospauth_exec( - struct ast_channel* chan, - void* data) + struct ast_channel *chan, + const char *data) { int res; const char* provider = OSP_DEF_PROVIDER; @@ -1334,7 +1529,7 @@ */ static int osplookup_exec( struct ast_channel* chan, - void* data) + const char * data) { int res, cres; const char* provider = OSP_DEF_PROVIDER; @@ -1342,6 +1537,7 @@ struct ast_var_t* current; const char* srcdev = ""; const char* snetid = ""; + const char* rnumber = ""; char buffer[OSP_TOKSTR_SIZE]; unsigned int callidtypes = OSP_CALLID_UNDEFINED; struct osp_result result; @@ -1401,6 +1597,8 @@ } } else if (!strcasecmp(ast_var_name(current), "OSPINNETWORKID")) { snetid = ast_var_value(current); + } else if (!strcasecmp(ast_var_name(current), "OSPROUTINGNUMBER")) { + rnumber = ast_var_value(current); } else if (!strcasecmp(ast_var_name(current), "OSPPEERIP")) { srcdev = ast_var_value(current); } @@ -1408,13 +1606,14 @@ ast_debug(1, "OSPLookup: OSPINHANDLE '%d'\n", result.inhandle); ast_debug(1, "OSPLookup: OSPINTIMELIMIT '%d'\n", result.intimelimit); ast_debug(1, "OSPLookup: OSPINNETWORKID '%s'\n", snetid); + ast_debug(1, "OSPLookup: OSPROUTINGNUMBER '%s'\n", rnumber); ast_debug(1, "OSPLookup: source device '%s'\n", srcdev); if ((cres = ast_autoservice_start(chan)) < 0) { return -1; } - if ((res = osp_lookup(provider, srcdev, chan->cid.cid_num, args.exten, callidtypes, &result)) > 0) { + if ((res = osp_lookup(provider, srcdev, chan->cid.cid_num, args.exten, snetid, rnumber, callidtypes, &result)) > 0) { status = AST_OSP_SUCCESS; } else { result.tech[0] = '\0'; @@ -1445,6 +1644,8 @@ ast_debug(1, "OSPLookup: OSPCALLED '%s'\n", result.called); pbx_builtin_setvar_helper(chan, "OSPCALLING", result.calling); ast_debug(1, "OSPLookup: OSPCALLING '%s'\n", result.calling); + pbx_builtin_setvar_helper(chan, "OSPOUTNETWORKID", result.networkid); + ast_debug(1, "OSPLookup: OSPOUTNETWORKID '%s'\n", result.networkid); pbx_builtin_setvar_helper(chan, "OSPOUTTOKEN", result.token); ast_debug(1, "OSPLookup: OSPOUTTOKEN size '%zd'\n", strlen(result.token)); snprintf(buffer, sizeof(buffer), "%d", result.numresults); @@ -1502,7 +1703,7 @@ */ static int ospnext_exec( struct ast_channel* chan, - void* data) + const char * data) { int res; const char* provider = OSP_DEF_PROVIDER; @@ -1606,6 +1807,8 @@ ast_debug(1, "OSPNext: OSPCALLED'%s'\n", result.called); pbx_builtin_setvar_helper(chan, "OSPCALLING", result.calling); ast_debug(1, "OSPNext: OSPCALLING '%s'\n", result.calling); + pbx_builtin_setvar_helper(chan, "OSPOUTNETWORKID", result.networkid); + ast_debug(1, "OSPLookup: OSPOUTNETWORKID '%s'\n", result.networkid); pbx_builtin_setvar_helper(chan, "OSPOUTTOKEN", result.token); ast_debug(1, "OSPNext: OSPOUTTOKEN size '%zd'\n", strlen(result.token)); snprintf(buffer, sizeof(buffer), "%d", result.numresults); @@ -1656,7 +1859,7 @@ */ static int ospfinished_exec( struct ast_channel* chan, - void* data) + const char * data) { int res = 1; int cause = 0; @@ -1801,6 +2004,12 @@ } ast_debug(1, "OSP: osp_hardware '%d'\n", osp_hardware); + t = ast_variable_retrieve(cfg, OSP_GENERAL_CAT, "securityfeatures"); + if (t && ast_true(t)) { + osp_security = 1; + } + ast_debug(1, "OSP: osp_security '%d'\n", osp_security); + t = ast_variable_retrieve(cfg, OSP_GENERAL_CAT, "tokenformat"); if (t) { if ((sscanf(t, "%d", &v) == 1) && @@ -1854,6 +2063,7 @@ OSPPCleanup(); osp_tokenformat = TOKEN_ALGO_SIGNED; + osp_security = 0; osp_hardware = 0; osp_initialized = 0; } @@ -1896,8 +2106,11 @@ tokenalgo = "Signed"; break; } - ast_cli(a->fd, "OSP: %s %s %s\n", - osp_initialized ? "Initialized" : "Uninitialized", osp_hardware ? "Accelerated" : "Normal", tokenalgo); + ast_cli(a->fd, "OSP: %s/%s/%s/%s\n", + osp_initialized ? "Initialized" : "Uninitialized", + osp_hardware ? "Accelerated" : "Normal", + osp_security ? "Enabled" : "Disabled", + tokenalgo); } ast_mutex_lock(&osplock); @@ -1908,10 +2121,12 @@ ast_cli(a->fd, "\n"); } ast_cli(a->fd, " == OSP Provider '%s' == \n", p->name); - ast_cli(a->fd, "Local Private Key: %s\n", p->privatekey); - ast_cli(a->fd, "Local Certificate: %s\n", p->localcert); - for (i = 0; i < p->cacount; i++) { - ast_cli(a->fd, "CA Certificate %d: %s\n", i + 1, p->cacerts[i]); + if (osp_security) { + ast_cli(a->fd, "Local Private Key: %s\n", p->privatekey); + ast_cli(a->fd, "Local Certificate: %s\n", p->localcert); + for (i = 0; i < p->cacount; i++) { + ast_cli(a->fd, "CA Certificate %d: %s\n", i + 1, p->cacerts[i]); + } } for (i = 0; i < p->spcount; i++) { ast_cli(a->fd, "Service Point %d: %s\n", i + 1, p->srvpoints[i]); @@ -1940,63 +2155,17 @@ return CLI_SUCCESS; } -static const char* app1= "OSPAuth"; -static const char* synopsis1 = "OSP authentication"; -static const char* descrip1 = -" OSPAuth([provider[,options]]): Authenticate a SIP INVITE by OSP and sets\n" -"the variables:\n" -" ${OSPINHANDLE}: The inbound call transaction handle\n" -" ${OSPINTIMELIMIT}: The inbound call duration limit in seconds\n" -"\n" -"This application sets the following channel variable upon completion:\n" -" OSPAUTHSTATUS The status of the OSP Auth attempt as a text string, one of\n" -" SUCCESS | FAILED | ERROR\n"; +/* OSPAuth() dialplan application */ +static const char app1[] = "OSPAuth"; -static const char* app2= "OSPLookup"; -static const char* synopsis2 = "Lookup destination by OSP"; -static const char* descrip2 = -" OSPLookup(exten[,provider[,options]]): Looks up an extension via OSP and sets\n" -"the variables, where 'n' is the number of the result beginning with 1:\n" -" ${OSPOUTHANDLE}: The OSP Handle for anything remaining\n" -" ${OSPTECH}: The technology to use for the call\n" -" ${OSPDEST}: The destination to use for the call\n" -" ${OSPCALLED}: The called number to use for the call\n" -" ${OSPCALLING}: The calling number to use for the call\n" -" ${OSPDIALSTR}: The dial command string\n" -" ${OSPOUTTOKEN}: The actual OSP token as a string\n" -" ${OSPOUTTIMELIMIT}: The outbound call duration limit in seconds\n" -" ${OSPOUTCALLIDTYPES}: The outbound call id types\n" -" ${OSPOUTCALLID}: The outbound call id\n" -" ${OSPRESULTS}: The number of OSP results total remaining\n" -"\n" -"The option string may contain the following character:\n" -" 'h' -- generate H323 call id for the outbound call\n" -" 's' -- generate SIP call id for the outbound call. Have not been implemented\n" -" 'i' -- generate IAX call id for the outbound call. Have not been implemented\n" -"This application sets the following channel variable upon completion:\n" -" OSPLOOKUPSTATUS The status of the OSP Lookup attempt as a text string, one of\n" -" SUCCESS | FAILED | ERROR\n"; +/* OSPLookup() dialplan application */ +static const char app2[] = "OSPLookup"; -static const char* app3 = "OSPNext"; -static const char* synopsis3 = "Lookup next destination by OSP"; -static const char* descrip3 = -" OSPNext(cause[,provider[,options]]): Looks up the next OSP Destination for ${OSPOUTHANDLE}\n" -"See OSPLookup for more information\n" -"\n" -"This application sets the following channel variable upon completion:\n" -" OSPNEXTSTATUS The status of the OSP Next attempt as a text string, one of\n" -" SUCCESS | FAILED | ERROR\n"; +/* OSPNext() dialplan application */ +static const char app3[] = "OSPNext"; -static const char* app4 = "OSPFinish"; -static const char* synopsis4 = "Record OSP entry"; -static const char* descrip4 = -" OSPFinish([status[,options]]): Records call state for ${OSPINHANDLE}, according to\n" -"status, which should be one of BUSY, CONGESTION, ANSWER, NOANSWER, or CHANUNAVAIL\n" -"or coincidentally, just what the Dial application stores in its ${DIALSTATUS}.\n" -"\n" -"This application sets the following channel variable upon completion:\n" -" OSPFINISHSTATUS The status of the OSP Finish attempt as a text string, one of\n" -" SUCCESS | FAILED | ERROR \n"; +/* OSPFinish() dialplan application */ +static const char app4[] = "OSPFinish"; static struct ast_cli_entry cli_osp[] = { AST_CLI_DEFINE(handle_cli_osp_show, "Displays OSF information") @@ -2010,10 +2179,10 @@ return AST_MODULE_LOAD_DECLINE; ast_cli_register_multiple(cli_osp, sizeof(cli_osp) / sizeof(struct ast_cli_entry)); - res = ast_register_application(app1, ospauth_exec, synopsis1, descrip1); - res |= ast_register_application(app2, osplookup_exec, synopsis2, descrip2); - res |= ast_register_application(app3, ospnext_exec, synopsis3, descrip3); - res |= ast_register_application(app4, ospfinished_exec, synopsis4, descrip4); + res = ast_register_application_xml(app1, ospauth_exec); + res |= ast_register_application_xml(app2, osplookup_exec); + res |= ast_register_application_xml(app3, ospnext_exec); + res |= ast_register_application_xml(app4, ospfinished_exec); return res; } Index: apps/app_sendtext.c =================================================================== --- a/apps/app_sendtext.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_sendtext.c (.../trunk) (revision 202568) @@ -70,9 +70,9 @@ ***/ -static const char *app = "SendText"; +static const char * const app = "SendText"; -static int sendtext_exec(struct ast_channel *chan, void *data) +static int sendtext_exec(struct ast_channel *chan, const char *data) { int res = 0; char *status = "UNSUPPORTED"; Index: apps/app_amd.c =================================================================== --- a/apps/app_amd.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_amd.c (.../trunk) (revision 202568) @@ -124,7 +124,7 @@ ***/ -static char *app = "AMD"; +static const char app[] = "AMD"; #define STATE_IN_WORD 1 #define STATE_IN_SILENCE 2 @@ -143,7 +143,7 @@ /* Set to the lowest ms value provided in amd.conf or application parameters */ static int dfltMaxWaitTimeForFrame = 50; -static void isAnsweringMachine(struct ast_channel *chan, void *data) +static void isAnsweringMachine(struct ast_channel *chan, const char *data) { int res = 0; struct ast_frame *f = NULL; @@ -404,7 +404,7 @@ } -static int amd_exec(struct ast_channel *chan, void *data) +static int amd_exec(struct ast_channel *chan, const char *data) { isAnsweringMachine(chan, data); Index: apps/app_url.c =================================================================== --- a/apps/app_url.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_url.c (.../trunk) (revision 202568) @@ -82,15 +82,15 @@ static char *app = "SendURL"; -enum { +enum option_flags { OPTION_WAIT = (1 << 0), -} option_flags; +}; AST_APP_OPTIONS(app_opts,{ AST_APP_OPTION('w', OPTION_WAIT), }); -static int sendurl_exec(struct ast_channel *chan, void *data) +static int sendurl_exec(struct ast_channel *chan, const char *data) { int res = 0; char *tmp; Index: apps/app_externalivr.c =================================================================== --- a/apps/app_externalivr.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_externalivr.c (.../trunk) (revision 202568) @@ -48,34 +48,58 @@ #include "asterisk/tcptls.h" #include "asterisk/astobj2.h" -static const char *app = "ExternalIVR"; +/*** DOCUMENTATION + + + Interfaces with an external IVR application. + + + + + + + + + + + + + + + + Either forks a process to run given command or makes a socket to connect + to given host and starts a generator on the channel. The generator's play list + is controlled by the external application, which can add and clear entries via + simple commands issued over its stdout. The external application will receive + all DTMF events received on the channel, and notification if the channel is + hung up. The received on the channel, and notification if the channel is hung + up. The application will not be forcibly terminated when the channel is hung up. + See doc/externalivr.txt for a protocol specification. + + + ***/ -static const char *synopsis = "Interfaces with an external IVR application"; -static const char *descrip = -" ExternalIVR(command|ivr://ivrhosti([,arg[,arg...]])[,options]): Either forks a process\n" -"to run given command or makes a socket to connect to given host and starts\n" -"a generator on the channel. The generator's play list is controlled by the\n" -"external application, which can add and clear entries via simple commands\n" -"issued over its stdout. The external application will receive all DTMF events\n" -"received on the channel, and notification if the channel is hung up. The\n" -"application will not be forcibly terminated when the channel is hung up.\n" -"See doc/externalivr.txt for a protocol specification.\n" -"The 'n' option tells ExternalIVR() not to answer the channel. \n" -"The 'i' option tells ExternalIVR() not to send a hangup and exit when the\n" -" channel receives a hangup, instead it sends an 'I' informative message\n" -" meaning that the external application MUST hang up the call with an H command\n" -"The 'd' option tells ExternalIVR() to run on a channel that has been hung up\n" -" and will not look for hangups. The external application must exit with\n" -" an 'E' command.\n"; +static const char app[] = "ExternalIVR"; /* XXX the parser in gcc 2.95 gets confused if you don't put a space between 'name' and the comma */ #define ast_chan_log(level, channel, format, ...) ast_log(level, "%s: " format, channel->name , ## __VA_ARGS__) -enum { +enum options_flags { noanswer = (1 << 0), ignore_hangup = (1 << 1), run_dead = (1 << 2), -} options_flags; +}; AST_APP_OPTIONS(app_opts, { AST_APP_OPTION('n', noanswer), @@ -311,7 +335,7 @@ return entry; } -static int app_exec(struct ast_channel *chan, void *data) +static int app_exec(struct ast_channel *chan, const char *data) { struct ast_flags flags = { 0, }; char *opts[0]; @@ -800,7 +824,7 @@ static int load_module(void) { - return ast_register_application(app, app_exec, synopsis, descrip); + return ast_register_application_xml(app, app_exec); } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "External IVR Interface Application"); Index: apps/app_directory.c =================================================================== --- a/apps/app_directory.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_directory.c (.../trunk) (revision 202568) @@ -113,7 +113,7 @@ ***/ -static char *app = "Directory"; +static const char app[] = "Directory"; /* For simplicity, I'm keeping the format compatible with the voicemail config, but i'm open to suggestions for isolating it */ @@ -128,7 +128,7 @@ OPT_LISTBYLASTNAME = (1 << 4), OPT_LISTBYEITHER = OPT_LISTBYFIRSTNAME | OPT_LISTBYLASTNAME, OPT_PAUSE = (1 << 5), -} directory_option_flags; +}; enum { OPT_ARG_FIRSTNAME = 0, @@ -714,7 +714,7 @@ return res; } -static int directory_exec(struct ast_channel *chan, void *data) +static int directory_exec(struct ast_channel *chan, const char *data) { int res = 0, digit = 3; struct ast_config *cfg, *ucfg; Index: apps/app_while.c =================================================================== --- a/apps/app_while.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_while.c (.../trunk) (revision 202568) @@ -186,7 +186,7 @@ return res; } -static int _while_exec(struct ast_channel *chan, void *data, int end) +static int _while_exec(struct ast_channel *chan, const char *data, int end) { int res=0; const char *while_pri = NULL; @@ -296,19 +296,19 @@ return res; } -static int while_start_exec(struct ast_channel *chan, void *data) { +static int while_start_exec(struct ast_channel *chan, const char *data) { return _while_exec(chan, data, 0); } -static int while_end_exec(struct ast_channel *chan, void *data) { +static int while_end_exec(struct ast_channel *chan, const char *data) { return _while_exec(chan, data, 1); } -static int while_exit_exec(struct ast_channel *chan, void *data) { +static int while_exit_exec(struct ast_channel *chan, const char *data) { return _while_exec(chan, data, 2); } -static int while_continue_exec(struct ast_channel *chan, void *data) +static int while_continue_exec(struct ast_channel *chan, const char *data) { int x; const char *prefix = "WHILE", *while_pri=NULL; Index: apps/app_dahdibarge.c =================================================================== --- a/apps/app_dahdibarge.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_dahdibarge.c (.../trunk) (revision 202568) @@ -70,7 +70,7 @@ ***/ -static char *app = "DAHDIBarge"; +static const char app[] = "DAHDIBarge"; #define CONF_SIZE 160 @@ -258,7 +258,7 @@ return ret; } -static int conf_exec(struct ast_channel *chan, void *data) +static int conf_exec(struct ast_channel *chan, const char *data) { int res = -1; int retrycnt = 0; Index: apps/app_privacy.c =================================================================== --- a/apps/app_privacy.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_privacy.c (.../trunk) (revision 202568) @@ -80,7 +80,7 @@ static char *app = "PrivacyManager"; -static int privacy_exec (struct ast_channel *chan, void *data) +static int privacy_exec(struct ast_channel *chan, const char *data) { int res=0; int retries; Index: apps/app_record.c =================================================================== --- a/apps/app_record.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_record.c (.../trunk) (revision 202568) @@ -129,7 +129,7 @@ AST_APP_OPTION('x', OPTION_IGNORE_TERMINATE), }); -static int record_exec(struct ast_channel *chan, void *data) +static int record_exec(struct ast_channel *chan, const char *data) { int res = 0; int count = 0; Index: apps/app_authenticate.c =================================================================== --- a/apps/app_authenticate.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_authenticate.c (.../trunk) (revision 202568) @@ -43,7 +43,7 @@ OPT_DATABASE = (1 << 1), OPT_MULTIPLE = (1 << 3), OPT_REMOVE = (1 << 4), -} auth_option_flags; +}; AST_APP_OPTIONS(auth_app_options, { AST_APP_OPTION('a', OPT_ACCOUNT), @@ -53,7 +53,7 @@ }); -static char *app = "Authenticate"; +static const char app[] = "Authenticate"; /*** DOCUMENTATION @@ -105,7 +105,7 @@ ***/ -static int auth_exec(struct ast_channel *chan, void *data) +static int auth_exec(struct ast_channel *chan, const char *data) { int res = 0, retries, maxdigits; char passwd[256], *prompt = "agent-pass", *argcopy = NULL; Index: apps/app_fax.c =================================================================== --- a/apps/app_fax.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_fax.c (.../trunk) (revision 202568) @@ -141,8 +141,8 @@ ***/ -static char *app_sndfax_name = "SendFAX"; -static char *app_rcvfax_name = "ReceiveFAX"; +static const char app_sndfax_name[] = "SendFAX"; +static const char app_rcvfax_name[] = "ReceiveFAX"; #define MAX_SAMPLES 240 @@ -351,7 +351,7 @@ return 0; } -struct ast_generator generator = { +static struct ast_generator generator = { alloc: fax_generator_alloc, generate: fax_generator_generate, }; @@ -374,7 +374,7 @@ struct timeval now, start, state_change; enum ast_control_t38 t38control; -#if SPANDSP_RELEASE_DATE >= 20081012 +#if SPANDSP_RELEASE_DATE >= 20080725 /* for spandsp shaphots 0.0.6 and higher */ t30state = &fax.t30; #else @@ -553,7 +553,7 @@ t30_state_t *t30state; t38_core_state_t *t38state; -#if SPANDSP_RELEASE_DATE >= 20081012 +#if SPANDSP_RELEASE_DATE >= 20080725 /* for spandsp shaphots 0.0.6 and higher */ t30state = &t38.t30; t38state = &t38.t38_fe.t38; @@ -705,11 +705,12 @@ /* === Application functions === */ -static int sndfax_exec(struct ast_channel *chan, void *data) +static int sndfax_exec(struct ast_channel *chan, const char *data) { int res = 0; char *parse; fax_session session; + char restore_digit_detect = 0; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(file_name); @@ -744,16 +745,41 @@ session.chan = chan; session.finished = 0; + /* get current digit detection mode, then disable digit detection if enabled */ + { + int dummy = sizeof(restore_digit_detect); + + ast_channel_queryoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, &dummy, 0); + } + + if (restore_digit_detect) { + char new_digit_detect = 0; + + ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &new_digit_detect, sizeof(new_digit_detect), 0); + } + + /* disable FAX tone detection if enabled */ + { + char new_fax_detect = 0; + + ast_channel_setoption(chan, AST_OPTION_FAX_DETECT, &new_fax_detect, sizeof(new_fax_detect), 0); + } + res = transmit(&session); + if (restore_digit_detect) { + ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, sizeof(restore_digit_detect), 0); + } + return res; } -static int rcvfax_exec(struct ast_channel *chan, void *data) +static int rcvfax_exec(struct ast_channel *chan, const char *data) { int res = 0; char *parse; fax_session session; + char restore_digit_detect = 0; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(file_name); @@ -788,8 +814,32 @@ session.chan = chan; session.finished = 0; + /* get current digit detection mode, then disable digit detection if enabled */ + { + int dummy = sizeof(restore_digit_detect); + + ast_channel_queryoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, &dummy, 0); + } + + if (restore_digit_detect) { + char new_digit_detect = 0; + + ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &new_digit_detect, sizeof(new_digit_detect), 0); + } + + /* disable FAX tone detection if enabled */ + { + char new_fax_detect = 0; + + ast_channel_setoption(chan, AST_OPTION_FAX_DETECT, &new_fax_detect, sizeof(new_fax_detect), 0); + } + res = transmit(&session); + if (restore_digit_detect) { + ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, sizeof(restore_digit_detect), 0); + } + return res; } Index: apps/app_waituntil.c =================================================================== --- a/apps/app_waituntil.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_waituntil.c (.../trunk) (revision 202568) @@ -67,7 +67,7 @@ static char *app = "WaitUntil"; -static int waituntil_exec(struct ast_channel *chan, void *data) +static int waituntil_exec(struct ast_channel *chan, const char *data) { int res; double fraction; Index: apps/app_originate.c =================================================================== --- a/apps/app_originate.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_originate.c (.../trunk) (revision 202568) @@ -89,7 +89,7 @@ ***/ -static int originate_exec(struct ast_channel *chan, void *data) +static int originate_exec(struct ast_channel *chan, const char *data) { AST_DECLARE_APP_ARGS(args, AST_APP_ARG(tech_data); Index: apps/app_queue.c =================================================================== --- a/apps/app_queue.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_queue.c (.../trunk) (revision 202568) @@ -94,6 +94,7 @@ #include "asterisk/strings.h" #include "asterisk/global_datastores.h" #include "asterisk/taskprocessor.h" +#include "asterisk/callerid.h" /*! * \par Please read before modifying this file. @@ -141,6 +142,10 @@ Ignore call forward requests from queue members and do nothing when they are requested. + @@ -198,6 +203,11 @@ Will cause the queue's defaultrule to be overridden by the rule specified. + + Attempt to enter the caller into the queue at the numerical position specified. 1 + would attempt to enter the caller at the head of the queue, and 3 would attempt to place + the caller third in the queue. + In addition to transferring the call, a call may be parked and then picked @@ -484,7 +494,160 @@ Gets or sets queue members penalty. - + + + Queues. + + + + + + + + + + Show queue status. + + + + + + + + + + + + Show queue summary. + + + + + + + + + + + Add interface to queue. + + + + + + + + + + + + + + + + Remove interface from queue. + + + + + + + + + + + + Makes a queue member temporarily unavailable. + + + + + + + + + + + + + + Adds custom entry in queue_log. + + + + + + + + + + + + + + + Set the penalty for a queue member. + + + + + + + + + + + + + Queue Rules. + + + + + + + + + + + Reload a queue, queues, or any sub-section of a queue or queues. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Reset queue statistics. + + + + + + + + ***/ enum { @@ -547,7 +710,7 @@ static char *app_ql = "QueueLog" ; /*! \brief Persistent Members astdb family */ -static const char *pm_family = "Queue/PersistentMembers"; +static const char * const pm_family = "Queue/PersistentMembers"; /* The maximum length of each persistent member queue database entry */ #define PM_MAX_LEN 8192 @@ -583,7 +746,7 @@ QUEUE_CONTINUE = 7, }; -const struct { +static const struct { enum queue_result id; char *text; } queue_results[] = { @@ -625,6 +788,8 @@ time_t lastcall; struct call_queue *lastqueue; struct member *member; + unsigned int update_connectedline:1; + struct ast_party_connected_line connected; }; @@ -812,7 +977,7 @@ AST_LIST_ENTRY(rule_list) list; }; -AST_LIST_HEAD_STATIC(rule_lists, rule_list); +static AST_LIST_HEAD_STATIC(rule_lists, rule_list); static struct ao2_container *queues; @@ -1918,7 +2083,7 @@ ast_config_destroy(member_config); } -static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason) +static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason, int position) { struct call_queue *q; struct queue_ent *cur, *prev = NULL; @@ -1959,6 +2124,17 @@ insert_entry(q, prev, qe, &pos); inserted = 1; } + /* <= is necessary for the position comparison because it may not be possible to enter + * at our desired position since higher-priority callers may have taken the position we want + */ + if (!inserted && (qe->prio <= cur->prio) && position && (position <= pos + 1)) { + insert_entry(q, prev, qe, &pos); + /*pos is incremented inside insert_entry, so don't need to add 1 here*/ + if (position < pos) { + ast_log(LOG_NOTICE, "Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position, pos); + } + inserted = 1; + } cur->pos = ++pos; prev = cur; cur = cur->next; @@ -2219,12 +2395,13 @@ prev = NULL; for (current = q->head; current; current = current->next) { if (current == qe) { + char posstr[20]; q->count--; /* Take us out of the queue */ manager_event(EVENT_FLAG_CALL, "Leave", - "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n", - qe->chan->name, q->name, q->count, qe->chan->uniqueid); + "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nPosition: %d\r\nUniqueid: %s\r\n", + qe->chan->name, q->name, q->count, qe->pos, qe->chan->uniqueid); ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name ); /* Take us out of the queue */ if (prev) @@ -2234,6 +2411,8 @@ /* Free penalty rules */ while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list))) ast_free(pr_iter); + snprintf(posstr, sizeof(posstr), "%d", qe->pos); + pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr); } else { /* Renumber the people after us in the queue based on a new count */ current->pos = ++pos; @@ -2378,7 +2557,7 @@ static char *vars2manager(struct ast_channel *chan, char *vars, size_t len) { struct ast_str *buf = ast_str_thread_get(&ast_str_thread_global_buf, len + 1); - char *tmp; + const char *tmp; if (pbx_builtin_serialize_variables(chan, &buf)) { int i, j; @@ -2492,23 +2671,41 @@ (*busies)++; return 0; } - + + ast_channel_lock(tmp->chan); + while (ast_channel_trylock(qe->chan)) { + CHANNEL_DEADLOCK_AVOIDANCE(tmp->chan); + } + if (qe->cancel_answered_elsewhere) { ast_set_flag(tmp->chan, AST_FLAG_ANSWERED_ELSEWHERE); } tmp->chan->appl = "AppQueue"; tmp->chan->data = "(Outgoing Line)"; memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup)); - if (tmp->chan->cid.cid_num) - ast_free(tmp->chan->cid.cid_num); - tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num); - if (tmp->chan->cid.cid_name) - ast_free(tmp->chan->cid.cid_name); - tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name); - if (tmp->chan->cid.cid_ani) - ast_free(tmp->chan->cid.cid_ani); - tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani); + /* If the new channel has no callerid, try to guess what it should be */ + if (ast_strlen_zero(tmp->chan->cid.cid_num)) { + if (!ast_strlen_zero(qe->chan->connected.id.number)) { + ast_set_callerid(tmp->chan, qe->chan->connected.id.number, qe->chan->connected.id.name, qe->chan->connected.ani); + tmp->chan->cid.cid_pres = qe->chan->connected.id.number_presentation; + } else if (!ast_strlen_zero(qe->chan->cid.cid_dnid)) { + ast_set_callerid(tmp->chan, qe->chan->cid.cid_dnid, NULL, NULL); + } else if (!ast_strlen_zero(S_OR(qe->chan->macroexten, qe->chan->exten))) { + ast_set_callerid(tmp->chan, S_OR(qe->chan->macroexten, qe->chan->exten), NULL, NULL); + } + tmp->update_connectedline = 0; + } + + if (tmp->chan->cid.cid_rdnis) + ast_free(tmp->chan->cid.cid_rdnis); + tmp->chan->cid.cid_rdnis = ast_strdup(qe->chan->cid.cid_rdnis); + ast_party_redirecting_copy(&tmp->chan->redirecting, &qe->chan->redirecting); + + tmp->chan->cid.cid_tns = qe->chan->cid.cid_tns; + + ast_connected_line_copy_from_caller(&tmp->chan->connected, &qe->chan->cid); + /* Inherit specially named variables from parent channel */ ast_channel_inherit_variables(qe->chan, tmp->chan); @@ -2516,7 +2713,6 @@ tmp->chan->adsicpe = qe->chan->adsicpe; /* Inherit context and extension */ - ast_channel_lock(qe->chan); macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT"); ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext); macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN"); @@ -2539,13 +2735,14 @@ strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode); strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield); } - ast_channel_unlock(qe->chan); /* Place the call, but don't wait on the answer */ if ((res = ast_call(tmp->chan, location, 0))) { /* Again, keep going even if there's an error */ ast_debug(1, "ast call on peer returned %d\n", res); ast_verb(3, "Couldn't call %s\n", tmp->interface); + ast_channel_unlock(tmp->chan); + ast_channel_unlock(qe->chan); do_hang(tmp); (*busies)++; update_status(qe->parent, tmp->member, ast_device_state(tmp->member->state_interface)); @@ -2573,6 +2770,8 @@ qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); ast_verb(3, "Called %s\n", tmp->interface); } + ast_channel_unlock(tmp->chan); + ast_channel_unlock(qe->chan); update_status(qe->parent, tmp->member, ast_device_state(tmp->member->state_interface)); return 1; @@ -2802,8 +3001,10 @@ * \param[in] prebusies number of busy members calculated prior to calling wait_for_answer * \param[in] caller_disconnect if the 'H' option is used when calling Queue(), this is used to detect if the caller pressed * to disconnect the call * \param[in] forwardsallowed used to detect if we should allow call forwarding, based on the 'i' option to Queue() + * + * \todo eventually all call forward logic should be intergerated into and replaced by ast_call_forward() */ -static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed) +static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed, int update_connectedline) { const char *queue = qe->parent->name; struct callattempt *o, *start = NULL, *prev = NULL; @@ -2823,7 +3024,15 @@ #ifdef HAVE_EPOLL struct callattempt *epollo; #endif + struct ast_party_connected_line connected_caller; + char *inchan_name; + ast_party_connected_line_init(&connected_caller); + + ast_channel_lock(qe->chan); + inchan_name = ast_strdupa(qe->chan->name); + ast_channel_unlock(qe->chan); + starttime = (long) time(NULL); #ifdef HAVE_EPOLL for (epollo = outgoing; epollo; epollo = epollo->q_next) { @@ -2873,9 +3082,32 @@ } winner = ast_waitfor_n(watchers, pos, to); for (o = start; o; o = o->call_next) { + /* We go with a static buffer here instead of using ast_strdupa. Using + * ast_strdupa in a loop like this one can cause a stack overflow + */ + char ochan_name[AST_CHANNEL_NAME]; + if (o->chan) { + ast_channel_lock(o->chan); + ast_copy_string(ochan_name, o->chan->name, sizeof(ochan_name)); + ast_channel_unlock(o->chan); + } if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) { if (!peer) { - ast_verb(3, "%s answered %s\n", o->chan->name, in->name); + ast_verb(3, "%s answered %s\n", ochan_name, inchan_name); + if (update_connectedline) { + if (o->connected.id.number) { + if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) { + ast_channel_update_connected_line(in, &o->connected); + } + } else if (o->update_connectedline) { + ast_channel_lock(o->chan); + ast_connected_line_copy_from_caller(&connected_caller, &o->chan->cid); + ast_channel_unlock(o->chan); + connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; + ast_channel_update_connected_line(in, &connected_caller); + ast_party_connected_line_free(&connected_caller); + } + } peer = o; } } else if (o->chan && (o->chan == winner)) { @@ -2884,12 +3116,15 @@ ast_copy_string(membername, o->member->membername, sizeof(membername)); if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) { - ast_verb(3, "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward); + ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, o->chan->call_forward); numnochan++; do_hang(o); winner = NULL; continue; } else if (!ast_strlen_zero(o->chan->call_forward)) { + struct ast_party_redirecting *apr = &o->chan->redirecting; + struct ast_party_connected_line *apc = &o->chan->connected; + struct ast_channel *original = o->chan; char tmpchan[256]; char *stuff; char *tech; @@ -2904,7 +3139,7 @@ tech = "Local"; } /* Before processing channel, go ahead and check for forwarding */ - ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name); + ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name); /* Setup parameters */ o->chan = ast_request(tech, in->nativeformats, stuff, &status); if (!o->chan) { @@ -2912,32 +3147,44 @@ o->stillgoing = 0; numnochan++; } else { + ast_channel_lock(o->chan); + while (ast_channel_trylock(in)) { + CHANNEL_DEADLOCK_AVOIDANCE(o->chan); + } ast_channel_inherit_variables(in, o->chan); ast_channel_datastore_inherit(in, o->chan); - if (o->chan->cid.cid_num) - ast_free(o->chan->cid.cid_num); - o->chan->cid.cid_num = ast_strdup(in->cid.cid_num); - if (o->chan->cid.cid_name) - ast_free(o->chan->cid.cid_name); - o->chan->cid.cid_name = ast_strdup(in->cid.cid_name); - ast_string_field_set(o->chan, accountcode, in->accountcode); o->chan->cdrflags = in->cdrflags; - if (in->cid.cid_ani) { - if (o->chan->cid.cid_ani) - ast_free(o->chan->cid.cid_ani); - o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani); - } + ast_channel_set_redirecting(o->chan, apr); + if (o->chan->cid.cid_rdnis) ast_free(o->chan->cid.cid_rdnis); - o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten)); + o->chan->cid.cid_rdnis = ast_strdup(S_OR(original->cid.cid_rdnis,S_OR(in->macroexten, in->exten))); + + o->chan->cid.cid_tns = in->cid.cid_tns; + + ast_party_caller_copy(&o->chan->cid, &in->cid); + ast_party_connected_line_copy(&o->chan->connected, apc); + + ast_channel_update_redirecting(in, apr); + if (in->cid.cid_rdnis) { + ast_free(in->cid.cid_rdnis); + } + in->cid.cid_rdnis = ast_strdup(o->chan->cid.cid_rdnis); + + update_connectedline = 1; + if (ast_call(o->chan, tmpchan, 0)) { ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan); + ast_channel_unlock(o->chan); do_hang(o); numnochan++; + } else { + ast_channel_unlock(o->chan); } + ast_channel_unlock(in); } /* Hangup the original channel now, in case we needed it */ ast_hangup(winner); @@ -2950,12 +3197,26 @@ case AST_CONTROL_ANSWER: /* This is our guy if someone answered. */ if (!peer) { - ast_verb(3, "%s answered %s\n", o->chan->name, in->name); + ast_verb(3, "%s answered %s\n", ochan_name, inchan_name); + if (update_connectedline) { + if (o->connected.id.number) { + if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) { + ast_channel_update_connected_line(in, &o->connected); + } + } else if (o->update_connectedline) { + ast_channel_lock(o->chan); + ast_connected_line_copy_from_caller(&connected_caller, &o->chan->cid); + ast_channel_unlock(o->chan); + connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; + ast_channel_update_connected_line(in, &connected_caller); + ast_party_connected_line_free(&connected_caller); + } + } peer = o; } break; case AST_CONTROL_BUSY: - ast_verb(3, "%s is busy\n", o->chan->name); + ast_verb(3, "%s is busy\n", ochan_name); if (in->cdr) ast_cdr_busy(in->cdr); do_hang(o); @@ -2970,7 +3231,7 @@ numbusies++; break; case AST_CONTROL_CONGESTION: - ast_verb(3, "%s is circuit-busy\n", o->chan->name); + ast_verb(3, "%s is circuit-busy\n", ochan_name); if (in->cdr) ast_cdr_busy(in->cdr); endtime = (long) time(NULL); @@ -2985,13 +3246,38 @@ numbusies++; break; case AST_CONTROL_RINGING: - ast_verb(3, "%s is ringing\n", o->chan->name); + ast_verb(3, "%s is ringing\n", ochan_name); break; case AST_CONTROL_OFFHOOK: /* Ignore going off hook */ break; + case AST_CONTROL_CONNECTED_LINE: + if (!update_connectedline) { + ast_verb(3, "Connected line update to %s prevented.\n", inchan_name); + } else if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { + struct ast_party_connected_line connected; + ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name); + ast_party_connected_line_set_init(&connected, &o->connected); + ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected); + ast_party_connected_line_set(&o->connected, &connected); + ast_party_connected_line_free(&connected); + } else { + if (ast_channel_connected_line_macro(o->chan, in, f, 1, 1)) { + ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen); + } + } + break; + case AST_CONTROL_REDIRECTING: + if (!update_connectedline) { + ast_verb(3, "Redirecting update to %s prevented\n", inchan_name); + } else { + ast_verb(3, "%s redirecting info has changed, passing it to %s\n", ochan_name, inchan_name); + ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen); + } + break; default: ast_debug(1, "Dunno what to do with control type %d\n", f->subclass); + break; } } ast_frfree(f); @@ -3545,6 +3831,7 @@ char *p; char vars[2048]; int forwardsallowed = 1; + int update_connectedline = 1; int callcompletedinsl; struct ao2_iterator memi; struct ast_datastore *datastore, *transfer_ds; @@ -3610,6 +3897,9 @@ case 'i': forwardsallowed = 0; break; + case 'I': + update_connectedline = 0; + break; case 'x': ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON); break; @@ -3619,7 +3909,6 @@ case 'C': qe->cancel_answered_elsewhere = 1; break; - } /* if the calling channel has the ANSWERED_ELSEWHERE flag set, make sure this is inherited. @@ -3689,6 +3978,17 @@ } } AST_LIST_UNLOCK(dialed_interfaces); + + ast_channel_lock(qe->chan); + /* If any pre-existing connected line information exists on this + * channel, like from the CONNECTED_LINE dialplan function, use this + * to seed the connected line information. It may, of course, be updated + * during the call + */ + if (qe->chan->connected.id.number) { + ast_party_connected_line_copy(&tmp->connected, &qe->chan->connected); + } + ast_channel_unlock(qe->chan); if (di) { free(tmp); @@ -3720,6 +4020,7 @@ tmp->oldstatus = cur->status; tmp->lastcall = cur->lastcall; tmp->lastqueue = cur->lastqueue; + tmp->update_connectedline = 1; ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface)); /* Special case: If we ring everyone, go ahead and ring them, otherwise just calculate their metric for the appropriate strategy */ @@ -3760,7 +4061,7 @@ ring_one(qe, outgoing, &numbusies); if (use_weight) ao2_unlock(queues); - lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed); + lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed, update_connectedline); /* The ast_channel_datastore_remove() function could fail here if the * datastore was moved to another channel during a masquerade. If this is * the case, don't free the datastore here because later, when the channel @@ -3955,18 +4256,16 @@ else which = peer; ast_channel_unlock(qe->chan); - if (ast_monitor_start) { - if (monitorfilename) { - ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT); - } else if (qe->chan->cdr && ast_monitor_start) { - ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT); - } else if (ast_monitor_start) { - /* Last ditch effort -- no CDR, make up something */ - snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); - ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT); - } + if (monitorfilename) { + ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT); + } else if (qe->chan->cdr) { + ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT); + } else { + /* Last ditch effort -- no CDR, make up something */ + snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); + ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT); } - if (!ast_strlen_zero(monexec) && ast_monitor_setjoinfiles) { + if (!ast_strlen_zero(monexec)) { ast_monitor_setjoinfiles(which, 1); } } else { @@ -4514,7 +4813,7 @@ } /* \brief Sets members penalty, if queuename=NULL we set member penalty in all the queues. */ -static int set_member_penalty(char *queuename, char *interface, int penalty) +static int set_member_penalty(const char *queuename, const char *interface, int penalty) { int foundinterface = 0, foundqueue = 0; struct call_queue *q; @@ -4691,7 +4990,7 @@ } /*! \brief PauseQueueMember application */ -static int pqm_exec(struct ast_channel *chan, void *data) +static int pqm_exec(struct ast_channel *chan, const char *data) { char *parse; AST_DECLARE_APP_ARGS(args, @@ -4727,7 +5026,7 @@ } /*! \brief UnPauseQueueMember application */ -static int upqm_exec(struct ast_channel *chan, void *data) +static int upqm_exec(struct ast_channel *chan, const char *data) { char *parse; AST_DECLARE_APP_ARGS(args, @@ -4763,7 +5062,7 @@ } /*! \brief RemoveQueueMember application */ -static int rqm_exec(struct ast_channel *chan, void *data) +static int rqm_exec(struct ast_channel *chan, const char *data) { int res=-1; char *parse, *temppos = NULL; @@ -4820,7 +5119,7 @@ } /*! \brief AddQueueMember application */ -static int aqm_exec(struct ast_channel *chan, void *data) +static int aqm_exec(struct ast_channel *chan, const char *data) { int res=-1; char *parse, *temppos = NULL; @@ -4883,7 +5182,7 @@ } /*! \brief QueueLog application */ -static int ql_exec(struct ast_channel *chan, void *data) +static int ql_exec(struct ast_channel *chan, const char *data) { char *parse; @@ -4958,7 +5257,7 @@ * 6. Try 4 again unless some condition (such as an expiration time) causes us to * exit the queue. */ -static int queue_exec(struct ast_channel *chan, void *data) +static int queue_exec(struct ast_channel *chan, const char *data) { int res=-1; int ringing=0; @@ -4974,6 +5273,7 @@ int noption = 0; char *parse; int makeannouncement = 0; + int position = 0; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(queuename); AST_APP_ARG(options); @@ -4984,12 +5284,13 @@ AST_APP_ARG(macro); AST_APP_ARG(gosub); AST_APP_ARG(rule); + AST_APP_ARG(position); ); /* Our queue entry */ struct queue_ent qe; if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule]]]]]]]]\n"); + ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule[,position]]]]]]]]]\n"); return -1; } @@ -5055,6 +5356,14 @@ if (args.options && (strchr(args.options, 'c'))) qcontinue = 1; + if (args.position) { + position = atoi(args.position); + if (position < 0) { + ast_log(LOG_WARNING, "Invalid position '%s' given for call to queue '%s'. Assuming no preference for position\n", args.position, args.queuename); + position = 0; + } + } + ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n", args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio); @@ -5067,7 +5376,7 @@ qe.last_periodic_announce_time = time(NULL); qe.last_periodic_announce_sound = 0; qe.valid_digits = 0; - if (join_queue(args.queuename, &qe, &reason)) { + if (join_queue(args.queuename, &qe, &reason, position)) { ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename); set_queue_result(chan, reason); return 0; @@ -5998,7 +6307,7 @@ * List the queues strategy, calls processed, members logged in, * other queue statistics such as avg hold time. */ -static char *__queues_show(struct mansession *s, int fd, int argc, char **argv) +static char *__queues_show(struct mansession *s, int fd, int argc, const char * const *argv) { struct call_queue *q; struct ast_str *out = ast_str_alloca(240); @@ -6173,7 +6482,7 @@ */ static int manager_queues_show(struct mansession *s, const struct message *m) { - char *a[] = { "queue", "show" }; + static const char * const a[] = { "queue", "show" }; __queues_show(s, -1, 2, a); astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */ @@ -6609,7 +6918,7 @@ static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - char *queuename, *interface, *membername = NULL, *state_interface = NULL; + const char *queuename, *interface, *membername = NULL, *state_interface = NULL; int penalty; switch ( cmd ) { @@ -6724,7 +7033,7 @@ static char *handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - char *queuename, *interface; + const char *queuename, *interface; switch (cmd) { case CLI_INIT: @@ -6789,7 +7098,7 @@ static char *handle_queue_pause_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - char *queuename, *interface, *reason; + const char *queuename, *interface, *reason; int paused; switch (cmd) { @@ -6863,7 +7172,7 @@ static char *handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - char *queuename = NULL, *interface; + const char *queuename = NULL, *interface; int penalty = 0; switch (cmd) { @@ -6925,7 +7234,7 @@ static char *handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - char *rule; + const char *rule; struct rule_list *rl_iter; struct penalty_rule *pr_iter; switch (cmd) { @@ -7152,17 +7461,17 @@ res |= ast_register_application_xml(app_pqm, pqm_exec); res |= ast_register_application_xml(app_upqm, upqm_exec); res |= ast_register_application_xml(app_ql, ql_exec); - res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues"); - res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status"); - res |= ast_manager_register("QueueSummary", 0, manager_queues_summary, "Queue Summary"); - res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue."); - res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue."); - res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable"); - res |= ast_manager_register("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom, "Adds custom entry in queue_log"); - res |= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty, "Set the penalty for a queue member"); - res |= ast_manager_register("QueueRule", 0, manager_queue_rule_show, "Queue Rules"); - res |= ast_manager_register("QueueReload", 0, manager_queue_reload, "Reload a queue, queues, or any sub-section of a queue or queues"); - res |= ast_manager_register("QueueReset", 0, manager_queue_reset, "Reset queue statistics"); + res |= ast_manager_register_xml("Queues", 0, manager_queues_show); + res |= ast_manager_register_xml("QueueStatus", 0, manager_queues_status); + res |= ast_manager_register_xml("QueueSummary", 0, manager_queues_summary); + res |= ast_manager_register_xml("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member); + res |= ast_manager_register_xml("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member); + res |= ast_manager_register_xml("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member); + res |= ast_manager_register_xml("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom); + res |= ast_manager_register_xml("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty); + res |= ast_manager_register_xml("QueueRule", 0, manager_queue_rule_show); + res |= ast_manager_register_xml("QueueReload", 0, manager_queue_reload); + res |= ast_manager_register_xml("QueueReset", 0, manager_queue_reset); res |= ast_custom_function_register(&queuevar_function); res |= ast_custom_function_register(&queuemembercount_function); res |= ast_custom_function_register(&queuemembercount_dep); Index: apps/app_followme.c =================================================================== --- a/apps/app_followme.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_followme.c (.../trunk) (revision 202568) @@ -275,18 +275,19 @@ } /*! \brief Add a new number */ -static struct number *create_followme_number(char *number, int timeout, int numorder) +static struct number *create_followme_number(const char *number, int timeout, int numorder) { struct number *cur; + char *buf = ast_strdupa(number); char *tmp; if (!(cur = ast_calloc(1, sizeof(*cur)))) return NULL; cur->timeout = timeout; - if ((tmp = strchr(number, ','))) + if ((tmp = strchr(buf, ','))) *tmp = '\0'; - ast_copy_string(cur->number, number, sizeof(cur->number)); + ast_copy_string(cur->number, buf, sizeof(cur->number)); cur->order = numorder; ast_debug(1, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout); @@ -996,7 +997,7 @@ bconfig->end_bridge_callback_data = originator; } -static int app_exec(struct ast_channel *chan, void *data) +static int app_exec(struct ast_channel *chan, const char *data) { struct fm_args targs = { 0, }; struct ast_bridge_config config; Index: apps/app_waitforring.c =================================================================== --- a/apps/app_waitforring.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_waitforring.c (.../trunk) (revision 202568) @@ -53,7 +53,7 @@ static char *app = "WaitForRing"; -static int waitforring_exec(struct ast_channel *chan, void *data) +static int waitforring_exec(struct ast_channel *chan, const char *data) { struct ast_frame *f; int res = 0; Index: apps/app_flash.c =================================================================== --- a/apps/app_flash.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/apps/app_flash.c (.../trunk) (revision 202568) @@ -72,7 +72,7 @@ return j; } -static int flash_exec(struct ast_channel *chan, void *data) +static int flash_exec(struct ast_channel *chan, const char *data) { int res = -1; int x; Index: doc/asterisk.sgml =================================================================== --- a/doc/asterisk.sgml (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/doc/asterisk.sgml (.../trunk) (revision 202568) @@ -327,7 +327,7 @@ BUGS - Bug reports and feature requests may be filed at http://bugs.digium.com + Bug reports and feature requests may be filed at https://issues.asterisk.org Index: doc/CODING-GUIDELINES =================================================================== --- a/doc/CODING-GUIDELINES (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/doc/CODING-GUIDELINES (.../trunk) (revision 202568) @@ -22,7 +22,7 @@ Asterisk is published under a dual-licensing scheme by Digium. To be accepted into the codebase, all non-trivial changes must be licensed to Digium. For more information, see the electronic license -agreement on http://bugs.digium.com/. +agreement on https://issues.asterisk.org/. Patches should be in the form of a unified (-u) diff, made from a checkout from subversion. @@ -114,7 +114,6 @@ is almost always necessary) write a short comment next to each #include to explain why you need it. - * Declaration of functions and variables ---------------------------------------- @@ -122,15 +121,47 @@ since it is harder to read and not portable to GCC 2.95 and others. - Functions and variables that are not intended to be used outside the module - must be declared static. + must be declared static. If you are compiling on a Linux platform that has the + 'dwarves' package available, you can use the 'pglobal' tool from that package + to check for unintended global variables or functions being exposed in your + object files. Usage is very simple: + $ pglobal -vf + - When reading integer numeric input with scanf (or variants), do _NOT_ use '%i' unless you specifically want to allow non-base-10 input; '%d' is always a better choice, since it will not silently turn numbers with leading zeros into base-8. -- Strings that are coming from input should not be used as a first argument to - a formatted *printf function. +- Strings that are coming from input should not be used as the format argument to + any printf-style function. +* Structure alignment and padding +--------------------------------- + +On many platforms, structure fields (in structures that are not marked 'packed') +will be laid out by the compiler with gaps (padding) between them, in order to +satisfy alignment requirements. As a simple example: + +struct foo { + int bar; + void *xyz; +} + +On nearly every 64-bit platform, this will result in 4 bytes of dead space between +'bar' and 'xyz', because pointers on 64-bit platforms must be aligned on 8-byte +boundaries. Once you have your code written and tested, it may be worthwhile to review +your structure definitions to look for problems of this nature. If you are on a Linux +platform with the 'dwarves' package available, the 'pahole' tool from that package +can be used to both check for padding issues of this type and also propose reorganized +structure definitions to eliminate it. Usage is quite simple; for a structure named 'foo', +the command would look something like this: + +$ pahole --reorganize --show_reorg_steps -C foo + +The 'pahole' tool has many other modes available, including some that will list all the +structures declared in the module and the amount of padding in each one that could possibly +be recovered. + * Use the internal API ---------------------- Index: doc/asterisk.8 =================================================================== --- a/doc/asterisk.8 (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/doc/asterisk.8 (.../trunk) (revision 202568) @@ -176,7 +176,7 @@ \fBasterisk -rx "core show channels"\fR - Display channels on running server .SH "BUGS" .PP -Bug reports and feature requests may be filed at http://bugs.digium.com +Bug reports and feature requests may be filed at https://issues.asterisk.org .SH "SEE ALSO" .PP *CLI> \fBhelp\fR - Help on Asterisk CLI Index: doc/backtrace.txt =================================================================== --- a/doc/backtrace.txt (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/doc/backtrace.txt (.../trunk) (revision 202568) @@ -1,6 +1,6 @@ This document is intended to provide information on how to obtain the backtraces required on the asterisk bug tracker, available at -http://bugs.digium.com. The information is required by developers to +https://issues.asterisk.org. The information is required by developers to help fix problem with bugs of any kind. Backtraces provide information about what was wrong when a program crashed; in our case, Asterisk. There are two kind of backtraces (aka 'bt') which are Index: doc/janitor-projects.txt =================================================================== --- a/doc/janitor-projects.txt (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/doc/janitor-projects.txt (.../trunk) (revision 202568) @@ -32,3 +32,5 @@ -- Audit all channel/res/app/etc. modules to ensure that they do not register any entrypoints with the Asterisk core until after they are ready to service requests; all config file reading/processing, structure allocation, etc. must be completed before Asterisk is made aware of any services the module offers. -- Ensure that Realtime-enabled modules do not depend on the order of columns returned by the database lookup (example: outboundproxy and host settings in chan_sip). + + -- Convert all usage of the signal(2) system API to the more portable sigaction(2) system API. Index: doc/lang/urdu.ods =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/x-open-document-spreadsheet Property changes on: doc/lang/urdu.ods ___________________________________________________________________ Added: svn:mime-type + application/x-open-document-spreadsheet Index: doc/appdocsxml.dtd =================================================================== --- a/doc/appdocsxml.dtd (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/doc/appdocsxml.dtd (.../trunk) (revision 202568) @@ -1,4 +1,4 @@ - + @@ -23,6 +23,10 @@ + + + + Index: doc/datastores.txt =================================================================== --- a/doc/datastores.txt (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/doc/datastores.txt (.../trunk) (revision 202568) @@ -19,8 +19,8 @@ * How do you create a data store? -1. Use ast_channel_datastore_alloc function to return a pre-allocated structure - Ex: datastore = ast_channel_datastore_alloc(&example_datastore, "uid"); +1. Use ast_datastore_alloc function to return a pre-allocated structure + Ex: datastore = ast_datastore_alloc(&example_datastore, "uid"); This function takes two arguments: (datastore info structure, uid) 2. Attach data to pre-allocated structure. Ex: datastore->data = mysillydata; @@ -36,7 +36,7 @@ } struct ast_datastore *datastore = NULL; -datastore = ast_channel_datastore_alloc(&example_datastore, NULL); +datastore = ast_datastore_alloc(&example_datastore, NULL); datastore->data = mysillydata; ast_channel_datastore_add(chan, datastore); Index: doc/manager_1_1.txt =================================================================== --- a/doc/manager_1_1.txt (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/doc/manager_1_1.txt (.../trunk) (revision 202568) @@ -252,6 +252,35 @@ To list the information about a specific SKINNY line. Variables: Line: Line to show information about. + +- Action: CoreSettings + Modules: manager.c + Purpose: To report core settings, like AMI and Asterisk version, + maxcalls and maxload settings. + * Integrated in SVN trunk as of May 4th, 2007 + Example: + Response: Success + ActionID: 1681692777 + AMIversion: 1.1 + AsteriskVersion: SVN-oej-moremanager-r61756M + SystemName: EDVINA-node-a + CoreMaxCalls: 120 + CoreMaxLoadAvg: 0.000000 + CoreRunUser: edvina + CoreRunGroup: edvina + +- Action: CoreStatus + Modules: manager.c + Purpose: To report current PBX core status flags, like + number of concurrent calls, startup and reload time. + * Integrated in SVN trunk as of May 4th, 2007 + Example: + Response: Success + ActionID: 1649760492 + CoreStartupTime: 22:35:17 + CoreReloadTime: 22:35:17 + CoreCurrentCalls: 20 + * NEW EVENTS ------------ @@ -294,34 +323,6 @@ SIPcallid: NTQzYWFiOWM4NmE0MWRkZjExMzU2YzQ3OWQwNzg3ZmI. SIPfullcontact: sip:olle@127.0.0.1:49054 -- Action: CoreSettings - Modules: manager.c - Purpose: To report core settings, like AMI and Asterisk version, - maxcalls and maxload settings. - * Integrated in SVN trunk as of May 4th, 2007 - Example: - Response: Success - ActionID: 1681692777 - AMIversion: 1.1 - AsteriskVersion: SVN-oej-moremanager-r61756M - SystemName: EDVINA-node-a - CoreMaxCalls: 120 - CoreMaxLoadAvg: 0.000000 - CoreRunUser: edvina - CoreRunGroup: edvina - -- Action: CoreStatus - Modules: manager.c - Purpose: To report current PBX core status flags, like - number of concurrent calls, startup and reload time. - * Integrated in SVN trunk as of May 4th, 2007 - Example: - Response: Success - ActionID: 1649760492 - CoreStartupTime: 22:35:17 - CoreReloadTime: 22:35:17 - CoreCurrentCalls: 20 - - Event: NewAccountCode Modules: cdr.c Purpose: To report a change in account code for a live channel Index: doc/tex/asterisk.tex =================================================================== --- a/doc/tex/asterisk.tex (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/doc/tex/asterisk.tex (.../trunk) (revision 202568) @@ -135,6 +135,9 @@ \chapter{Phone Provisioning} \input{phoneprov.tex} +\chapter{Calendaring} + \input{calendaring.tex} + \chapter{Development} \section{Backtrace} \input{backtrace.tex} Index: doc/tex/channelvariables.tex =================================================================== --- a/doc/tex/channelvariables.tex (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/doc/tex/channelvariables.tex (.../trunk) (revision 202568) @@ -853,6 +853,10 @@ ${QUEUE_MIN_PENALTY} Minimum member penalty allowed to answer caller ${QUEUESTATUS} Status of the call, one of: (TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL) +${QUEUEPOSITION} * When a caller is removed from a queue, his current position is logged + in this variable. If the value is 0, then this means that the caller was + serviced by a queue member. If non-zero, then this was the position in the + queue the caller was in when he left. ${RECORDED_FILE} * Recorded file in record() ${TALK_DETECTED} * Result from talkdetect() ${TOUCH_MONITOR} The filename base to use with Touch Monitor (auto record) @@ -925,7 +929,9 @@ ${SIPFROMDOMAIN} Set SIP domain on outbound calls ${SIPUSERAGENT} * SIP user agent (deprecated) ${SIPURI} * SIP uri -${SIP_CODEC} Set the SIP codec for a call +${SIP_CODEC} Set the SIP codec for an inbound call +${SIP_CODEC_INBOUND} Set the SIP codec for an inbound call +${SIP_CODEC_OUTBOUND} Set the SIP codec for an outbound call ${SIP_URI_OPTIONS} * additional options to add to the URI for an outgoing call ${RTPAUDIOQOS} RTCP QoS report for the audio of this call ${RTPVIDEOQOS} RTCP QoS report for the video of this call @@ -999,3 +1005,11 @@ ${OSPOUTTIMELIMIT} Duration limit for out_bound call ${OSPRESULTS} Number of remained destinations \end{verbatim} + +\subsection{Connected line digit manipulation} +\begin{verbatim} +${CONNECTED_LINE_SEND_CALLEE_MACRO} Macro to call before sending a connected line update to the callee +${CONNECTED_LINE_SEND_CALLEE_MACRO_ARGS} Arguments to pass to ${CONNECTED_LINE_SEND_CALLEE_MACRO} +${CONNECTED_LINE_SEND_CALLER_MACRO} Macro to call before sending a connected line update to the caller +${CONNECTED_LINE_SEND_CALLER_MACRO_ARGS} Arguments to pass to ${CONNECTED_LINE_SEND_CALLER_MACRO} +\end{verbatim} Index: doc/tex/calendaring.tex =================================================================== --- a/doc/tex/calendaring.tex (.../tags/1.6.2.0-beta3) (revision 0) +++ b/doc/tex/calendaring.tex (.../trunk) (revision 202568) @@ -0,0 +1,206 @@ +\section{Introduction} + +The Asterisk Calendaring API aims to be a generic interface for integrating +Asterisk with various calendaring technologies. The goal is to be able to +support reading and writing of calendar events as well as allowing notification +of pending events through the Asterisk dialplan. + +There are three calendaring modules that ship with Asterisk that provide support +for iCalendar, CalDAV, and Microsoft Exchange Server calendars. All three +modules support event notification. Both CalDAV and Exchange support reading +and writing calendars, while iCalendar is a read-only format. + +\section{Configuring Asterisk Calendaring} + +All asterisk calendaring modules are configured through calender.conf. Each +calendar module can define its own set of required parameters in addition to the +parameters available to all calendar types. An effort has been made to keep all +options the same in all calendaring modules, but some options will diverge over +time as features are added to each module. + +An example calendar.conf might look like: +\begin{astlisting} +\begin{verbatim} +[calendar_joe] +type = ical +url = https://example.com/home/jdoe/Calendar +user = jdoe +secret = mysecret +refresh = 15 +timeframe = 600 +autoreminder = 10 +channel = SIP/joe +context = calendar_event_notify +extension = s +waittime = 30 +\end{verbatim} +\end{astlisting} + +\subsection{Module-independent settings} + +The settings related to calendar event notification are handled by the core +calendaring API. These settings are: +\begin{description} +\item[autoreminder] This allows the overriding of any alarms that may or may not +be set for a calendar event. It is specified in minutes. +\item[refresh] How often to refresh the calendar data; specified in minutes. +\item[timeframe] How far into the future each calendar refresh should look. This +is the amount of data that will be visible to queries from the dialplan. This +setting should always be greater than or equal to the refresh setting or events +may be missed. It is specified in minutes. +\item[channel] The channel that should be used for making the notification +attempt. +\item[waittime] How long to wait, in seconds, for the channel to answer a notification +attempt. +\end{description} + +There are two ways to specify how to handle a notification. One option is +providing a context and extension, while the other is providing an application +and the arguments to that application. One (and only one) of these options +should be provided. + +\begin{description} +\item[context] The context of the extension to connect to the notification +channel +\item[extension] The extension to connect to the notification. Note that the +priority will always be 1. +\end{description} + +or + +\begin{description} +\item[app] The dialplan application to execute upon the answer of a notification +\item[appdata] The data to pass to the notification dialplan application +\end{description} + +\subsection{Module-dependent settings} + +Connection-related options are specific to each module. Currently, all modules +take a url, user, and secret for configuration and no other module-specific +settings have been implemented. At this time, no support for HTTP redirects has +been implemented, so it is important to specify the correct URL--paying attention +to any trailing slashes that may be necessary. + +\section{Dialplan functions} +\subsection{Read functions} + +The simplest dialplan query is the CALENDAR\_BUSY query. It takes a single +option, the name of the calendar defined, and returns "1" for busy (including +tentatively busy) and "0" for not busy. + +For more information about a calendar event, a combination of CALENDAR\_QUERY +and CALENDAR\_QUERY\_RESULT is used. CALENDAR\_QUERY takes the calendar name +and optionally a start and end time in "unix time" (seconds from unix epoch). It +returns an id that can be passed to CALENDAR\_QUERY\_RESULT along with a field +name to return the data in that field. If multiple events are returned in the +query, the number of the event in the list can be specified as well. The available +fields to return are: +\begin{description} +\item[summary] A short summary of the event +\item[description] The full description of the event +\item[organizer] Who organized the event +\item[location] Where the event is located +\item[calendar] The name of the calendar from calendar.conf +\item[uid] The unique identifier associated with the event +\item[start] The start of the event in seconds since Unix epoch +\item[end] The end of the event in seconds since Unix epoch +\item[busystate] The busy state 0=Free, 1=Tentative, 2=Busy +\item[attendees] A comma separated list of attendees as stored in the event and +may include prefixes such as "mailto:". +\end{description} + +When an event notification is sent to the dial plan, the CALENDAR\_EVENT +function may be used to return the information about the event that is causing +the notification. The fields that can be returned are the same as those from +CALENDAR\_QUERY\_RESULT. +\subsection{Write functions} + +To write an event to a calendar, the CALENDAR\_WRITE function is used. This +function takes a calendar name and also uses the same fields as +CALENDAR\_QUERY\_RESULT. As a write function, it takes a set of comma-separated +values that are in the same order as the specified fields. For example: +\begin{astlisting} +\begin{verbatim} +CALENDAR_WRITE(mycalendar,summary,organizer,start,end,busystate)= + "My event","mailto:jdoe@example.com",228383580,228383640,1) +\end{verbatim} +\end{astlisting} + +\section{Dialplan Examples} +\subsection{Office hours} +A common business PBX scenario is would be executing dialplan logic based on +when the business is open and the phones staffed. If the business is closed for +holidays, it is sometimes desirable to play a message to the caller stating why +the business is closed. + +The standard way to do this in asterisk has been doing a series of GotoIfTime +statements or time-based include statements. Either way can be tedious and +requires someone with access to edit asterisk config files. + +With calendaring, the adminstrator only needs to set up a calendar that contains +the various holidays or even recurring events specifying the office hours. A +custom greeting filename could even be contained in the description field for +playback. For example: +\begin{astlisting} +\begin{verbatim} +[incoming] +exten => 5555551212,1,Answer +exten => 5555551212,n,GotoIf(${CALENDAR_BUSY(officehours)}?closed:attendant,s,1) +exten => 5555551212,n(closed),Set(id=${CALENDAR_QUERY(office,${EPOCH},${EPOCH})}) +exten => 5555551212,n,Set(soundfile=${CALENDAR_QUERY_RESULT(${id},description)}) +exten => 5555551212,n,Playback($[${ISNULL(soundfile)} ? generic-closed :: ${soundfile}]) +exten => 5555551212,n,Hangup +\end{verbatim} +\end{astlisting} + +\subsection{Meeting reminders} +One useful application of Asterisk Calendaring is the ability to execute +dialplan logic based on an event notification. Most calendaring technologies +allow a user to set an alarm for an event. If these alarms are set on a calendar +that Asterisk is monitoring and the calendar is set up for event notification +via calendar.conf, then Asterisk will execute notify the specified channel at +the time of the alarm. If an overrided notification time is set with the +autoreminder setting, then the notification would happen at that time instead. + +The following example demonstrates the set up for a simple event notification +that plays back a generic message followed by the time of the upcoming meeting. +calendar.conf. +\begin{astlisting} +\begin{verbatim} +[calendar_joe] +type = ical +url = https://example.com/home/jdoe/Calendar +user = jdoe +secret = mysecret +refresh = 15 +timeframe = 600 +autoreminder = 10 +channel = SIP/joe +context = calendar_event_notify +extension = s +waittime = 30 +\end{verbatim} +\end{astlisting} + +\begin{astlisting} +\begin{verbatim} +[calendar_event_notify] +exten => s,1,Answer +exten => s,n,Playback(you-have-a-meeting-at) +exten => s,n,SayUnixTime(${CALENDAR_EVENT(start)}) +exten => s,n,Hangup +\end{verbatim} +\end{astlisting} + +\subsection{Writing an event} +Both CalDAV and Exchange calendar servers support creating new events. The +following example demonstrates writing a log of a call to a calendar. +\begin{astlisting} +\begin{verbatim} +[incoming] +exten => 6000,1,Set(start=${EPOCH}) +exten => 6000,n,Dial(SIP/joe) +exten => h,1,Set(end=${EPOCH}) +exten => h,n,Set(CALENDAR_WRITE(calendar_joe,summary,start,end)=Call from ${CALLERID(all)},${start},${end}) +\end{verbatim} +\end{astlisting} Property changes on: doc/tex/calendaring.tex ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + Author Date Id Revision Index: doc/tex/misdn.tex =================================================================== --- a/doc/tex/misdn.tex (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/doc/tex/misdn.tex (.../trunk) (revision 202568) @@ -247,7 +247,7 @@ an '*', the rest is clear I think. Please take a trace of the problem and open a report in the Asterisk issue -tracker at \url{http://bugs.digium.com} in the "channel drivers" project, +tracker at \url{https://issues.asterisk.org} in the "channel drivers" project, "chan\_misdn" category. Read the bug guidelines to make sure you provide all the information needed. Index: doc/tex/backtrace.tex =================================================================== --- a/doc/tex/backtrace.tex (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/doc/tex/backtrace.tex (.../trunk) (revision 202568) @@ -1,6 +1,6 @@ This document is intended to provide information on how to obtain the backtraces required on the asterisk bug tracker, available at -\url{http://bugs.digium.com}. The information is required by developers to +\url{https://issues.asterisk.org}. The information is required by developers to help fix problem with bugs of any kind. Backtraces provide information about what was wrong when a program crashed; in our case, Asterisk. There are two kind of backtraces (aka 'bt') which are Index: doc/tex/mp3.tex =================================================================== --- a/doc/tex/mp3.tex (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/doc/tex/mp3.tex (.../trunk) (revision 202568) @@ -7,5 +7,5 @@ However, if you still need to use mp3 as your music on hold format, a format driver for reading MP3 audio files is available in the asterisk-addons SVN repository on svn.digium.com or in the asterisk-addons release at -\url{http://downloads.digium.com/pub/telephony/asterisk/}. +\url{http://downloads.asterisk.org/pub/telephony/asterisk/}. Index: UPGRADE.txt =================================================================== --- a/UPGRADE.txt (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/UPGRADE.txt (.../trunk) (revision 202568) @@ -18,6 +18,17 @@ === =========================================================== +From 1.6.2 to 1.6.3: + +* The usage of RTP inside of Asterisk has now become modularized. This means + the Asterisk RTP stack now exists as a loadable module, res_rtp_asterisk. + If you are not using autoload=yes in modules.conf you will need to ensure + it is set to load. If not, then any module which uses RTP (such as chan_sip) + will not be able to send or receive calls. +* The app_dahdiscan.c file has been removed, but the dialplan app DAHDIScan still + remains. It now exists within app_chanspy.c and retains the exact same + functionality as before. + From 1.6.1 to 1.6.2: * The res_indications module has been removed. Its functionality was important Index: CHANGES =================================================================== --- a/CHANGES (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/CHANGES (.../trunk) (revision 202568) @@ -7,7 +7,170 @@ === and the other UPGRADE files for older releases. === ====================================================================== +------------------------------------------------------------------------------ +--- Functionality changes from Asterisk 1.6.2 to Asterisk 1.6.3 ------------- +------------------------------------------------------------------------------ +SIP Changes +----------- + * Added preferred_codec_only option in sip.conf. This feature limits the joint + codecs sent in response to an INVITE to the single most preferred codec. + * Added SIP_CODEC_OUTBOUND dialplan variable which can be used to set the codec + to be used for the outgoing call. It must be one of the codecs configured + for the device. + * Added tlsprivatekey option to sip.conf. This allows a separate .pem file + to be used for holding a private key. If tlsprivatekey is not specified, + tlscertfile is searched for both public and private key. + * Added tlsclientmethod option to sip.conf. This allows the protocol for + outbound client connections to be specified. + * The sendrpid parameter has been expanded to include the options + 'rpid' and 'pai'. Setting sendrpid to 'rpid' will cause Remote-Party-ID + header to be sent (equivalent to setting sendrpid=yes) and setting + sendrpid to 'pai' will cause P-Asserted-Identity header to be sent. + * The 'ignoresdpversion' behavior has been made automatic when the SDP received + is in response to a T.38 re-INVITE that Asterisk initiated. In this situation, + since the call will fail if Asterisk does not process the incoming SDP, Asterisk + will accept the SDP even if the SDP version number is not properly incremented, + but will generate a warning in the log indicating that the SIP peer that sent + the SDP should have the 'ignoresdpversion' option set. + +IAX2 Changes +----------- + * Added rtsavesysname option into iax.conf to allow the systname to be saved + on realtime updates. + +Applications +------------ + * Added progress option to the app_dial D() option. When progress DTMF is + present, those values are sent immediatly upon receiving a PROGRESS message + regardless if the call has been answered or not. + * Added functionality to the app_dial F() option to continue with execution + at the current location when no parameters are provided. + * Added c() option to app_chanspy. This option allows custom DTMF to be set + to cycle through the next avaliable channel. By default this is still '*'. + * Added x() option to app_chanspy. This option allows DTMF to be set to + exit the application. + +Dialplan Functions +------------------ + * Added new dialplan functions CONNECTEDLINE and REDIRECTING which permits + setting various connected line and redirecting party information. + * The CHANNEL() function now supports the "name" option. + * For DAHDI channels, the CHANNEL() dialplan function now + supports changing the channel's buffer policy (for the current + call only), using this syntax: + + exten => s,n,Set(CHANNEL(buffers)=6,full) + + This would change the channel to the 'full' buffer policy and + 6 (six) buffers. Possible options for this setting are the same + as those in chan_dahdi.conf. + * For DAHDI channels, the CHANNEL() dialplan function now allows + the dialplan to request changes in the configuration of the active + echo canceller on the channel (if any), for the current call only. + The syntax is: + + exten => s,n,Set(CHANNEL(echocan_mode)=off) + + The possible values are: + + on - normal mode (the echo canceller is actually reinitalized) + off - disabled + fax - FAX/data mode (NLP disabled if possible, otherwise completely + disabled) + voice - voice mode (returns from FAX mode, reverting the changes that + were made when FAX mode was requested) + +Queue changes +------------- + * A new option, 'I' has been added to both app_queue and app_dial. + By setting this option, Asterisk will not update the caller with + connected line changes or redirecting party changes when they occur. + +mISDN channel driver (chan_misdn) changes +---------------------------------------- + * Added display_connected parameter to misdn.conf to put a display string + in the CONNECT message containing the connected name and/or number if + the presentation setting permits it. + * Added display_setup parameter to misdn.conf to put a display string + in the SETUP message containing the caller name and/or number if the + presentation setting permits it. + * Made misdn.conf parameters localdialplan and cpndialplan take a -1 to + indicate the dialplan settings are to be obtained from the asterisk + channel. + * Made misdn.conf parameter callerid accept the "name" format + used by the rest of the system. + * Made use the nationalprefix and internationalprefix misdn.conf + parameters to prefix any received number from the ISDN link if that + number has the corresponding Type-Of-Number. + * Added the following new parameters: unknownprefix, netspecificprefix, + subscriberprefix, and abbreviatedprefix in misdn.conf to prefix any + received number from the ISDN link if that number has the corresponding + Type-Of-Number. + * Added new dialplan application misdn_command which permits controlling + the CCBS/CCNR functionality. + * Added new dialplan function mISDN_CC which permits retrieval of various + values from an active call completion record. + * For PTP, you should manually send the COLR of the redirected-to party + for an incomming redirected call if the incoming call could experience + further redirects. Just set the REDIRECTING(to-num,i) = ${EXTEN} and + set the REDIRECTING(to-pres) to the COLR. A call has been redirected + if the REDIRECTING(from-num) is not empty. + * For outgoing PTP redirected calls, you now need to use the inhibit(i) + option on all of the REDIRECTING statements before dialing the + redirected-to party. You still have to set the REDIRECTING(to-xxx,i) + and the REDIRECTING(from-xxx,i) values. The PTP call will update the + redirecting-to presentation (COLR) when it becomes available. + * Added outgoing_colp parameter to misdn.conf to filter outgoing COLP + information. + +thirdparty mISDN enhancements +----------------------------- +mISDN has been modified by Digium, Inc. to greatly expand facility message +support to allow: + * Enhanced COLP support for call diversion and transfer. + * CCBS/CCNR support. + +The latest modified mISDN v1.1.x based version is available at: +http://svn.digium.com/svn/thirdparty/mISDN/trunk +http://svn.digium.com/svn/thirdparty/mISDNuser/trunk + +Tagged versions of the modified mISDN code are available under: +http://svn.digium.com/svn/thirdparty/mISDN/tags +http://svn.digium.com/svn/thirdparty/mISDNuser/tags + +Asterisk Manager Interface +-------------------------- + * The Hangup action now accepts a Cause header which may be used to + set the channel's hangup cause. + * sslprivatekey option added to manager.conf and http.conf. Adds the ability + to specify a separate .pem file to hold a private key. By default sslcert + is used to hold both the public and private key. + * Options in manager.conf and http.conf with the 'ssl' prefix have been replaced + for options containing the 'tls' prefix. For example, 'sslenable' is now + 'tlsenable'. This has been done in effort to keep ssl and tls options consistent + across all .conf files. All affected sample.conf files have been modified to + reflect this change. Previous options such as 'sslenable' still work, + but options with the 'tls' prefix are preferred. + +Logger +------ + * The rarely used 'event_log' and LOG_EVENT channel have been removed; the few + users of this channel in the tree have been converted to LOG_NOTICE or removed + (in cases where the same message was already generated to another channel). + +CDR +--- + * Multiple files and formats can now be specified in cdr_custom.conf. + +Calendaring for Asterisk +------------------------ + * A new set of modules were added supporing calendar integration with Asterisk. + Dialplan functions for reading from and writing to calendars are included, + as well as the ability to execute dialplan logic upon calendar event notifications. + iCalendar, CalDAV, and Exchange Server calendars are supported (Exchange support + only tested on Exchange Server 2003 with no support for forms-based authentication). + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 1.6.1 to Asterisk 1.6.2 ------------- ------------------------------------------------------------------------------ @@ -77,6 +240,7 @@ and a 'full' buffer policy for a fax transmission, add: faxbuffers=>6,full The faxbuffers configuration will be in affect until the call is torn down. + * Added service message support for 4ESS/5ESS switches. Dialplan Functions ------------------ Index: sounds/Makefile =================================================================== --- a/sounds/Makefile (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/sounds/Makefile (.../trunk) (revision 202568) @@ -19,7 +19,7 @@ MOH_DIR:=$(DESTDIR)$(ASTDATADIR)/moh CORE_SOUNDS_VERSION:=1.4.15 EXTRA_SOUNDS_VERSION:=1.4.9 -SOUNDS_URL:=http://downloads.digium.com/pub/telephony/sounds/releases +SOUNDS_URL:=http://downloads.asterisk.org/pub/telephony/sounds/releases MCS:=$(subst -EN-,-en-,$(MENUSELECT_CORE_SOUNDS)) MCS:=$(subst -FR-,-fr-,$(MCS)) MCS:=$(subst -ES-,-es-,$(MCS)) Index: Makefile =================================================================== --- a/Makefile (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/Makefile (.../trunk) (revision 202568) @@ -307,7 +307,7 @@ ifneq ($(findstring darwin,$(OSARCH)),) ASTCFLAGS+=-D__Darwin__ - SOLINK=-dynamic -bundle -undefined suppress -force_flat_namespace + SOLINK=-bundle -undefined suppress -force_flat_namespace else # These are used for all but Darwin SOLINK=-shared @@ -327,6 +327,10 @@ # comment to print directories during submakes #PRINT_DIR=yes +ifneq ($(INSIDE_EMACS),) +PRINT_DIR=yes +endif + SILENTMAKE:=$(MAKE) --quiet --no-print-directory ifneq ($(PRINT_DIR)$(NOISY_BUILD),) SUBMAKE:=$(MAKE) @@ -371,24 +375,24 @@ menuselect/menuselect --check-deps menuselect.makeopts $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) $(MOD_SUBDIRS_EMBED_LDSCRIPT): - @echo "EMBED_LDSCRIPTS+="`$(SILENTMAKE) -C $(@:-embed-ldscript=) SUBDIR=$(@:-embed-ldscript=) __embed_ldscript` >> makeopts.embed_rules + +@echo "EMBED_LDSCRIPTS+="`$(SILENTMAKE) -C $(@:-embed-ldscript=) SUBDIR=$(@:-embed-ldscript=) __embed_ldscript` >> makeopts.embed_rules $(MOD_SUBDIRS_EMBED_LDFLAGS): - @echo "EMBED_LDFLAGS+="`$(SILENTMAKE) -C $(@:-embed-ldflags=) SUBDIR=$(@:-embed-ldflags=) __embed_ldflags` >> makeopts.embed_rules + +@echo "EMBED_LDFLAGS+="`$(SILENTMAKE) -C $(@:-embed-ldflags=) SUBDIR=$(@:-embed-ldflags=) __embed_ldflags` >> makeopts.embed_rules $(MOD_SUBDIRS_EMBED_LIBS): - @echo "EMBED_LIBS+="`$(SILENTMAKE) -C $(@:-embed-libs=) SUBDIR=$(@:-embed-libs=) __embed_libs` >> makeopts.embed_rules + +@echo "EMBED_LIBS+="`$(SILENTMAKE) -C $(@:-embed-libs=) SUBDIR=$(@:-embed-libs=) __embed_libs` >> makeopts.embed_rules $(MOD_SUBDIRS_MENUSELECT_TREE): - @$(SUBMAKE) -C $(@:-menuselect-tree=) SUBDIR=$(@:-menuselect-tree=) moduleinfo - @$(SUBMAKE) -C $(@:-menuselect-tree=) SUBDIR=$(@:-menuselect-tree=) makeopts + +@$(SUBMAKE) -C $(@:-menuselect-tree=) SUBDIR=$(@:-menuselect-tree=) moduleinfo + +@$(SUBMAKE) -C $(@:-menuselect-tree=) SUBDIR=$(@:-menuselect-tree=) makeopts makeopts.embed_rules: menuselect.makeopts @echo "Generating embedded module rules ..." @rm -f $@ - @$(SUBMAKE) $(MOD_SUBDIRS_EMBED_LDSCRIPT) - @$(SUBMAKE) $(MOD_SUBDIRS_EMBED_LDFLAGS) - @$(SUBMAKE) $(MOD_SUBDIRS_EMBED_LIBS) + +@$(SUBMAKE) $(MOD_SUBDIRS_EMBED_LDSCRIPT) + +@$(SUBMAKE) $(MOD_SUBDIRS_EMBED_LDFLAGS) + +@$(SUBMAKE) $(MOD_SUBDIRS_EMBED_LIBS) $(SUBDIRS): main/version.c include/asterisk/version.h include/asterisk/build.h include/asterisk/buildopts.h defaults.h makeopts.embed_rules @@ -411,10 +415,10 @@ endif $(MOD_SUBDIRS): - @ASTCFLAGS="$(MOD_SUBDIR_CFLAGS) $(ASTCFLAGS)" ASTLDFLAGS="$(ASTLDFLAGS)" $(SUBMAKE) --no-builtin-rules -C $@ SUBDIR=$@ all + +@ASTCFLAGS="$(MOD_SUBDIR_CFLAGS) $(ASTCFLAGS)" ASTLDFLAGS="$(ASTLDFLAGS)" $(SUBMAKE) --no-builtin-rules -C $@ SUBDIR=$@ all $(OTHER_SUBDIRS): - @ASTCFLAGS="$(OTHER_SUBDIR_CFLAGS) $(ASTCFLAGS)" ASTLDFLAGS="$(ASTLDFLAGS)" $(SUBMAKE) --no-builtin-rules -C $@ SUBDIR=$@ all + +@ASTCFLAGS="$(OTHER_SUBDIR_CFLAGS) $(ASTCFLAGS)" ASTLDFLAGS="$(ASTLDFLAGS)" $(SUBMAKE) --no-builtin-rules -C $@ SUBDIR=$@ all defaults.h: makeopts @build_tools/make_defaults_h > $@.tmp @@ -442,10 +446,10 @@ @rm -f $@.tmp $(SUBDIRS_CLEAN): - @$(SUBMAKE) -C $(@:-clean=) clean + +@$(SUBMAKE) -C $(@:-clean=) clean $(SUBDIRS_DIST_CLEAN): - @$(SUBMAKE) -C $(@:-dist-clean=) dist-clean + +@$(SUBMAKE) -C $(@:-dist-clean=) dist-clean clean: $(SUBDIRS_CLEAN) _clean @@ -559,8 +563,10 @@ chmod 755 $(DESTDIR)$(ASTSBINDIR)/safe_asterisk;\ fi $(INSTALL) -d $(DESTDIR)$(ASTHEADERDIR) + $(INSTALL) -d $(DESTDIR)$(ASTHEADERDIR)/doxygen $(INSTALL) -m 644 include/asterisk.h $(DESTDIR)$(includedir) $(INSTALL) -m 644 include/asterisk/*.h $(DESTDIR)$(ASTHEADERDIR) + $(INSTALL) -m 644 include/asterisk/doxygen/*.h $(DESTDIR)$(ASTHEADERDIR)/doxygen if [ -n "$(OLDHEADERS)" ]; then \ rm -f $(addprefix $(DESTDIR)$(ASTHEADERDIR)/,$(OLDHEADERS)) ;\ fi @@ -918,28 +924,28 @@ MAKE_MENUSELECT=CC="$(HOST_CC)" CXX="$(CXX)" LD="" AR="" RANLIB="" CFLAGS="" $(MAKE) -C menuselect CONFIGURE_SILENT="--silent" menuselect/menuselect: menuselect/makeopts - $(MAKE_MENUSELECT) menuselect + +$(MAKE_MENUSELECT) menuselect menuselect/cmenuselect: menuselect/makeopts - $(MAKE_MENUSELECT) cmenuselect + +$(MAKE_MENUSELECT) cmenuselect menuselect/gmenuselect: menuselect/makeopts - $(MAKE_MENUSELECT) gmenuselect + +$(MAKE_MENUSELECT) gmenuselect menuselect/nmenuselect: menuselect/makeopts - $(MAKE_MENUSELECT) nmenuselect + +$(MAKE_MENUSELECT) nmenuselect menuselect/makeopts: makeopts - $(MAKE_MENUSELECT) makeopts + +$(MAKE_MENUSELECT) makeopts menuselect-tree: $(foreach dir,$(filter-out main,$(MOD_SUBDIRS)),$(wildcard $(dir)/*.c) $(wildcard $(dir)/*.cc)) build_tools/cflags.xml build_tools/cflags-devmode.xml sounds/sounds.xml build_tools/embed_modules.xml configure @echo "Generating input for menuselect ..." @echo "" > $@ @echo >> $@ @echo "" >> $@ - @for dir in $(sort $(filter-out main,$(MOD_SUBDIRS))); do $(SILENTMAKE) -C $${dir} SUBDIR=$${dir} moduleinfo >> $@; done + +@for dir in $(sort $(filter-out main,$(MOD_SUBDIRS))); do $(SILENTMAKE) -C $${dir} SUBDIR=$${dir} moduleinfo >> $@; done @cat build_tools/cflags.xml >> $@ - @for dir in $(sort $(filter-out main,$(MOD_SUBDIRS))); do $(SILENTMAKE) -C $${dir} SUBDIR=$${dir} makeopts >> $@; done + +@for dir in $(sort $(filter-out main,$(MOD_SUBDIRS))); do $(SILENTMAKE) -C $${dir} SUBDIR=$${dir} makeopts >> $@; done @if [ "${AST_DEVMODE}" = "yes" ]; then \ cat build_tools/cflags-devmode.xml >> $@; \ fi Index: autoconf/ast_gcc_attribute.m4 =================================================================== --- a/autoconf/ast_gcc_attribute.m4 (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/autoconf/ast_gcc_attribute.m4 (.../trunk) (revision 202568) @@ -1,16 +1,16 @@ # Helper function to check for gcc attributes. -# AST_GCC_ATTRIBUTE([attribute name], [attribute syntax]) +# AST_GCC_ATTRIBUTE([attribute name], [attribute syntax], [attribute scope]) AC_DEFUN([AST_GCC_ATTRIBUTE], [ AC_MSG_CHECKING(for compiler 'attribute $1' support) saved_CFLAGS="$CFLAGS" -CFLAGS="$CFLAGS -Werror" +CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" if test "x$2" = "x" then AC_COMPILE_IFELSE( - AC_LANG_PROGRAM([void __attribute__(($1)) *test(void *muffin, ...) {}], + AC_LANG_PROGRAM([$3 void __attribute__(($1)) *test(void *muffin, ...) {return (void *) 0;}], []), AC_MSG_RESULT(yes) AC_DEFINE_UNQUOTED([HAVE_ATTRIBUTE_$1], 1, [Define to 1 if your GCC C compiler supports the '$1' attribute.]), @@ -18,7 +18,7 @@ ) else AC_COMPILE_IFELSE( - AC_LANG_PROGRAM([void __attribute__(($2)) *test(void *muffin, ...) {}], + AC_LANG_PROGRAM([$3 void __attribute__(($2)) *test(void *muffin, ...) {return (void *) 0;}], []), AC_MSG_RESULT(yes) AC_DEFINE_UNQUOTED([HAVE_ATTRIBUTE_$1], 1, [Define to 1 if your GCC C compiler supports the '$1' attribute.]), Index: autoconf/ast_ext_lib.m4 =================================================================== --- a/autoconf/ast_ext_lib.m4 (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/autoconf/ast_ext_lib.m4 (.../trunk) (revision 202568) @@ -11,7 +11,7 @@ $1_DESCRIP="$2" $1_OPTION="$3" PBX_$1=0 - AC_ARG_WITH([$3], AC_HELP_STRING([--with-$3=PATH],[use $2 files in PATH $4]), + AC_ARG_WITH([$3], AC_HELP_STRING([--with-$3=PATH],[use $2 files in PATH$4]), [ case ${withval} in n|no) Index: funcs/func_rand.c =================================================================== --- a/funcs/func_rand.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_rand.c (.../trunk) (revision 202568) @@ -87,6 +87,7 @@ static struct ast_custom_function acf_rand = { .name = "RAND", .read = acf_rand_exec, + .read_max = 12, }; static int unload_module(void) Index: funcs/func_base64.c =================================================================== --- a/funcs/func_base64.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_base64.c (.../trunk) (revision 202568) @@ -29,6 +29,7 @@ #include "asterisk/module.h" #include "asterisk/pbx.h" /* function register/unregister */ #include "asterisk/utils.h" +#include "asterisk/strings.h" /*** DOCUMENTATION @@ -59,40 +60,61 @@ ***/ -static int base64_encode(struct ast_channel *chan, const char *cmd, char *data, - char *buf, size_t len) +static int base64_helper(struct ast_channel *chan, const char *cmd, char *data, + char *buf, struct ast_str **str, ssize_t len) { if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "Syntax: BASE64_ENCODE() - missing argument!\n"); + ast_log(LOG_WARNING, "Syntax: %s() - missing argument!\n", cmd); return -1; } - ast_base64encode(buf, (unsigned char *) data, strlen(data), len); + if (cmd[7] == 'E') { + if (buf) { + ast_base64encode(buf, (unsigned char *) data, strlen(data), len); + } else { + if (len >= 0) { + ast_str_make_space(str, len ? len : ast_str_strlen(*str) + strlen(data) * 4 / 3 + 2); + } + ast_base64encode(ast_str_buffer(*str) + ast_str_strlen(*str), (unsigned char *) data, strlen(data), ast_str_size(*str) - ast_str_strlen(*str)); + ast_str_update(*str); + } + } else { + if (buf) { + ast_base64decode((unsigned char *) buf, data, len); + } else { + if (len >= 0) { + ast_str_make_space(str, len ? len : ast_str_strlen(*str) + strlen(data) * 3 / 4 + 2); + } + ast_base64decode((unsigned char *) ast_str_buffer(*str) + ast_str_strlen(*str), data, ast_str_size(*str) - ast_str_strlen(*str)); + ast_str_update(*str); + } + } return 0; } -static int base64_decode(struct ast_channel *chan, const char *cmd, char *data, +static int base64_buf_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) { - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "Syntax: BASE64_DECODE() - missing argument!\n"); - return -1; - } + return base64_helper(chan, cmd, data, buf, NULL, len); +} - ast_base64decode((unsigned char *) buf, data, len); - - return 0; +static int base64_str_helper(struct ast_channel *chan, const char *cmd, char *data, + struct ast_str **buf, ssize_t len) +{ + return base64_helper(chan, cmd, data, NULL, buf, len); } static struct ast_custom_function base64_encode_function = { .name = "BASE64_ENCODE", - .read = base64_encode, + .read = base64_buf_helper, + .read2 = base64_str_helper, }; static struct ast_custom_function base64_decode_function = { .name = "BASE64_DECODE", - .read = base64_decode, + .read = base64_buf_helper, + .read2 = base64_str_helper, }; static int unload_module(void) Index: funcs/func_speex.c =================================================================== --- a/funcs/func_speex.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_speex.c (.../trunk) (revision 202568) @@ -337,13 +337,15 @@ static struct ast_custom_function agc_function = { .name = "AGC", .write = speex_write, - .read = speex_read + .read = speex_read, + .read_max = 22, }; static struct ast_custom_function denoise_function = { .name = "DENOISE", .write = speex_write, - .read = speex_read + .read = speex_read, + .read_max = 22, }; static int unload_module(void) Index: funcs/func_module.c =================================================================== --- a/funcs/func_module.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_module.c (.../trunk) (revision 202568) @@ -65,6 +65,7 @@ static struct ast_custom_function ifmodule_function = { .name = "IFMODULE", .read = ifmodule_read, + .read_max = 2, }; Index: funcs/func_md5.c =================================================================== --- a/funcs/func_md5.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_md5.c (.../trunk) (revision 202568) @@ -64,6 +64,7 @@ static struct ast_custom_function md5_function = { .name = "MD5", .read = md5, + .read_max = 33, }; static int unload_module(void) Index: funcs/func_env.c =================================================================== --- a/funcs/func_env.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_env.c (.../trunk) (revision 202568) @@ -230,7 +230,8 @@ static struct ast_custom_function stat_function = { .name = "STAT", - .read = stat_read + .read = stat_read, + .read_max = 12, }; static struct ast_custom_function file_function = { Index: funcs/func_strings.c =================================================================== --- a/funcs/func_strings.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_strings.c (.../trunk) (revision 202568) @@ -276,12 +276,83 @@ Example: ${QUOTE(ab"c"de)} will return "abcde" + + + Removes and returns the first item off of a variable containing delimited text + + + + + + + Example: + exten => s,1,Set(array=one,two,three) + exten => s,n,While($["${SET(var=${SHIFT(array)})}" != ""]) + exten => s,n,NoOp(var is ${var}) + exten => s,n,EndWhile + This would iterate over each value in array, left to right, and + would result in NoOp(var is one), NoOp(var is two), and + NoOp(var is three) being executed. + + + + + + Removes and returns the last item off of a variable containing delimited text + + + + + + + Example: + exten => s,1,Set(array=one,two,three) + exten => s,n,While($["${SET(var=${POP(array)})}" != ""]) + exten => s,n,NoOp(var is ${var}) + exten => s,n,EndWhile + This would iterate over each value in array, right to left, and + would result in NoOp(var is three), NoOp(var is two), and + NoOp(var is one) being executed. + + + + + + Appends one or more values to the end of a variable containing delimited text + + + + + + + Example: Set(PUSH(array)=one,two,three) would append one, + two, and three to the end of the values stored in the variable + "array". + + + + + + Inserts one or more values to the beginning of a variable containing delimited text + + + + + + + Example: Set(UNSHIFT(array)=one,two,three) would insert one, + two, and three before the values stored in the variable + "array". + + + ***/ -static int function_fieldqty(struct ast_channel *chan, const char *cmd, - char *parse, char *buf, size_t len) +static int function_fieldqty_helper(struct ast_channel *chan, const char *cmd, + char *parse, char *buf, struct ast_str **sbuf, ssize_t len) { - char *varsubst, varval[8192], *varval2 = varval; + char *varsubst; + struct ast_str *str = ast_str_create(16); int fieldcount = 0; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(varname); @@ -290,6 +361,10 @@ char delim[2] = ""; size_t delim_used; + if (!str) { + return -1; + } + AST_STANDARD_APP_ARGS(args, parse); if (args.delim) { ast_get_encoded_char(args.delim, delim, &delim_used); @@ -297,27 +372,47 @@ varsubst = alloca(strlen(args.varname) + 4); sprintf(varsubst, "${%s}", args.varname); - pbx_substitute_variables_helper(chan, varsubst, varval, sizeof(varval) - 1); - if (ast_strlen_zero(varval2)) + ast_str_substitute_variables(&str, 0, chan, varsubst); + if (ast_str_strlen(str) == 0) { fieldcount = 0; - else { - while (strsep(&varval2, delim)) + } else { + char *varval = ast_str_buffer(str); + while (strsep(&varval, delim)) { fieldcount++; + } } } else { fieldcount = 1; } - snprintf(buf, len, "%d", fieldcount); + if (sbuf) { + ast_str_set(sbuf, len, "%d", fieldcount); + } else { + snprintf(buf, len, "%d", fieldcount); + } + ast_free(str); return 0; } +static int function_fieldqty(struct ast_channel *chan, const char *cmd, + char *parse, char *buf, size_t len) +{ + return function_fieldqty_helper(chan, cmd, parse, buf, NULL, len); +} + +static int function_fieldqty_str(struct ast_channel *chan, const char *cmd, + char *parse, struct ast_str **buf, ssize_t len) +{ + return function_fieldqty_helper(chan, cmd, parse, NULL, buf, len); +} + static struct ast_custom_function fieldqty_function = { .name = "FIELDQTY", .read = function_fieldqty, + .read2 = function_fieldqty_str, }; -static int listfilter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len) +static int listfilter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, struct ast_str **bufstr, ssize_t len) { AST_DECLARE_APP_ARGS(args, AST_APP_ARG(listname); @@ -327,11 +422,18 @@ const char *orig_list, *ptr; const char *begin, *cur, *next; int dlen, flen, first = 1; - struct ast_str *result = ast_str_thread_get(&result_buf, 16); + struct ast_str *result, **result_ptr = &result; char *delim; AST_STANDARD_APP_ARGS(args, parse); + if (buf) { + result = ast_str_thread_get(&result_buf, 16); + } else { + /* Place the result directly into the output buffer */ + result_ptr = bufstr; + } + if (args.argc < 3) { ast_log(LOG_ERROR, "Usage: LISTFILTER(,,)\n"); return -1; @@ -351,7 +453,11 @@ /* If the string isn't there, just copy out the string and be done with it. */ if (!(ptr = strstr(orig_list, args.fieldvalue))) { - ast_copy_string(buf, orig_list, len); + if (buf) { + ast_copy_string(buf, orig_list, len); + } else { + ast_str_set(result_ptr, len, "%s", orig_list); + } if (chan) { ast_channel_unlock(chan); } @@ -371,7 +477,9 @@ ast_str_reset(result); /* Enough space for any result */ - ast_str_make_space(&result, strlen(orig_list) + 1); + if (len > -1) { + ast_str_make_space(result_ptr, len ? len : strlen(orig_list) + 1); + } begin = orig_list; next = strstr(begin, delim); @@ -391,10 +499,10 @@ } else { /* Copy field to output */ if (!first) { - ast_str_append(&result, 0, "%s", delim); + ast_str_append(result_ptr, len, "%s", delim); } - ast_str_append_substr(&result, 0, begin, cur - begin + 1); + ast_str_append_substr(result_ptr, len, begin, cur - begin + 1); first = 0; begin = cur + dlen; } @@ -403,14 +511,27 @@ ast_channel_unlock(chan); } - ast_copy_string(buf, ast_str_buffer(result), len); + if (buf) { + ast_copy_string(buf, ast_str_buffer(result), len); + } return 0; } +static int listfilter_read(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len) +{ + return listfilter(chan, cmd, parse, buf, NULL, len); +} + +static int listfilter_read2(struct ast_channel *chan, const char *cmd, char *parse, struct ast_str **buf, ssize_t len) +{ + return listfilter(chan, cmd, parse, NULL, buf, len); +} + static struct ast_custom_function listfilter_function = { .name = "LISTFILTER", - .read = listfilter, + .read = listfilter_read, + .read2 = listfilter_read2, }; static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, @@ -538,7 +659,7 @@ AST_LIST_TRAVERSE_SAFE_END } -static int exec_clearhash(struct ast_channel *chan, void *data) +static int exec_clearhash(struct ast_channel *chan, const char *data) { char prefix[80]; snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null"); @@ -614,16 +735,15 @@ static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) { struct ast_var_t *newvar; - int plen; - char prefix[80]; - snprintf(prefix, sizeof(prefix), HASH_PREFIX, data); - plen = strlen(prefix); + struct ast_str *prefix = ast_str_alloca(80); + ast_str_set(&prefix, -1, HASH_PREFIX, data); memset(buf, 0, len); + AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) { - if (strncasecmp(prefix, ast_var_name(newvar), plen) == 0) { + if (strncasecmp(ast_str_buffer(prefix), ast_var_name(newvar), ast_str_strlen(prefix)) == 0) { /* Copy everything after the prefix */ - strncat(buf, ast_var_name(newvar) + plen, len - strlen(buf) - 1); + strncat(buf, ast_var_name(newvar) + ast_str_strlen(prefix), len - strlen(buf) - 1); /* Trim the trailing ~ */ buf[strlen(buf) - 1] = ','; } @@ -633,6 +753,29 @@ return 0; } +static int hashkeys_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len) +{ + struct ast_var_t *newvar; + struct ast_str *prefix = ast_str_alloca(80); + char *tmp; + + ast_str_set(&prefix, -1, HASH_PREFIX, data); + + AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) { + if (strncasecmp(ast_str_buffer(prefix), ast_var_name(newvar), ast_str_strlen(prefix)) == 0) { + /* Copy everything after the prefix */ + ast_str_append(buf, len, "%s", ast_var_name(newvar) + ast_str_strlen(prefix)); + /* Trim the trailing ~ */ + tmp = ast_str_buffer(*buf); + tmp[ast_str_strlen(*buf) - 1] = ','; + } + } + /* Trim the trailing comma */ + tmp = ast_str_buffer(*buf); + tmp[ast_str_strlen(*buf) - 1] = '\0'; + return 0; +} + static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value) { char varname[256]; @@ -708,6 +851,7 @@ static struct ast_custom_function hashkeys_function = { .name = "HASHKEYS", .read = hashkeys_read, + .read2 = hashkeys_read2, }; static struct ast_custom_function array_function = { @@ -759,6 +903,7 @@ static struct ast_custom_function len_function = { .name = "LEN", .read = len, + .read_max = 12, }; static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse, @@ -850,9 +995,23 @@ return 0; } +static int function_eval2(struct ast_channel *chan, const char *cmd, char *data, + struct ast_str **buf, ssize_t buflen) +{ + if (ast_strlen_zero(data)) { + ast_log(LOG_WARNING, "EVAL requires an argument: EVAL()\n"); + return -1; + } + + ast_str_substitute_variables(buf, buflen, chan, data); + + return 0; +} + static struct ast_custom_function eval_function = { .name = "EVAL", .read = function_eval, + .read2 = function_eval2, }; static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen) @@ -904,9 +1063,24 @@ return 0; } +static int string_toupper2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen) +{ + char *bufptr, *dataptr = data; + + if (buflen > -1) { + ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1); + } + bufptr = ast_str_buffer(*buf); + while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = toupper(*dataptr++))); + ast_str_update(*buf); + + return 0; +} + static struct ast_custom_function toupper_function = { .name = "TOUPPER", .read = string_toupper, + .read2 = string_toupper2, }; static int string_tolower(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen) @@ -918,11 +1092,160 @@ return 0; } +static int string_tolower2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen) +{ + char *bufptr, *dataptr = data; + + if (buflen > -1) { + ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1); + } + bufptr = ast_str_buffer(*buf); + while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = tolower(*dataptr++))); + ast_str_update(*buf); + + return 0; +} + static struct ast_custom_function tolower_function = { .name = "TOLOWER", .read = string_tolower, + .read2 = string_tolower2, }; +static int array_remove(struct ast_channel *chan, const char *cmd, char *var, char *buf, size_t len, int beginning) +{ + const char *tmp; + char *after, *before; + char *(*search_func)(const char *s, int c) = beginning ? strchr : strrchr; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(var); + AST_APP_ARG(delimiter); + ); + + if (!chan) { + ast_log(LOG_WARNING, "%s requires a channel\n", cmd); + return -1; + } + + AST_STANDARD_APP_ARGS(args, var); + + if (ast_strlen_zero(args.var)) { + ast_log(LOG_WARNING, "%s requires a channel variable name\n", cmd); + return -1; + } + + if (args.delimiter && strlen(args.delimiter) != 1) { + ast_log(LOG_WARNING, "%s delimeters should be a single character\n", cmd); + return -1; + } + + ast_channel_lock(chan); + if (ast_strlen_zero(tmp = pbx_builtin_getvar_helper(chan, args.var))) { + ast_channel_unlock(chan); + return 0; + } + + before = ast_strdupa(tmp); + ast_channel_unlock(chan); + + /* Only one entry in array */ + if (!(after = search_func(before, S_OR(args.delimiter, ",")[0]))) { + ast_copy_string(buf, before, len); + pbx_builtin_setvar_helper(chan, args.var, ""); + } else { + *after++ = '\0'; + ast_copy_string(buf, beginning ? before : after, len); + pbx_builtin_setvar_helper(chan, args.var, beginning ? after : before); + } + + return 0; + +} + +static int shift(struct ast_channel *chan, const char *cmd, char *var, char *buf, size_t len) +{ + return array_remove(chan, cmd, var, buf, len, 1); +} +static struct ast_custom_function shift_function = { + .name = "SHIFT", + .read = shift, +}; + +static int pop(struct ast_channel *chan, const char *cmd, char *var, char *buf, size_t len) +{ + return array_remove(chan, cmd, var, buf, len, 0); +} + +static struct ast_custom_function pop_function = { + .name = "POP", + .read = pop, +}; + +static int array_insert(struct ast_channel *chan, const char *cmd, char *var, const char *val, int beginning) +{ + const char *tmp; + struct ast_str *buf; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(var); + AST_APP_ARG(delimiter); + ); + + if (!chan) { + ast_log(LOG_WARNING, "%s requires a channel\n", cmd); + return -1; + } + + AST_STANDARD_APP_ARGS(args, var); + + if (ast_strlen_zero(args.var) || ast_strlen_zero(val)) { + ast_log(LOG_WARNING, "%s requires a variable, and at least one value\n", cmd); + return -1; + } + + if (args.delimiter && strlen(args.delimiter) != 1) { + ast_log(LOG_WARNING, "%s delimeters should be a single character\n", cmd); + return -1; + } + + if (!(buf = ast_str_create(32))) { + ast_log(LOG_ERROR, "Unable to allocate memory for buffer!\n"); + return -1; + } + + ast_channel_lock(chan); + if (!(tmp = pbx_builtin_getvar_helper(chan, args.var))) { + ast_str_set(&buf, 0, "%s", val); + } else { + ast_str_append(&buf, 0, "%s%s%s", beginning ? val : tmp, S_OR(args.delimiter, ","), beginning ? tmp : val); + } + ast_channel_unlock(chan); + + pbx_builtin_setvar_helper(chan, args.var, ast_str_buffer(buf)); + ast_free(buf); + + return 0; +} + +static int push(struct ast_channel *chan, const char *cmd, char *var, const char *val) +{ + return array_insert(chan, cmd, var, val, 0); +} + +static struct ast_custom_function push_function = { + .name = "PUSH", + .write = push, +}; + +static int unshift(struct ast_channel *chan, const char *cmd, char *var, const char *val) +{ + return array_insert(chan, cmd, var, val, 1); +} + +static struct ast_custom_function unshift_function = { + .name = "UNSHIFT", + .write = unshift, +}; + static int unload_module(void) { int res = 0; @@ -943,6 +1266,10 @@ res |= ast_unregister_application(app_clearhash); res |= ast_custom_function_unregister(&toupper_function); res |= ast_custom_function_unregister(&tolower_function); + res |= ast_custom_function_unregister(&shift_function); + res |= ast_custom_function_unregister(&pop_function); + res |= ast_custom_function_unregister(&push_function); + res |= ast_custom_function_unregister(&unshift_function); return res; } @@ -967,6 +1294,10 @@ res |= ast_register_application_xml(app_clearhash, exec_clearhash); res |= ast_custom_function_register(&toupper_function); res |= ast_custom_function_register(&tolower_function); + res |= ast_custom_function_register(&shift_function); + res |= ast_custom_function_register(&pop_function); + res |= ast_custom_function_register(&push_function); + res |= ast_custom_function_register(&unshift_function); return res; } Index: funcs/func_sysinfo.c =================================================================== --- a/funcs/func_sysinfo.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_sysinfo.c (.../trunk) (revision 202568) @@ -36,6 +36,57 @@ #include "asterisk/module.h" #include "asterisk/pbx.h" +/*** DOCUMENTATION + + + Returns system information specified by parameter. + + + + + + System load average from past minute. + + + Number of active calls currently in progress. + + + System uptime in hours. + This parameter is dependant upon operating system. + + + Total usable main memory size in KiB. + This parameter is dependant upon operating system. + + + Available memory size in KiB. + This parameter is dependant upon operating system. + + + Memory used by buffers in KiB. + This parameter is dependant upon operating system. + + + Total swap space still available in KiB. + This parameter is dependant upon operating system. + + + Free swap space still available in KiB. + This parameter is dependant upon operating system. + + + Number of current processes. + This parameter is dependant upon operating system. + + + + + + Returns information from a given parameter. + + + ***/ + static int sysinfo_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) { @@ -83,23 +134,8 @@ static struct ast_custom_function sysinfo_function = { .name = "SYSINFO", - .synopsis = "Returns system information specified by parameter.", - .syntax = "SYSINFO()", .read = sysinfo_helper, - .desc = -"Returns information from a given parameter\n" -" Options:\n" -" loadavg - system load average from past minute\n" -" numcalls - number of active calls currently in progress\n" -#if defined(HAVE_SYSINFO) -" uptime - system uptime in hours\n" -" totalram - total usable main memory size in KiB\n" -" freeram - available memory size in KiB\n" -" bufferram - memory used by buffers in KiB\n" -" totalswap - total swap space size in KiB\n" -" freeswap - free swap space still available in KiB\n" -" numprocs - number of current processes\n", -#endif /* HAVE_SYSINFO */ + .read_max = 22, }; static int unload_module(void) Index: funcs/func_vmcount.c =================================================================== --- a/funcs/func_vmcount.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_vmcount.c (.../trunk) (revision 202568) @@ -94,9 +94,10 @@ return 0; } -struct ast_custom_function acf_vmcount = { +static struct ast_custom_function acf_vmcount = { .name = "VMCOUNT", .read = acf_vmcount_exec, + .read_max = 12, }; static int unload_module(void) Index: funcs/func_sha1.c =================================================================== --- a/funcs/func_sha1.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_sha1.c (.../trunk) (revision 202568) @@ -74,6 +74,7 @@ static struct ast_custom_function sha1_function = { .name = "SHA1", .read = sha1, + .read_max = 42, }; static int unload_module(void) Index: funcs/func_logic.c =================================================================== --- a/funcs/func_logic.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_logic.c (.../trunk) (revision 202568) @@ -223,41 +223,73 @@ return 0; } -static int acf_import(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) +static int set2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **str, ssize_t len) { + if (len > -1) { + ast_str_make_space(str, len == 0 ? strlen(data) : len); + } + return set(chan, cmd, data, ast_str_buffer(*str), ast_str_size(*str)); +} + +static int import_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **str, ssize_t len) +{ AST_DECLARE_APP_ARGS(args, AST_APP_ARG(channel); AST_APP_ARG(varname); ); AST_STANDARD_APP_ARGS(args, data); - buf[0] = 0; + if (buf) { + *buf = '\0'; + } + if (!ast_strlen_zero(args.varname)) { - struct ast_channel *chan2 = ast_get_channel_by_name_locked(args.channel); - if (chan2) { + struct ast_channel *chan2; + + if ((chan2 = ast_channel_get_by_name(args.channel))) { char *s = alloca(strlen(args.varname) + 4); if (s) { sprintf(s, "${%s}", args.varname); - pbx_substitute_variables_helper(chan2, s, buf, len); + ast_channel_lock(chan2); + if (buf) { + pbx_substitute_variables_helper(chan2, s, buf, len); + } else { + ast_str_substitute_variables(str, len, chan2, s); + } + ast_channel_unlock(chan2); } - ast_channel_unlock(chan2); + chan2 = ast_channel_unref(chan2); } } + return 0; } +static int import_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) +{ + return import_helper(chan, cmd, data, buf, NULL, len); +} + +static int import_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **str, ssize_t len) +{ + return import_helper(chan, cmd, data, NULL, str, len); +} + static struct ast_custom_function isnull_function = { .name = "ISNULL", .read = isnull, + .read_max = 2, }; static struct ast_custom_function set_function = { .name = "SET", .read = set, + .read2 = set2, }; static struct ast_custom_function exists_function = { .name = "EXISTS", .read = exists, + .read_max = 2, }; static struct ast_custom_function if_function = { @@ -272,7 +304,8 @@ static struct ast_custom_function import_function = { .name = "IMPORT", - .read = acf_import, + .read = import_read, + .read2 = import_read2, }; static int unload_module(void) Index: funcs/func_enum.c =================================================================== --- a/funcs/func_enum.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_enum.c (.../trunk) (revision 202568) @@ -216,7 +216,7 @@ return 0; } -unsigned int enum_datastore_id; +static unsigned int enum_datastore_id; struct enum_result_datastore { struct enum_context *context; @@ -243,7 +243,7 @@ erds_destroy(erds); } -const struct ast_datastore_info enum_result_datastore_info = { +static const struct ast_datastore_info enum_result_datastore_info = { .type = "ENUMQUERY", .destroy = erds_destroy_cb, }; Index: funcs/func_groupcount.c =================================================================== --- a/funcs/func_groupcount.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_groupcount.c (.../trunk) (revision 202568) @@ -134,6 +134,7 @@ static struct ast_custom_function group_count_function = { .name = "GROUP_COUNT", .read = group_count_function_read, + .read_max = 12, }; static int group_match_count_function_read(struct ast_channel *chan, @@ -159,6 +160,7 @@ static struct ast_custom_function group_match_count_function = { .name = "GROUP_MATCH_COUNT", .read = group_match_count_function_read, + .read_max = 12, .write = NULL, }; Index: funcs/func_odbc.c =================================================================== --- a/funcs/func_odbc.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_odbc.c (.../trunk) (revision 202568) @@ -99,10 +99,10 @@ static char *config = "func_odbc.conf"; -enum { +enum odbc_option_flags { OPT_ESCAPECOMMAS = (1 << 0), OPT_MULTIROW = (1 << 1), -} odbc_option_flags; +}; struct acf_odbc_query { AST_RWLIST_ENTRY(acf_odbc_query) list; @@ -118,7 +118,7 @@ static void odbc_datastore_free(void *data); -struct ast_datastore_info odbc_info = { +static struct ast_datastore_info odbc_info = { .type = "FUNC_ODBC", .destroy = odbc_datastore_free, }; @@ -135,7 +135,7 @@ char names[0]; }; -AST_RWLIST_HEAD_STATIC(queries, acf_odbc_query); +static AST_RWLIST_HEAD_STATIC(queries, acf_odbc_query); static int resultcount = 0; @@ -257,7 +257,7 @@ if (chan) ast_autoservice_stop(chan); if (bogus_chan) { - ast_channel_free(chan); + ast_channel_release(chan); } else { pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status); } @@ -367,7 +367,7 @@ if (chan) ast_autoservice_stop(chan); if (bogus_chan) - ast_channel_free(chan); + ast_channel_release(chan); return 0; } @@ -473,7 +473,7 @@ ast_autoservice_stop(chan); } if (bogus_chan) { - ast_channel_free(chan); + ast_channel_release(chan); } return -1; } @@ -490,7 +490,7 @@ ast_autoservice_stop(chan); } if (bogus_chan) { - ast_channel_free(chan); + ast_channel_release(chan); } return -1; } @@ -517,7 +517,7 @@ if (chan) ast_autoservice_stop(chan); if (bogus_chan) - ast_channel_free(chan); + ast_channel_release(chan); return res1; } @@ -561,7 +561,7 @@ if (chan) ast_autoservice_stop(chan); if (bogus_chan) - ast_channel_free(chan); + ast_channel_release(chan); return -1; } resultset = tmp; @@ -657,7 +657,7 @@ if (chan) ast_autoservice_stop(chan); if (bogus_chan) - ast_channel_free(chan); + ast_channel_release(chan); return -1; } odbc_store->data = resultset; @@ -670,7 +670,7 @@ if (chan) ast_autoservice_stop(chan); if (bogus_chan) - ast_channel_free(chan); + ast_channel_release(chan); return 0; } @@ -732,7 +732,7 @@ static char *app_odbcfinish = "ODBCFinish"; -static int exec_odbcfinish(struct ast_channel *chan, void *data) +static int exec_odbcfinish(struct ast_channel *chan, const char *data) { struct ast_datastore *store = ast_channel_datastore_find(chan, &odbc_info, data); if (!store) /* Already freed; no big deal. */ @@ -1060,7 +1060,7 @@ } ast_str_substitute_variables(&sql, 0, chan, query->sql_read); - ast_channel_free(chan); + chan = ast_channel_release(chan); if (a->argc == 5 && !strcmp(a->argv[4], "exec")) { /* Execute the query */ @@ -1273,7 +1273,7 @@ pbx_builtin_pushvar_helper(chan, "VALUE", S_OR(a->argv[4], "")); ast_str_substitute_variables(&sql, 0, chan, query->sql_write); ast_debug(1, "SQL is %s\n", ast_str_buffer(sql)); - ast_channel_free(chan); + chan = ast_channel_release(chan); if (a->argc == 6 && !strcmp(a->argv[5], "exec")) { /* Execute the query */ Index: funcs/func_aes.c =================================================================== --- a/funcs/func_aes.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_aes.c (.../trunk) (revision 202568) @@ -31,6 +31,7 @@ #include "asterisk/pbx.h" #include "asterisk/app.h" #include "asterisk/aes.h" +#include "asterisk/strings.h" #define AES_BLOCK_SIZE 16 @@ -71,15 +72,15 @@ static int aes_helper(struct ast_channel *chan, const char *cmd, char *data, - char *buf, size_t len) + char *buf, struct ast_str **str, ssize_t maxlen) { unsigned char curblock[AES_BLOCK_SIZE] = { 0, }; - char *tmp; + char *tmp = NULL; char *tmpP; int data_len, encrypt; + int keylen, len, tmplen, elen = 0; ast_aes_encrypt_key ecx; /* AES 128 Encryption context */ ast_aes_decrypt_key dcx; - AST_DECLARE_APP_ARGS(args, AST_APP_ARG(key); AST_APP_ARG(data); @@ -92,22 +93,37 @@ return -1; } - if (strlen(args.key) != AES_BLOCK_SIZE) { /* key must be of 16 characters in length, 128 bits */ - ast_log(LOG_WARNING, "Syntax: %s(,) - parameter must be exactly 16 characters!\n", cmd); + if ((keylen = strlen(args.key)) != AES_BLOCK_SIZE) { /* key must be of 16 characters in length, 128 bits */ + ast_log(LOG_WARNING, "Syntax: %s(,) - parameter must be exactly 16 characters%s!\n", cmd, keylen < 16 ? " - padding" : ""); return -1; } - ast_aes_encrypt_key((unsigned char *) args.key, &ecx); /* encryption: plaintext -> encryptedtext -> base64 */ - ast_aes_decrypt_key((unsigned char *) args.key, &dcx); /* decryption: base64 -> encryptedtext -> plaintext */ - tmp = ast_calloc(1, len); /* requires a tmp buffer for the base64 decode */ + if (buf) { + len = maxlen; + } else if (maxlen == -1) { + len = ast_str_size(*str); + } else if (maxlen > 0) { + len = maxlen; + } else { + len = INT_MAX; + } + ast_debug(3, "len=%d\n", len); + + encrypt = strcmp("AES_DECRYPT", cmd); /* -1 if encrypting, 0 if decrypting */ + /* Round up the buffer to an even multiple of 16, plus 1 */ + tmplen = (strlen(args.data) / 16 + 1) * 16 + 1; + tmp = ast_calloc(1, tmplen); tmpP = tmp; - encrypt = strcmp("AES_DECRYPT", cmd); /* -1 if encrypting, 0 if decrypting */ if (encrypt) { /* if decrypting first decode src to base64 */ - ast_copy_string(tmp, args.data, len); + /* encryption: plaintext -> encryptedtext -> base64 */ + ast_aes_encrypt_key((unsigned char *) args.key, &ecx); + strcpy(tmp, args.data); data_len = strlen(tmp); } else { - data_len = ast_base64decode((unsigned char *) tmp, args.data, len); + /* decryption: base64 -> encryptedtext -> plaintext */ + ast_aes_decrypt_key((unsigned char *) args.key, &dcx); + data_len = ast_base64decode((unsigned char *) tmp, args.data, tmplen); } if (data_len >= len) { /* make sure to not go over buffer len */ @@ -116,8 +132,11 @@ } while (data_len > 0) { + /* Tricky operation. We first copy the data into curblock, then + * the data is encrypted or decrypted and put back into the original + * buffer. */ memset(curblock, 0, AES_BLOCK_SIZE); - memcpy(curblock, tmpP, (data_len < AES_BLOCK_SIZE) ? data_len : AES_BLOCK_SIZE); + memcpy(curblock, tmpP, AES_BLOCK_SIZE); if (encrypt) { ast_aes_encrypt(curblock, (unsigned char *) tmpP, &ecx); } else { @@ -125,26 +144,53 @@ } tmpP += AES_BLOCK_SIZE; data_len -= AES_BLOCK_SIZE; + elen += AES_BLOCK_SIZE; } if (encrypt) { /* if encrypting encode result to base64 */ - ast_base64encode(buf, (unsigned char *) tmp, strlen(tmp), len); + if (buf) { + ast_base64encode(buf, (unsigned char *) tmp, elen, len); + } else { + if (maxlen >= 0) { + ast_str_make_space(str, maxlen ? maxlen : elen * 4 / 3 + 2); + } + ast_base64encode(ast_str_buffer(*str), (unsigned char *) tmp, elen, ast_str_size(*str)); + ast_str_update(*str); + } } else { - memcpy(buf, tmp, len); + if (buf) { + memcpy(buf, tmp, len); + } else { + ast_str_set(str, maxlen, "%s", tmp); + } } ast_free(tmp); return 0; } +static int aes_buf_helper(struct ast_channel *chan, const char *cmd, char *data, + char *buf, size_t maxlen) +{ + return aes_helper(chan, cmd, data, buf, NULL, maxlen); +} + +static int aes_str_helper(struct ast_channel *chan, const char *cmd, char *data, + struct ast_str **buf, ssize_t maxlen) +{ + return aes_helper(chan, cmd, data, NULL, buf, maxlen); +} + static struct ast_custom_function aes_encrypt_function = { .name = "AES_ENCRYPT", - .read = aes_helper, + .read = aes_buf_helper, + .read2 = aes_str_helper, }; static struct ast_custom_function aes_decrypt_function = { .name = "AES_DECRYPT", - .read = aes_helper, + .read = aes_buf_helper, + .read2 = aes_str_helper, }; static int unload_module(void) Index: funcs/func_dialplan.c =================================================================== --- a/funcs/func_dialplan.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_dialplan.c (.../trunk) (revision 202568) @@ -105,6 +105,7 @@ static struct ast_custom_function isexten_function = { .name = "DIALPLAN_EXISTS", .read = isexten_function_read, + .read_max = 2, }; static int unload_module(void) Index: funcs/func_db.c =================================================================== --- a/funcs/func_db.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_db.c (.../trunk) (revision 202568) @@ -201,6 +201,7 @@ static struct ast_custom_function db_exists_function = { .name = "DB_EXISTS", .read = function_db_exists, + .read_max = 2, }; static int function_db_delete(struct ast_channel *chan, const char *cmd, Index: funcs/func_timeout.c =================================================================== --- a/funcs/func_timeout.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_timeout.c (.../trunk) (revision 202568) @@ -191,6 +191,7 @@ static struct ast_custom_function timeout_function = { .name = "TIMEOUT", .read = timeout_read, + .read_max = 22, .write = timeout_write, }; Index: funcs/func_lock.c =================================================================== --- a/funcs/func_lock.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_lock.c (.../trunk) (revision 202568) @@ -84,7 +84,7 @@ -AST_LIST_HEAD_STATIC(locklist, lock_frame); +static AST_LIST_HEAD_STATIC(locklist, lock_frame); static void lock_free(void *data); static int unloading = 0; @@ -324,16 +324,19 @@ static struct ast_custom_function lock_function = { .name = "LOCK", .read = lock_read, + .read_max = 2, }; static struct ast_custom_function trylock_function = { .name = "TRYLOCK", .read = trylock_read, + .read_max = 2, }; static struct ast_custom_function unlock_function = { .name = "UNLOCK", .read = unlock_read, + .read_max = 2, }; static int unload_module(void) Index: funcs/func_math.c =================================================================== --- a/funcs/func_math.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_math.c (.../trunk) (revision 202568) @@ -1,9 +1,10 @@ /* * Asterisk -- An open source telephony toolkit. * - * Copyright (C) 2004 - 2006, Andy Powell + * Copyright (C) 2004 - 2006, Andy Powell * * Updated by Mark Spencer + * Updated by Nir Simionovich * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact @@ -22,6 +23,7 @@ * * \author Andy Powell * \author Mark Spencer + * \author Nir Simionovich * * \ingroup functions */ @@ -66,6 +68,40 @@ Example: Set(i=${MATH(123%16,int)}) - sets var i=11 + + + Increments the value of a variable, while returning the updated value to the dialplan + + + + + The variable name to be manipulated, without the braces. + + + + + Increments the value of a variable, while returning the updated value to the dialplan + Example: INC(MyVAR) - Increments MyVar + Note: INC(${MyVAR}) - Is wrong, as INC expects the variable name, not its value + + + + + Decrements the value of a variable, while returning the updated value to the dialplan + + + + + The variable name to be manipulated, without the braces. + + + + + Decrements the value of a variable, while returning the updated value to the dialplan + Example: DEC(MyVAR) - Increments MyVar + Note: DEC(${MyVAR}) - Is wrong, as INC expects the variable name, not its value + + ***/ enum TypeOfFunctions { @@ -333,19 +369,107 @@ return 0; } +static int crement_function_read(struct ast_channel *chan, const char *cmd, + char *data, char *buf, size_t len) +{ + int ret = -1; + int int_value = 0; + int modify_orig = 0; + const char *var; + char endchar = 0, returnvar[12]; /* If you need a variable longer than 11 digits - something is way wrong */ + + if (ast_strlen_zero(data)) { + ast_log(LOG_WARNING, "Syntax: %s() - missing argument!\n", cmd); + return -1; + } + + ast_channel_lock(chan); + + if (!(var = pbx_builtin_getvar_helper(chan, data))) { + ast_log(LOG_NOTICE, "Failed to obtain variable %s, bailing out\n", data); + ast_channel_unlock(chan); + return -1; + } + + if (ast_strlen_zero(var)) { + ast_log(LOG_NOTICE, "Variable %s doesn't exist - are you sure you wrote it corrrectly?\n", data); + ast_channel_unlock(chan); + return -1; + } + + if (sscanf(var, "%d%c", &int_value, &endchar) == 0 || endchar != 0) { + ast_log(LOG_NOTICE, "The content of ${%s} is not a numeric value - bailing out!\n", data); + ast_channel_unlock(chan); + return -1; + } + + /* now we'll actually do something useful */ + if (!strcasecmp(cmd, "INC")) { /* Increment variable */ + int_value++; + modify_orig = 1; + } else if (!strcasecmp(cmd, "DEC")) { /* Decrement variable */ + int_value--; + modify_orig = 1; + } + + ast_log(LOG_NOTICE, "The value is now: %d\n", int_value); + + if (snprintf(returnvar, sizeof(returnvar), "%d", int_value) > 0) { + pbx_builtin_setvar_helper(chan, data, returnvar); + if (modify_orig) { + ast_copy_string(buf, returnvar, len); + } + ret = 0; + } else { + pbx_builtin_setvar_helper(chan, data, "0"); + if (modify_orig) { + ast_copy_string(buf, "0", len); + } + ast_log(LOG_NOTICE, "Variable %s refused to be %sREMENTED, setting value to 0", data, cmd); + ret = 0; + } + + ast_channel_unlock(chan); + + return ret; +} + + static struct ast_custom_function math_function = { .name = "MATH", .read = math }; +static struct ast_custom_function increment_function = { + .name = "INC", + .read = crement_function_read, +}; + +static struct ast_custom_function decrement_function = { + .name = "DEC", + .read = crement_function_read, +}; + static int unload_module(void) { - return ast_custom_function_unregister(&math_function); + int res = 0; + + res |= ast_custom_function_unregister(&math_function); + res |= ast_custom_function_unregister(&increment_function); + res |= ast_custom_function_unregister(&decrement_function); + + return res; } static int load_module(void) { - return ast_custom_function_register(&math_function); + int res = 0; + + res |= ast_custom_function_register(&math_function); + res |= ast_custom_function_register(&increment_function); + res |= ast_custom_function_register(&decrement_function); + + return res; } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mathematical dialplan function"); Index: funcs/func_cut.c =================================================================== --- a/funcs/func_cut.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_cut.c (.../trunk) (revision 202568) @@ -77,9 +77,6 @@ ***/ -/* Maximum length of any variable */ -#define MAXRESULT 1024 - struct sortable_keys { char *key; float value; @@ -151,99 +148,86 @@ return 0; } -static int cut_internal(struct ast_channel *chan, char *data, char *buffer, size_t buflen) +static int cut_internal(struct ast_channel *chan, char *data, struct ast_str **buf, ssize_t buflen) { - char *parse; + char *parse, ds[2], *var_expr; size_t delim_consumed; + struct ast_str *var_value; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(varname); AST_APP_ARG(delimiter); AST_APP_ARG(field); ); - *buffer = '\0'; - parse = ast_strdupa(data); AST_STANDARD_APP_ARGS(args, parse); - /* Check and parse arguments */ + /* Check arguments */ if (args.argc < 3) { return ERROR_NOARG; - } else { - char d, ds[2] = ""; - char *tmp = alloca(strlen(args.varname) + 4); - char varvalue[MAXRESULT], *tmp2=varvalue; + } else if (!(var_expr = alloca(strlen(args.varname) + 4))) { + return ERROR_NOMEM; + } - if (tmp) { - snprintf(tmp, strlen(args.varname) + 4, "${%s}", args.varname); - } else { - return ERROR_NOMEM; - } + /* Get the value of the variable named in the 1st argument */ + snprintf(var_expr, strlen(args.varname) + 4, "${%s}", args.varname); + var_value = ast_str_create(16); + ast_str_substitute_variables(&var_value, 0, chan, var_expr); - if (ast_get_encoded_char(args.delimiter, ds, &delim_consumed)) - ast_copy_string(ds, "-", sizeof(ds)); + /* Copy delimiter from 2nd argument to ds[] possibly decoding backslash escapes */ + if (ast_get_encoded_char(args.delimiter, ds, &delim_consumed)) { + ast_copy_string(ds, "-", sizeof(ds)); + } + ds[1] = '\0'; - /* String form of the delimiter, for use with strsep(3) */ - d = *ds; + if (ast_str_strlen(var_value)) { + int curfieldnum = 1; + char *curfieldptr = ast_str_buffer(var_value); + int out_field_count = 0; - pbx_substitute_variables_helper(chan, tmp, tmp2, MAXRESULT - 1); + while (curfieldptr != NULL && args.field != NULL) { + char *next_range = strsep(&(args.field), "&"); + int start_field, stop_field; + char trashchar; - if (tmp2) { - int curfieldnum = 1, firstfield = 1; - while (tmp2 != NULL && args.field != NULL) { - char *nextgroup = strsep(&(args.field), "&"); - int num1 = 0, num2 = MAXRESULT; - char trashchar; + if (sscanf(next_range, "%d-%d", &start_field, &stop_field) == 2) { + /* range with both start and end */ + } else if (sscanf(next_range, "-%d", &stop_field) == 1) { + /* range with end only */ + start_field = 1; + } else if ((sscanf(next_range, "%d%c", &start_field, &trashchar) == 2) && (trashchar == '-')) { + /* range with start only */ + stop_field = INT_MAX; + } else if (sscanf(next_range, "%d", &start_field) == 1) { + /* single number */ + stop_field = start_field; + } else { + /* invalid field spec */ + ast_free(var_value); + return ERROR_USAGE; + } - if (sscanf(nextgroup, "%d-%d", &num1, &num2) == 2) { - /* range with both start and end */ - } else if (sscanf(nextgroup, "-%d", &num2) == 1) { - /* range with end */ - num1 = 0; - } else if ((sscanf(nextgroup, "%d%c", &num1, &trashchar) == 2) && (trashchar == '-')) { - /* range with start */ - num2 = MAXRESULT; - } else if (sscanf(nextgroup, "%d", &num1) == 1) { - /* single number */ - num2 = num1; - } else { - return ERROR_USAGE; - } + /* Get to start, if not there already */ + while (curfieldptr != NULL && curfieldnum < start_field) { + strsep(&curfieldptr, ds); + curfieldnum++; + } - /* Get to start, if any */ - if (num1 > 0) { - while (tmp2 != (char *)NULL + 1 && curfieldnum < num1) { - tmp2 = strchr(tmp2, d) + 1; - curfieldnum++; - } - } + /* Most frequent problem is the expectation of reordering fields */ + if (curfieldnum > start_field) { + ast_log(LOG_WARNING, "We're already past the field you wanted?\n"); + } - /* Most frequent problem is the expectation of reordering fields */ - if ((num1 > 0) && (curfieldnum > num1)) - ast_log(LOG_WARNING, "We're already past the field you wanted?\n"); - - /* Re-null tmp2 if we added 1 to NULL */ - if (tmp2 == (char *)NULL + 1) - tmp2 = NULL; - - /* Output fields until we either run out of fields or num2 is reached */ - while (tmp2 != NULL && curfieldnum <= num2) { - char *tmp3 = strsep(&tmp2, ds); - int curlen = strlen(buffer); - - if (firstfield) { - snprintf(buffer, buflen, "%s", tmp3); - firstfield = 0; - } else { - snprintf(buffer + curlen, buflen - curlen, "%c%s", d, tmp3); - } - - curfieldnum++; - } + /* Output fields until we either run out of fields or stop_field is reached */ + while (curfieldptr != NULL && curfieldnum <= stop_field) { + char *field_value = strsep(&curfieldptr, ds); + ast_str_append(buf, buflen, "%s%s", out_field_count++ ? ds : "", field_value); + curfieldnum++; } } } + ast_free(var_value); return 0; } @@ -271,7 +255,33 @@ static int acf_cut_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) { int ret = -1; + struct ast_str *str = ast_str_create(16); + switch (cut_internal(chan, data, &str, len)) { + case ERROR_NOARG: + ast_log(LOG_ERROR, "Syntax: CUT(,,) - missing argument!\n"); + break; + case ERROR_NOMEM: + ast_log(LOG_ERROR, "Out of memory\n"); + break; + case ERROR_USAGE: + ast_log(LOG_ERROR, "Usage: CUT(,,)\n"); + break; + case 0: + ret = 0; + ast_copy_string(buf, ast_str_buffer(str), len); + break; + default: + ast_log(LOG_ERROR, "Unknown internal error\n"); + } + ast_free(str); + return ret; +} + +static int acf_cut_exec2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len) +{ + int ret = -1; + switch (cut_internal(chan, data, buf, len)) { case ERROR_NOARG: ast_log(LOG_ERROR, "Syntax: CUT(,,) - missing argument!\n"); @@ -292,14 +302,15 @@ return ret; } -struct ast_custom_function acf_sort = { +static struct ast_custom_function acf_sort = { .name = "SORT", .read = acf_sort_exec, }; -struct ast_custom_function acf_cut = { +static struct ast_custom_function acf_cut = { .name = "CUT", .read = acf_cut_exec, + .read2 = acf_cut_exec2, }; static int unload_module(void) Index: funcs/func_redirecting.c =================================================================== --- a/funcs/func_redirecting.c (.../tags/1.6.2.0-beta3) (revision 0) +++ b/funcs/func_redirecting.c (.../trunk) (revision 202568) @@ -0,0 +1,482 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2008 Digium, Inc. + * + * Richard Mudgett + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \brief Redirecting data dialplan function + * \ingroup functions + * + * \author Richard Mudgett + * + * See Also: + * \arg \ref AstCREDITS + */ + + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +/* ------------------------------------------------------------------- */ + + +#include +#include +#include +#include + +#include "asterisk/module.h" +#include "asterisk/channel.h" +#include "asterisk/pbx.h" +#include "asterisk/logger.h" +#include "asterisk/utils.h" +#include "asterisk/app.h" +#include "asterisk/options.h" +#include "asterisk/callerid.h" + +/* + * Do not document the REDIRECTING(pres) datatype. + * It has turned out that the from-pres and to-pres values must be kept + * separate. They represent two different parties and there is a case when + * they are active at the same time. The plain pres option will simply + * live on as a historical relic. + */ +/*** DOCUMENTATION + + + Gets or sets Redirecting data on the channel. + + + + The allowable datatypes are: + + + + + + + + + + + + + + + + + If set, this will prevent the channel from sending out protocol + messages because of the value being set + + + + Gets or sets Redirecting data on the channel. The allowable values + for the reason field are the following: + + Unknown + Call Forwarding Busy + Call Forwarding No Reply + Callee is Unavailable + Time of Day + Do Not Disturb + Call Deflection + Follow Me + Called DTE Out-Of-Order + Callee is Away + Call Forwarding By The Called DTE + Call Forwarding Unconditional + + + + ***/ + +enum ID_FIELD_STATUS { + ID_FIELD_VALID, + ID_FIELD_INVALID, + ID_FIELD_UNKNOWN +}; + + + + +/* ******************************************************************* */ +/*! + * \internal + * \brief Read values from the party id struct. + * + * \param buf Buffer to fill with read value. + * \param len Length of the buffer + * \param data Remaining function datatype string + * + * \retval ID_FIELD_VALID on success. + * \retval ID_FIELD_UNKNOWN on unknown field name. + */ +static enum ID_FIELD_STATUS redirecting_id_read(char *buf, size_t len, char *data, const struct ast_party_id *id) +{ + enum ID_FIELD_STATUS status; + + status = ID_FIELD_VALID; + + if (!strncasecmp("all", data, 3)) { + snprintf(buf, len, "\"%s\" <%s>", + S_OR(id->name, ""), + S_OR(id->number, "")); + } else if (!strncasecmp("name", data, 4)) { + if (id->name) { + ast_copy_string(buf, id->name, len); + } + } else if (!strncasecmp("num", data, 3)) { + if (id->number) { + ast_copy_string(buf, id->number, len); + } + } else if (!strncasecmp("ton", data, 3)) { + snprintf(buf, len, "%d", id->number_type); + } else if (!strncasecmp("pres", data, 4)) { + ast_copy_string(buf, ast_named_caller_presentation(id->number_presentation), len); + } else { + status = ID_FIELD_UNKNOWN; + } + + return status; +} + + + + +/* ******************************************************************* */ +/*! + * \internal + * \brief Read values from the redirecting information struct. + * + * \param chan Asterisk channel to read + * \param cmd Not used + * \param data Redirecting function datatype string + * \param buf Buffer to fill with read value. + * \param len Length of the buffer + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int redirecting_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) +{ + /* Ensure that the buffer is empty */ + *buf = 0; + + if (!chan) + return -1; + + ast_channel_lock(chan); + + if (!strncasecmp("from-", data, 5)) { + struct ast_party_id from_id; + + from_id = chan->redirecting.from; + from_id.number = chan->cid.cid_rdnis; + switch (redirecting_id_read(buf, len, data + 5, &from_id)) { + case ID_FIELD_VALID: + case ID_FIELD_INVALID: + break; + + default: + ast_log(LOG_ERROR, "Unknown redirecting data type '%s'.\n", data); + break; + } + } else if (!strncasecmp("to-", data, 3)) { + switch (redirecting_id_read(buf, len, data + 3, &chan->redirecting.to)) { + case ID_FIELD_VALID: + case ID_FIELD_INVALID: + break; + + default: + ast_log(LOG_ERROR, "Unknown redirecting data type '%s'.\n", data); + break; + } + } else if (!strncasecmp("pres", data, 4)) { + ast_copy_string(buf, ast_named_caller_presentation(chan->redirecting.from.number_presentation), len); + } else if (!strncasecmp("reason", data, 6)) { + ast_copy_string(buf, ast_redirecting_reason_name(chan->redirecting.reason), len); + } else if (!strncasecmp("count", data, 5)) { + snprintf(buf, len, "%d", chan->redirecting.count); + } else { + ast_log(LOG_ERROR, "Unknown redirecting data type '%s'.\n", data); + } + + ast_channel_unlock(chan); + + return 0; +} + + + + +/* ******************************************************************* */ +/*! + * \internal + * \brief Write new values to the party id struct + * + * \param id Party ID struct to write values + * \param data Remaining function datatype string + * \param value Value to assign to the party id. + * + * \retval ID_FIELD_VALID on success. + * \retval ID_FIELD_INVALID on error with field value. + * \retval ID_FIELD_UNKNOWN on unknown field name. + */ +static enum ID_FIELD_STATUS redirecting_id_write(struct ast_party_id *id, char *data, const char *value) +{ + char *val; + enum ID_FIELD_STATUS status; + + status = ID_FIELD_VALID; + + if (!strncasecmp("all", data, 3)) { + char name[256]; + char num[256]; + + ast_callerid_split(value, name, sizeof(name), num, sizeof(num)); + if (!(id->name = ast_strdup(name))) { + return ID_FIELD_INVALID; + } + if (!(id->number = ast_strdup(num))) { + return ID_FIELD_INVALID; + } + } else if (!strncasecmp("name", data, 4)) { + id->name = ast_strdup(value); + ast_trim_blanks(id->name); + } else if (!strncasecmp("num", data, 3)) { + id->number = ast_strdup(value); + ast_trim_blanks(id->number); + } else if (!strncasecmp("ton", data, 3)) { + val = ast_strdupa(value); + ast_trim_blanks(val); + + if (('0' <= val[0]) && (val[0] <= '9')) { + id->number_type = atoi(val); + } else { + ast_log(LOG_ERROR, "Unknown redirecting type of number '%s', value unchanged\n", val); + status = ID_FIELD_INVALID; + } + } else if (!strncasecmp("pres", data, 4)) { + int pres; + + val = ast_strdupa(value); + ast_trim_blanks(val); + + if (('0' <= val[0]) && (val[0] <= '9')) { + pres = atoi(val); + } else { + pres = ast_parse_caller_presentation(val); + } + + if (pres < 0) { + ast_log(LOG_ERROR, "Unknown redirecting number presentation '%s', value unchanged\n", val); + status = ID_FIELD_INVALID; + } else { + id->number_presentation = pres; + } + } else { + status = ID_FIELD_UNKNOWN; + } + + return status; +} + + + + +/* ******************************************************************* */ +/*! + * \internal + * \brief Write new values to the redirecting information struct. + * + * \param chan Asterisk channel to update + * \param cmd Not used + * \param data Redirecting function datatype string + * \param value Value to assign to the redirecting information struct. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int redirecting_write(struct ast_channel *chan, const char *cmd, char *data, const char *value) +{ + struct ast_party_redirecting redirecting; + char *val; + char *option; + void (*set_it)(struct ast_channel *chan, const struct ast_party_redirecting *redirecting); + + if (!value || !chan) { + return -1; + } + + /* Determine if the update indication inhibit option is present */ + option = strchr(data, ','); + if (option) { + option = ast_skip_blanks(option + 1); + switch (*option) { + case 'i': + set_it = ast_channel_set_redirecting; + break; + + default: + ast_log(LOG_ERROR, "Unknown redirecting option '%s'.\n", option); + return 0; + } + } + else { + set_it = ast_channel_update_redirecting; + } + + ast_channel_lock(chan); + ast_party_redirecting_set_init(&redirecting, &chan->redirecting); + ast_channel_unlock(chan); + + value = ast_skip_blanks(value); + + if (!strncasecmp("from-", data, 5)) { + switch (redirecting_id_write(&redirecting.from, data + 5, value)) { + case ID_FIELD_VALID: + set_it(chan, &redirecting); + ast_party_redirecting_free(&redirecting); + break; + + case ID_FIELD_INVALID: + break; + + default: + ast_log(LOG_ERROR, "Unknown redirecting data type '%s'.\n", data); + break; + } + } else if (!strncasecmp("to-", data, 3)) { + switch (redirecting_id_write(&redirecting.to, data + 3, value)) { + case ID_FIELD_VALID: + set_it(chan, &redirecting); + ast_party_redirecting_free(&redirecting); + break; + + case ID_FIELD_INVALID: + break; + + default: + ast_log(LOG_ERROR, "Unknown redirecting data type '%s'.\n", data); + break; + } + } else if (!strncasecmp("pres", data, 4)) { + int pres; + + val = ast_strdupa(value); + ast_trim_blanks(val); + + if (('0' <= val[0]) && (val[0] <= '9')) { + pres = atoi(val); + } else { + pres = ast_parse_caller_presentation(val); + } + + if (pres < 0) { + ast_log(LOG_ERROR, "Unknown redirecting number presentation '%s', value unchanged\n", val); + } else { + redirecting.from.number_presentation = pres; + redirecting.to.number_presentation = pres; + set_it(chan, &redirecting); + } + } else if (!strncasecmp("reason", data, 6)) { + int reason; + + val = ast_strdupa(value); + ast_trim_blanks(val); + + if (('0' <= val[0]) && (val[0] <= '9')) { + reason = atoi(val); + } else { + reason = ast_redirecting_reason_parse(val); + } + + if (reason < 0) { + ast_log(LOG_ERROR, "Unknown redirecting reason '%s', value unchanged\n", val); + } else { + redirecting.reason = reason; + set_it(chan, &redirecting); + } + } else if (!strncasecmp("count", data, 5)) { + val = ast_strdupa(value); + ast_trim_blanks(val); + + if (('0' <= val[0]) && (val[0] <= '9')) { + redirecting.count = atoi(val); + set_it(chan, &redirecting); + } else { + ast_log(LOG_ERROR, "Unknown redirecting count '%s', value unchanged\n", val); + } + } else { + ast_log(LOG_ERROR, "Unknown redirecting data type '%s'.\n", data); + } + + return 0; +} + + + + +static struct ast_custom_function redirecting_function = { + .name = "REDIRECTING", + .read = redirecting_read, + .write = redirecting_write, +}; + + + + +/* ******************************************************************* */ +/*! + * \internal + * \brief Unload the function module + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int unload_module(void) +{ + return ast_custom_function_unregister(&redirecting_function); +} + + + + +/* ******************************************************************* */ +/*! + * \internal + * \brief Load and initialize the function module. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int load_module(void) +{ + return ast_custom_function_register(&redirecting_function) + ? AST_MODULE_LOAD_DECLINE + : AST_MODULE_LOAD_SUCCESS; +} + + + + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Redirecting data dialplan function"); + + +/* ------------------------------------------------------------------- */ +/* end func_redirecting.c */ Property changes on: funcs/func_redirecting.c ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + Author Date Id Revision Index: funcs/func_global.c =================================================================== --- a/funcs/func_global.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_global.c (.../trunk) (revision 202568) @@ -134,6 +134,7 @@ AST_APP_ARG(var); AST_APP_ARG(chan); ); + struct ast_channel *c_ref = NULL; if (ast_strlen_zero(data)) { ast_log(LOG_WARNING, "SHARED() requires an argument: SHARED([,])\n"); @@ -145,15 +146,20 @@ if (!ast_strlen_zero(args.chan)) { char *prefix = alloca(strlen(args.chan) + 2); sprintf(prefix, "%s-", args.chan); - if (!(chan = ast_get_channel_by_name_locked(args.chan)) && !(chan = ast_get_channel_by_name_prefix_locked(prefix, strlen(prefix)))) { + if (!(c_ref = ast_channel_get_by_name(args.chan)) && !(c_ref = ast_channel_get_by_name_prefix(prefix, strlen(prefix)))) { ast_log(LOG_ERROR, "Channel '%s' not found! Variable '%s' will be blank.\n", args.chan, args.var); return -1; } - } else - ast_channel_lock(chan); + chan = c_ref; + } + ast_channel_lock(chan); + if (!(varstore = ast_channel_datastore_find(chan, &shared_variable_info, NULL))) { ast_channel_unlock(chan); + if (c_ref) { + c_ref = ast_channel_unref(c_ref); + } return -1; } @@ -170,6 +176,10 @@ ast_channel_unlock(chan); + if (c_ref) { + c_ref = ast_channel_unref(c_ref); + } + return 0; } @@ -182,6 +192,7 @@ AST_APP_ARG(var); AST_APP_ARG(chan); ); + struct ast_channel *c_ref = NULL; if (ast_strlen_zero(data)) { ast_log(LOG_WARNING, "SHARED() requires an argument: SHARED([,])\n"); @@ -193,17 +204,22 @@ if (!ast_strlen_zero(args.chan)) { char *prefix = alloca(strlen(args.chan) + 2); sprintf(prefix, "%s-", args.chan); - if (!(chan = ast_get_channel_by_name_locked(args.chan)) && !(chan = ast_get_channel_by_name_prefix_locked(prefix, strlen(prefix)))) { + if (!(c_ref = ast_channel_get_by_name(args.chan)) && !(c_ref = ast_channel_get_by_name_prefix(prefix, strlen(prefix)))) { ast_log(LOG_ERROR, "Channel '%s' not found! Variable '%s' not set to '%s'.\n", args.chan, args.var, value); return -1; } - } else - ast_channel_lock(chan); + chan = c_ref; + } + ast_channel_lock(chan); + if (!(varstore = ast_channel_datastore_find(chan, &shared_variable_info, NULL))) { if (!(varstore = ast_datastore_alloc(&shared_variable_info, NULL))) { ast_log(LOG_ERROR, "Unable to allocate new datastore. Shared variable not set.\n"); ast_channel_unlock(chan); + if (c_ref) { + c_ref = ast_channel_unref(c_ref); + } return -1; } @@ -211,6 +227,9 @@ ast_log(LOG_ERROR, "Unable to allocate variable structure. Shared variable not set.\n"); ast_datastore_free(varstore); ast_channel_unlock(chan); + if (c_ref) { + c_ref = ast_channel_unref(c_ref); + } return -1; } @@ -241,6 +260,10 @@ ast_channel_unlock(chan); + if (c_ref) { + c_ref = ast_channel_unref(c_ref); + } + return 0; } Index: funcs/func_extstate.c =================================================================== --- a/funcs/func_extstate.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_extstate.c (.../trunk) (revision 202568) @@ -122,6 +122,7 @@ static struct ast_custom_function extstate_function = { .name = "EXTENSION_STATE", .read = extstate_read, + .read_max = 12, }; static int unload_module(void) Index: funcs/func_realtime.c =================================================================== --- a/funcs/func_realtime.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_realtime.c (.../trunk) (revision 202568) @@ -410,29 +410,29 @@ return 0; } -struct ast_custom_function realtime_function = { +static struct ast_custom_function realtime_function = { .name = "REALTIME", .read = function_realtime_read, .write = function_realtime_write, }; -struct ast_custom_function realtimefield_function = { +static struct ast_custom_function realtimefield_function = { .name = "REALTIME_FIELD", .read = realtimefield_read, .write = function_realtime_write, }; -struct ast_custom_function realtimehash_function = { +static struct ast_custom_function realtimehash_function = { .name = "REALTIME_HASH", .read = realtimefield_read, }; -struct ast_custom_function realtime_store_function = { +static struct ast_custom_function realtime_store_function = { .name = "REALTIME_STORE", .write = function_realtime_store, }; -struct ast_custom_function realtime_destroy_function = { +static struct ast_custom_function realtime_destroy_function = { .name = "REALTIME_DESTROY", .read = function_realtime_readdestroy, }; Index: funcs/func_curl.c =================================================================== --- a/funcs/func_curl.c (.../tags/1.6.2.0-beta3) (revision 202568) +++ b/funcs/func_curl.c (.../trunk) (revision 202568) @@ -276,7 +276,7 @@ return 0; } -static int acf_curlopt_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) +static int acf_curlopt_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, ssize_t len) { struct ast_datastore *store; struct global_curl_info *list[2] = { &global_curl_info, NULL }; @@ -303,38 +303,78 @@ AST_LIST_TRAVERSE(list[i], cur, list) { if (cur->key == key) { if (ot == OT_BOOLEAN || ot == OT_INTEGER) { - snprintf(buf, len, "%ld", (long)cur->value); + if (buf) { + snprintf(buf, len, "%ld", (long) cur->value); + } else { + ast_str_set(bufstr, len, "%ld", (long) cur->value); + } } else if (ot == OT_INTEGER_MS) { - if ((long)cur->value % 1000 == 0) { - snprintf(buf, len, "%ld", (long)cur->value / 1000); + if ((long) cur->value % 1000 == 0) { + if (buf) { + snprintf(buf, len, "%ld", (long)cur->value / 1000); + } else { + ast_str_set(bufstr, len, "%ld", (long) cur->value / 1000); + } } else { - snprintf(buf, len, "%.3f", (double)((long)cur->value) / 1000.0); + if (buf) { + snprintf(buf, len, "%.3f", (double) ((long) cur->value) / 1000.0); + } else { + ast_str_set(bufstr, len, "%.3f", (double) ((long) cur->value) / 1000.0); + } } } else if (ot == OT_STRING) { ast_debug(1, "Found entry %p, with key %d and value %p\n", cur, cur->key, cur->value); - ast_copy_string(buf, cur->value, len); + if (buf) { + ast_copy_string(buf, cur->value, len); + } else { + ast_str_set(bufstr, 0, "%s", (char *) cur->value); + } } else if (key == CURLOPT_PROXYTYPE) { if (0) { #if CURLVERSION_ATLEAST(7,15,2) } else if ((long)cur->value == CURLPROXY_SOCKS4) { - ast_copy_string(buf, "socks4", len); + if (buf) { + ast_copy_string(buf, "socks4", len); + } else { + ast_str_set(bufstr, 0, "socks4"); + } #endif #if CURLVERSION_ATLEAST(7,18,0) } else if ((long)cur->value == CURLPROXY_SOCKS4A) { - ast_copy_string(buf, "socks4a", len); + if (buf) { + ast_copy_string(buf, "socks4a", len); + } else { + ast_str_set(bufstr, 0, "socks4a"); + } #endif } else if ((long)cur->value == CURLPROXY_SOCKS5) { - ast_copy_string(buf, "socks5", len); + if (buf) { + ast_copy_string(buf, "socks5", len); + } else { + ast_str_set(bufstr, 0, "socks5"); + } #if CURLVERSION_ATLEAST(7,18,0) } else if ((long)cur->value == CURLPROXY_SOCKS5_HOSTNAME) { - ast_copy_string(buf, "socks5hostname", len); + if (buf) { + ast_copy_string(buf, "socks5hostname", len); + } else { + ast_str_set(bufstr, 0, "socks5hostname"); + } #endif #if CURLVERSION_ATLEAST(7,10,0) } else if ((long)cur->value == CURLPROXY_HTTP) { - ast_copy_string(buf, "http", len); + if (buf) { + ast_copy_string(buf, "http", len); + } else { + ast_str_set(bufstr, 0, "http"); + } #endif } else { - ast_copy_string(buf, "unknown", len); + if (buf) { + ast_copy_string(buf, "unknown", len); + } else { + ast_str_set(bufstr, 0, "unknown"); + } } } break; @@ -349,6 +389,16 @@ return cur ? 0 : -1; } +static int acf_curlopt_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) +{ + return acf_curlopt_helper(chan, cmd, data, buf, NULL, len); +} + +static int acf_curlopt_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len) +{ + return acf_curlopt_helper(chan, cmd, data, NULL, buf, len); +} + static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data) { register int realsize = size * nmemb; @@ -363,7 +413,7 @@ return realsize; } -static const char *global_useragent = "asterisk-libcurl-agent/1.0"; +static const char * const global_useragent = "asterisk-libcurl-agent/1.0"; static int curl_instance_init(void *data) { @@ -391,7 +441,7 @@ AST_THREADSTORAGE_CUSTOM(curl_instance, curl_instance_init, curl_instance_cleanup); -static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info, char *buf, size_t len) +static int acf_curl_helper(struct ast_channel *chan, const char *cmd, char *info, char *buf, struct ast_str **input_str, ssize_t len) { struct ast_str *str = ast_str_create(16); int ret = -1; @@ -405,15 +455,17 @@ int hashcompat = 0; AST_LIST_HEAD(global_curl_info, curl_settings) *list = NULL; - *buf = '\0'; - + if (buf) { + *buf = '\0'; + } + if (ast_strlen_zero(info)) { ast_log(LOG_WARNING, "CURL requires an argument (URL)\n"); ast_free(str); return -1; } - AST_STANDARD_APP_ARGS(args, info); + AST_STANDARD_APP_ARGS(args, info); if (chan) { ast_autoservice_start(chan); @@ -483,11 +535,19 @@ rowcount++; } pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(fields)); - ast_copy_string(buf, ast_str_buffer(values), len); + if (buf) { + ast_copy_string(buf, ast_str_buffer(values), len); + } else { + ast_str_set(input_str, len, "%s", ast_str_buffer(values)); + } ast_free(fields); ast_free(values); } else { - ast_copy_string(buf, ast_str_buffer(str), len); + if (buf) { + ast_copy_string(buf, ast_str_buffer(str), len); + } else { + ast_str_set(input_str, len, "%s", ast_str_buffer(str)); + } } ret = 0; } @@ -495,11 +555,21 @@ if (chan) ast_autoservice_stop(chan); - + return ret; } -struct ast_custom_function acf_curl = { +static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info, char *buf, size_t len) +{ + return acf_curl_helper(chan, cmd, info, buf, NULL, len); +} + +static int acf_curl2_exec(struct ast_channel *chan, const char *cmd, char *info, struct ast_str **buf, ssize_t len) +{ + return acf_curl_helper(chan, cmd, info, NULL, buf, len); +} + +static struct ast_custom_function acf_curl = { .name = "CURL", .synopsis = "Retrieves the contents of a URL", .syntax = "CURL(url[,post-data])", @@ -507,9 +577,10 @@ " url - URL to retrieve\n" " post-data - Optional data to send as a POST (GET is default action)\n", .read = acf_curl_exec, + .read2 = acf_curl2_exec, }; -struct ast_custom_function acf_curlopt = { +static struct ast_custom_function acf_curlopt = { .name = "CURLOPT", .synopsis = "Set options for use with the CURL() function", .syntax = "CURLOPT(