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; } - - - /*********************************************