From 59286cfbc1fe8b4b1362cc5bb7e4965087ee3bb7 Mon Sep 17 00:00:00 2001 From: Admin Commit Date: Thu, 20 Jan 2005 04:02:58 +0000 Subject: [PATCH] This commit was manufactured by cvs2svn to create tag 'v1-0-4'. git-svn-id: https://origsvn.digium.com/svn/asterisk/tags/v1-0-4@4850 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- CHANGES | 57 ++- CREDITS | 2 + Makefile | 8 +- acl.c | 7 +- app.c | 17 +- apps/app_alarmreceiver.c | 2 +- apps/app_chanisavail.c | 11 +- apps/app_dial.c | 13 +- apps/app_festival.c | 6 +- apps/app_forkcdr.c | 5 +- apps/app_getcpeid.c | 10 +- apps/app_hasnewvoicemail.c | 30 +- apps/app_macro.c | 3 +- apps/app_meetme.c | 18 +- apps/app_parkandannounce.c | 2 +- apps/app_queue.c | 23 +- apps/app_read.c | 3 +- apps/app_record.c | 2 +- apps/app_sms.c | 13 +- apps/app_test.c | 13 +- apps/app_userevent.c | 2 +- apps/app_voicemail.c | 353 ++++++++++------- ast_expr.y | 68 ++-- asterisk.c | 48 ++- callerid.c | 2 + cdr.c | 11 +- cdr/cdr_csv.c | 8 +- cdr/cdr_odbc.c | 253 +++++------- cdr/cdr_pgsql.c | 25 +- channel.c | 19 +- channels/chan_agent.c | 21 +- channels/chan_alsa.c | 2 +- channels/chan_h323.c | 28 +- channels/chan_iax2.c | 131 +++++-- channels/chan_local.c | 7 +- channels/chan_mgcp.c | 117 +++--- channels/chan_modem.c | 15 +- channels/chan_phone.c | 4 + channels/chan_sip.c | 715 ++++++++++++++++++++-------------- channels/chan_skinny.c | 2 +- channels/chan_zap.c | 89 +++-- channels/h323/.cvsignore | 1 + channels/h323/Makefile | 17 +- channels/h323/ast_h323.cpp | 6 +- channels/h323/chan_h323.h | 2 +- channels/iax2-parser.c | 2 +- cli.c | 43 +- codecs/Makefile | 3 +- codecs/gsm/Makefile | 4 +- configs/agents.conf.sample | 4 +- configs/queues.conf.sample | 11 +- configs/sip.conf.sample | 13 +- configs/zapata.conf.sample | 7 + contrib/README.festival | 17 + contrib/firmware/iax/iaxy.bin | Bin 39241 -> 39386 bytes db1-ast/hash/ndbm.c | 50 ++- doc/README.iax | 71 +++- doc/README.variables | 4 +- doc/cdr.txt | 2 +- dsp.c | 13 +- formats/format_wav_gsm.c | 2 +- frame.c | 453 +++++++++++++++------ include/asterisk/cdr.h | 2 +- include/asterisk/channel.h | 4 + include/asterisk/frame.h | 38 +- loader.c | 19 +- logger.c | 21 +- pbx.c | 45 ++- res/res_agi.c | 12 +- res/res_config_odbc.c | 2 +- res/res_crypto.c | 3 +- res/res_features.c | 9 + res/res_indications.c | 15 +- res/res_monitor.c | 61 +-- res/res_musiconhold.c | 10 +- res/res_odbc.c | 116 ++++-- rtp.c | 39 +- say.c | 36 +- sounds.txt | 2 + sounds/vm-saveoper.gsm | Bin 0 -> 16533 bytes tdd.c | 2 +- utils/astman.c | 683 -------------------------------- 82 files changed, 2114 insertions(+), 1895 deletions(-) create mode 100755 channels/h323/.cvsignore create mode 100755 sounds/vm-saveoper.gsm delete mode 100755 utils/astman.c diff --git a/CHANGES b/CHANGES index 066275c0a9..7ec5e1285b 100755 --- a/CHANGES +++ b/CHANGES @@ -1,8 +1,63 @@ + NOTE: Corrections or additions to the ChangeLog may be submitted + to http://bugs.digium.com. A complete listing of changes + is available through the Asterisk-CVS mailing list hosted + at http://lists.digium.com. + +Asterisk 1.0.4 + -- general + -- fix memory leak evident with extensive use of variables + -- update IAXy firmware to version 22 + -- enable some special write protection + -- enable outbound DTMF + -- fix seg fault with incorrect usage of SetVar + -- other minor fixes including typos and doc updates + -- chan_sip + -- fix codecs to not be case sensitive + -- Re-use auth credentials + -- fix MWI when using type=friend + -- fix global NAT option + -- chan_agent / chan_local + -- fix incorrect use count + -- chan_zap + -- Allow CID rings to be configured in zapata.conf + -- no more patching needed for UK CID + -- app_macro + -- allow Macros to exit with '*' or '#' like regular extension processing + -- app_voicemail + -- don't allow '#' as a password + -- add option to save voicemail before going to the operator + -- fix global operator=yes + -- app_read + -- return 0 instead of -1 if user enters nothing + -- res_agi + -- don't exit AGI when file not found to stream + -- send script parameter when using FastAGI +Asterisk 1.0.3 + -- chan_zap + -- fix seg fault when doing *0 to flash a trunk + -- rtp + -- seg fault fix + -- chan_sip + -- fix to prevent seg fault when attempting a transfer + -- fix bug with supervised transfers + -- fix codec preferences + -- chan_h323 + -- fix compilation problem + -- chan_iax2 + -- avoid a deadlock related to a static config of a BUNCH of peers + -- cdr_pgsql + -- fix memory leak when reading config + -- Numerous other minor bug fixes +Asterisk 1.0.2 + -- Major bugfix release +Asterisk 1.0.1 + -- Added AGI over TCP support + -- Add ability to purge callers from queue if no agents are logged in -- Fix inband PRI indication detection -- Fix for MGCP - always request digits if no RTP stream -- Fixed seg fault for ast_control_streamfile - -- Added AGI over TCP support -- Make pick-up extension configurable via features.conf + -- Numerous other bug fixes Asterisk 1.0.0 -- Use Q.931 standard cause codes for asterisk cause codes -- Bug fixes from the bug tracker diff --git a/CREDITS b/CREDITS index 8b36cfc3cd..11552bdeca 100755 --- a/CREDITS +++ b/CREDITS @@ -58,6 +58,8 @@ Thorsten Lockert - OpenBSD, FreeBSD ports, making MacOS X port run on 10.3, bugs. tholo@sigmasoft.com Brian West - ODBC support and Bug Marshaling William Waites - syslog support, SIP NAT traversal for SIP-UA. ww@styx.org +Rich Murphey - Porting to FreeBSD, NetBSD, OpenBSD, and Darwin. + rich@whiteoaklabs.com http://whiteoaklabs.com === OTHER CONTRIBUTIONS === John Todd - Monkey sounds and associated teletorture prompt diff --git a/Makefile b/Makefile index 5a6a51c49e..652afb37ab 100755 --- a/Makefile +++ b/Makefile @@ -138,15 +138,15 @@ CFLAGS+=$(shell if [ -f /usr/include/osp/osp.h ]; then echo "-DOSP_SUPPORT -I/us ifeq (${OSARCH},FreeBSD) OSVERSION=$(shell make -V OSVERSION -f /usr/share/mk/bsd.port.subdir.mk) -CFLAGS+=$(if ${OSVERSION}<500016,-D_THREAD_SAFE) -LIBS+=$(if ${OSVERSION}<502102,-lc_r,-pthread) +CFLAGS+=$(shell if test ${OSVERSION} -lt 500016 ; then echo "-D_THREAD_SAFE"; fi) +LIBS+=$(shell if test ${OSVERSION} -lt 502102 ; then echo "-lc_r"; else echo "-pthread"; fi) INCLUDE+=-I/usr/local/include CFLAGS+=$(shell if [ -d /usr/local/include/spandsp ]; then echo "-I/usr/local/include/spandsp"; fi) endif # FreeBSD ifeq (${OSARCH},NetBSD) CFLAGS+=-pthread -INCLUDE+=-I/usr/local/include +INCLUDE+=-I/usr/local/include -I/usr/pkg/include endif ifeq (${OSARCH},OpenBSD) @@ -197,7 +197,7 @@ ifeq (${OSARCH},FreeBSD) LIBS+=-lcrypto endif ifeq (${OSARCH},NetBSD) -LIBS+=-lpthread -lcrypto -lm -L/usr/local/lib -lncurses +LIBS+=-lpthread -lcrypto -lm -L/usr/local/lib -L/usr/pkg/lib -lncurses endif ifeq (${OSARCH},OpenBSD) LIBS=-lcrypto -lpthread -lm -lncurses diff --git a/acl.c b/acl.c index 6c460969fa..935ec808da 100755 --- a/acl.c +++ b/acl.c @@ -242,20 +242,19 @@ int ast_ouraddrfor(struct in_addr *them, struct in_addr *us) memset(&m_rtmsg, 0, sizeof(m_rtmsg)); m_rtmsg.m_rtm.rtm_type = RTM_GET; - m_rtmsg.m_rtm.rtm_flags = RTF_UP | RTF_HOST; m_rtmsg.m_rtm.rtm_version = RTM_VERSION; ast_mutex_lock(&routeseq_lock); seq = ++routeseq; ast_mutex_unlock(&routeseq_lock); m_rtmsg.m_rtm.rtm_seq = seq; - m_rtmsg.m_rtm.rtm_addrs = RTA_IFA | RTA_DST; + m_rtmsg.m_rtm.rtm_addrs = RTA_DST | RTA_IFA; m_rtmsg.m_rtm.rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in); sin = (struct sockaddr_in *)m_rtmsg.m_space; sin->sin_family = AF_INET; sin->sin_len = sizeof(struct sockaddr_in); sin->sin_addr = *them; - if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) { + if ((s = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC)) < 0) { ast_log(LOG_ERROR, "Error opening routing socket\n"); return -1; } @@ -268,7 +267,7 @@ int ast_ouraddrfor(struct in_addr *them, struct in_addr *us) } do { l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); - } while (l > 0 && (m_rtmsg.m_rtm.rtm_seq != 1 || m_rtmsg.m_rtm.rtm_pid != pid)); + } while (l > 0 && (m_rtmsg.m_rtm.rtm_seq != seq || m_rtmsg.m_rtm.rtm_pid != pid)); if (l < 0) { if (errno != EAGAIN) ast_log(LOG_ERROR, "Error reading from routing socket\n"); diff --git a/app.c b/app.c index f653aa909b..61cb63cd4c 100755 --- a/app.c +++ b/app.c @@ -706,10 +706,12 @@ int ast_play_and_record(struct ast_channel *chan, char *playfile, char *recordfi for (x=0;x 0) { + if (totalsilence) + ast_stream_rewind(others[x], totalsilence-200); + else + ast_stream_rewind(others[x], 200); + } ast_truncstream(others[x]); ast_closestream(others[x]); } @@ -718,14 +720,11 @@ int ast_play_and_record(struct ast_channel *chan, char *playfile, char *recordfi ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name); } } - if (outmsg) { - if (outmsg > 1) { + if (outmsg > 1) { /* Let them know recording is stopped */ - ast_streamfile(chan, "auth-thankyou", chan->language); + if(!ast_streamfile(chan, "auth-thankyou", chan->language)) ast_waitstream(chan, ""); - } } - return res; } diff --git a/apps/app_alarmreceiver.c b/apps/app_alarmreceiver.c index 28d28b4c43..5524230368 100755 --- a/apps/app_alarmreceiver.c +++ b/apps/app_alarmreceiver.c @@ -55,7 +55,7 @@ static char *tdesc = "Alarm Receiver for Asterisk"; static char *app = "AlarmReceiver"; -static char *synopsis = "Provide support for receving alarm reports from a burglar or fire alarm panel\n"; +static char *synopsis = "Provide support for receving alarm reports from a burglar or fire alarm panel"; static char *descrip = "Alarm receiver application for Asterisk. Only 1 signalling format is supported at this time:\n" "Ademco Contact ID. This application should be called whenever there is an alarm panel calling in\n" diff --git a/apps/app_chanisavail.c b/apps/app_chanisavail.c index 4438663ce1..07ea55c798 100755 --- a/apps/app_chanisavail.c +++ b/apps/app_chanisavail.c @@ -38,11 +38,12 @@ static char *descrip = "Checks is any of the requested channels are available. If none\n" "of the requested channels are available the new priority will be\n" "n+101 (unless such a priority does not exist or on error, in which\n" -"case ChanIsAvail will return -1). If any of the requested channels\n" -"are available, the next priority will be n+1, the channel variable\n" -"${AVAILCHAN} will be set to the name of the available channel and\n" -"the ChanIsAvail app will return 0. ${AVAILORIGCHAN} is\n" -"the canonical channel name that was used to create the channel.\n"; +"case ChanIsAvail will return -1).\n" +"If any of the requested channels are available, the next priority will be n+1,\n" +"the channel variable ${AVAILCHAN} will be set to the name of the available channel\n" +"and the ChanIsAvail app will return 0.\n" +"${AVAILORIGCHAN} is the canonical channel name that was used to create the channel.\n" +"${AVAILSTATUS} is the status code for the channel.\n"; STANDARD_LOCAL_USER; diff --git a/apps/app_dial.c b/apps/app_dial.c index 7f3c3d7682..ce50a10da9 100755 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -281,6 +281,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localu ast_hangup(o->chan); o->chan = NULL; numnochan++; + } else { + /* After calling, set callerid to extension */ + ast_set_callerid(o->chan, ast_strlen_zero(in->macroexten) ? in->exten : in->macroexten, 0); } } /* Hangup the original channel now, in case we needed it */ @@ -336,7 +339,8 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localu case AST_CONTROL_PROGRESS: if (option_verbose > 2) ast_verbose ( VERBOSE_PREFIX_3 "%s is making progress passing it to %s\n", o->chan->name,in->name); - ast_indicate(in, AST_CONTROL_PROGRESS); + if (!outgoing->ringbackonly) + ast_indicate(in, AST_CONTROL_PROGRESS); break; case AST_CONTROL_OFFHOOK: /* Ignore going off hook */ @@ -383,6 +387,8 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localu /* Got hung up */ *to=-1; strncpy(status, "CANCEL", statussize - 1); + if (f) + ast_frfree(f); return NULL; } if (f && (f->frametype == AST_FRAME_DTMF) && *allowdisconnect_out && @@ -391,6 +397,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localu ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass); *to=0; strcpy(status, "CANCEL"); + ast_frfree(f); return NULL; } if (single && ((f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_DTMF))) { @@ -841,9 +848,11 @@ static int dial_exec(struct ast_channel *chan, void *data) free(tmp); cur = rest; continue; - } else + } else { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", numsubst); + ast_set_callerid(tmp->chan, ast_strlen_zero(chan->macroexten) ? chan->exten : chan->macroexten, 0); + } /* Put them in the list of outgoing thingies... We're ready now. XXX If we're forcibly removed, these outgoing calls won't get hung up XXX */ diff --git a/apps/app_festival.c b/apps/app_festival.c index e7e0bd4269..7b6f68634e 100755 --- a/apps/app_festival.c +++ b/apps/app_festival.c @@ -305,9 +305,9 @@ static int festival_exec(struct ast_channel *chan, void *vdata) if (!(festivalcommand = ast_variable_retrieve(cfg, "general", "festivalcommand"))) { festivalcommand = "(tts_textasterisk \"%s\" 'file)(quit)\n"; } - ast_destroy(cfg); if (!vdata || ast_strlen_zero(vdata)) { ast_log(LOG_WARNING, "festival requires an argument (text)\n"); + ast_destroy(cfg); return -1; } strncpy(data, vdata, sizeof(data) - 1); @@ -325,6 +325,7 @@ static int festival_exec(struct ast_channel *chan, void *vdata) if (fd < 0) { ast_log(LOG_WARNING,"festival_client: can't get socket\n"); + ast_destroy(cfg); return -1; } memset(&serv_addr, 0, sizeof(serv_addr)); @@ -333,6 +334,7 @@ static int festival_exec(struct ast_channel *chan, void *vdata) serverhost = ast_gethostbyname(host, &ahp); if (serverhost == (struct hostent *)0) { ast_log(LOG_WARNING,"festival_client: gethostbyname failed\n"); + ast_destroy(cfg); return -1; } memmove(&serv_addr.sin_addr,serverhost->h_addr, serverhost->h_length); @@ -342,6 +344,7 @@ static int festival_exec(struct ast_channel *chan, void *vdata) if (connect(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) != 0) { ast_log(LOG_WARNING,"festival_client: connect to server failed\n"); + ast_destroy(cfg); return -1; } @@ -444,6 +447,7 @@ static int festival_exec(struct ast_channel *chan, void *vdata) } } while (strcmp(ack,"OK\n") != 0); close(fd); + ast_destroy(cfg); LOCAL_USER_REMOVE(u); return res; diff --git a/apps/app_forkcdr.c b/apps/app_forkcdr.c index 547bfa45ea..bf493d5231 100755 --- a/apps/app_forkcdr.c +++ b/apps/app_forkcdr.c @@ -23,7 +23,8 @@ static char *tdesc = "Fork The CDR into 2 seperate entities."; static char *app = "ForkCDR"; static char *synopsis = -"Forks the Call Data Record\n" +"Forks the Call Data Record"; +static char *descrip = " ForkCDR(): Causes the Call Data Record to fork an additional\n" "cdr record starting from the time of the fork call\n"; @@ -68,7 +69,7 @@ int unload_module(void) int load_module(void) { - return ast_register_application(app, forkcdr_exec, synopsis, tdesc); + return ast_register_application(app, forkcdr_exec, synopsis, descrip); } char *description(void) diff --git a/apps/app_getcpeid.c b/apps/app_getcpeid.c index d0a0be6bcd..8ebd568e8a 100755 --- a/apps/app_getcpeid.c +++ b/apps/app_getcpeid.c @@ -3,9 +3,9 @@ * * Execute arbitrary system commands * - * Copyright (C) 1999, Mark Spencer + * Copyright (C) 1999-2005, Digium * - * Mark Spencer + * Mark Spencer * * This program is free software, distributed under the terms of * the GNU General Public License @@ -31,9 +31,9 @@ static char *app = "GetCPEID"; static char *synopsis = "Get ADSI CPE ID"; static char *descrip = -" GetCPEID: Obtains and displays CPE ID and other information in order to\n" -"properly setup zapata.conf for on-hook operations. Returns -1 on hanup\n" -"only."; +" GetCPEID: Obtains and displays ADSI CPE ID and other information in order\n" +"to properly setup zapata.conf for on-hook operations.\n" +"Returns -1 on hangup only.\n"; STANDARD_LOCAL_USER; diff --git a/apps/app_hasnewvoicemail.c b/apps/app_hasnewvoicemail.c index 733a65ec81..e74739653d 100755 --- a/apps/app_hasnewvoicemail.c +++ b/apps/app_hasnewvoicemail.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -70,7 +71,7 @@ static int hasvoicemail_exec(struct ast_channel *chan, void *data) { int res=0; struct localuser *u; - char vmpath[256], *input, *varname = NULL, *vmbox, *vmfolder = "INBOX", *context = "default"; + char vmpath[256], *temps, *input, *varname = NULL, *vmbox, *vmfolder = "INBOX", *context = "default"; DIR *vmdir; struct dirent *vment; int vmcount = 0; @@ -83,21 +84,22 @@ static int hasvoicemail_exec(struct ast_channel *chan, void *data) input = ast_strdupa((char *)data); if (input) { - if ((vmbox = strsep(&input,":"))) - if ((vmfolder = strsep(&input,"|"))) + temps = input; + if ((temps = strsep(&input, "|"))) { + if (input && !ast_strlen_zero(input)) varname = input; - else - vmfolder = input; - else - if ((vmbox = strsep(&input,"|"))) - varname = input; - else - vmbox = input; - - if (index(vmbox,'@')) { - context = vmbox; - vmbox = strsep(&context,"@"); + input = temps; } + if ((temps = strsep(&input, ":"))) { + if (input && !ast_strlen_zero(input)) + vmfolder = input; + input = temps; + } + if ((vmbox = strsep(&input, "@"))) + if (input && !ast_strlen_zero(input)) + context = input; + if (!vmbox) + vmbox = input; snprintf(vmpath,sizeof(vmpath), "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR, context, vmbox, vmfolder); if (!(vmdir = opendir(vmpath))) { diff --git a/apps/app_macro.c b/apps/app_macro.c index ea6775c90d..03da13ed43 100755 --- a/apps/app_macro.c +++ b/apps/app_macro.c @@ -140,7 +140,8 @@ static int macro_exec(struct ast_channel *chan, void *data) while(ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->callerid)) { if ((res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->callerid))) { /* Something bad happened, or a hangup has been requested. */ - if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F'))) { + if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) || + (res == '*') || (res == '#')) { /* Just return result as to the previous application as if it had been dialed */ ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res); break; diff --git a/apps/app_meetme.c b/apps/app_meetme.c index ef2ff3de91..ae9846871b 100755 --- a/apps/app_meetme.c +++ b/apps/app_meetme.c @@ -524,7 +524,9 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c memset(user, 0, sizeof(struct ast_conf_user)); user->user_no = 0; /* User number 0 means starting up user! (dead - not in the list!) */ - + + time(&user->jointime); + if (conf->locked) { /* Sorry, but this confernce is locked! */ if (!ast_streamfile(chan, "conf-locked", chan->language)) @@ -557,7 +559,6 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c conf->lastuser = user; } } - strncpy(user->usrvalue, "test", sizeof(user->usrvalue) - 1); user->chan = chan; user->userflags = confflags; user->adminflags = 0; @@ -1043,10 +1044,10 @@ outrun: else ast_log(LOG_ERROR, "Bad! Bad! Bad! user->prevuser is NULL but we're not the beginning!\n"); } - /* Return the number of seconds the user was in the conf */ - snprintf(meetmesecs, sizeof(meetmesecs), "%i", (int) (user->jointime - time(NULL))); - pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs); } + /* Return the number of seconds the user was in the conf */ + snprintf(meetmesecs, sizeof(meetmesecs), "%i", (int) (time(NULL) - user->jointime)); + pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs); } free(user); ast_mutex_unlock(&conflock); @@ -1454,14 +1455,17 @@ static int admin_exec(struct ast_channel *chan, void *data) { command = strsep(¶ms, "|"); caller = strsep(¶ms, "|"); - ast_mutex_lock(&conflock); + if (!command) { + ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n"); + ast_mutex_unlock(&conflock); + return -1; + } cnf = confs; while (cnf) { if (strcmp(cnf->confno, conf) == 0) break; cnf = cnf->next; } - ast_mutex_unlock(&conflock); if (caller) user = find_user(cnf, caller); diff --git a/apps/app_parkandannounce.c b/apps/app_parkandannounce.c index c3f949ec32..bb8a7fb899 100755 --- a/apps/app_parkandannounce.c +++ b/apps/app_parkandannounce.c @@ -94,7 +94,7 @@ static int parkandannounce_exec(struct ast_channel *chan, void *data) } dial=strsep(&s, "|"); if(!dial) { - ast_log(LOG_WARNING, "PARK: A dial resouce must be specified i.e: Console/dsp or Zap/g1/5551212\n"); + ast_log(LOG_WARNING, "PARK: A dial resource must be specified i.e: Console/dsp or Zap/g1/5551212\n"); free(orig_s); return -1; } else { diff --git a/apps/app_queue.c b/apps/app_queue.c index 542ede2463..f48bd963d1 100755 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -226,6 +226,7 @@ struct ast_call_queue { int wrapped; /* Round Robin - wrapped around? */ int joinempty; /* Do we care if the queue has no members? */ int eventwhencalled; /* Generate an event when the agent is called (before pickup) */ + int leavewhenempty; /* If all agents leave the queue, remove callers from the queue */ struct member *members; /* Member channels to be tried */ struct queue_ent *head; /* Start of the actual queue */ @@ -912,6 +913,12 @@ static int wait_our_turn(struct queue_ent *qe, int ringing) break; } + /* leave the queue if no agents, if enabled */ + if (!(qe->parent->members) && qe->parent->leavewhenempty) { + leave_queue(qe); + break; + } + /* Make a position announcement, if enabled */ if (qe->parent->announcefrequency && !ringing) say_position(qe); @@ -1626,7 +1633,7 @@ check_turns: /* This is the wait loop for the head caller*/ /* To exit, they may get their call answered; */ /* they may dial a digit from the queue context; */ - /* or, they may may timeout. */ + /* or, they may timeout. */ /* Leave if we have exceeded our queuetimeout */ if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) { @@ -1634,6 +1641,12 @@ check_turns: break; } + /* leave the queue if no agents, if enabled */ + if (!((qe.parent)->members) && (qe.parent)->leavewhenempty) { + leave_queue(&qe); + break; + } + /* Make a position announcement, if enabled */ if (qe.parent->announcefrequency && !ringing) say_position(&qe); @@ -1691,7 +1704,7 @@ check_turns: } } /* Don't allow return code > 0 */ - if (res > 0 && res != AST_PBX_KEEPALIVE) { + if ((res == 0) || (res > 0 && res != AST_PBX_KEEPALIVE)) { res = 0; if (ringing) { ast_indicate(chan, -1); @@ -1781,7 +1794,7 @@ static void reload_queues(void) strncpy(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes) - 1); strncpy(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds) - 1); strncpy(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks) - 1); - strncpy(q->sound_lessthan, "queue-lessthan", sizeof(q->sound_lessthan) - 1); + strncpy(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan) - 1); prev = q->members; if (prev) { /* find the end of any dynamic members */ @@ -1818,7 +1831,7 @@ static void reload_queues(void) q->members = cur; prev = cur; } - } else if (!strcasecmp(var->name, "music")) { + } else if (!strcasecmp(var->name, "music") || !strcasecmp(var->name, "musiconhold")) { strncpy(q->moh, var->value, sizeof(q->moh) - 1); } else if (!strcasecmp(var->name, "announce")) { strncpy(q->announce, var->value, sizeof(q->announce) - 1); @@ -1872,6 +1885,8 @@ static void reload_queues(void) } } else if (!strcasecmp(var->name, "joinempty")) { q->joinempty = ast_true(var->value); + } else if (!strcasecmp(var->name, "leavewhenempty")) { + q->leavewhenempty = ast_true(var->value); } else if (!strcasecmp(var->name, "eventwhencalled")) { q->eventwhencalled = ast_true(var->value); } else { diff --git a/apps/app_read.c b/apps/app_read.c index 988ef8009e..e915138457 100755 --- a/apps/app_read.c +++ b/apps/app_read.c @@ -106,8 +106,9 @@ static int read_exec(struct ast_channel *chan, void *data) if (res > -1) { pbx_builtin_setvar_helper(chan, varname, tmp); ast_verbose(VERBOSE_PREFIX_3 "User entered '%s'\n", tmp); + res = 0; } else { - ast_verbose(VERBOSE_PREFIX_3 "User entered nothing\n"); + ast_verbose(VERBOSE_PREFIX_3 "User disconnected\n"); } } LOCAL_USER_REMOVE(u); diff --git a/apps/app_record.c b/apps/app_record.c index c1f06780a2..7de7016c0f 100755 --- a/apps/app_record.c +++ b/apps/app_record.c @@ -301,7 +301,7 @@ static int record_exec(struct ast_channel *chan, void *data) ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name); LOCAL_USER_REMOVE(u); - if (silence > 0) { + if ((silence > 0) && rfmt) { res = ast_set_read_format(chan, rfmt); if (res) ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); diff --git a/apps/app_sms.c b/apps/app_sms.c index a7b55bd463..882236822c 100755 --- a/apps/app_sms.c +++ b/apps/app_sms.c @@ -26,6 +26,7 @@ #include #include #include +#include "../astconf.h" /* ToDo */ /* When acting as SC and answering, should check for messages and send instead of sending EST as first packet */ @@ -36,6 +37,9 @@ static unsigned char message_ref; /* arbitary message ref */ +static char log_file[255]; +static char spool_dir[255]; + static char *tdesc = "SMS/PSTN handler"; static char *app = "SMS"; @@ -315,7 +319,7 @@ sms_log (sms_t * h, char status) { /* log the output, and remove file */ if (*h->oa || *h->da) { - int o = open ("/var/log/asterisk/sms", O_CREAT | O_APPEND | O_WRONLY, 0666); + int o = open (log_file, O_CREAT | O_APPEND | O_WRONLY, 0666); if (o >= 0) { char line[1000], *p; @@ -517,7 +521,7 @@ sms_writefile (sms_t * h) char fn2[200] = ""; FILE *o; - strncpy(fn, "/var/spool/asterisk/sms", sizeof(fn) - 1); + strncpy(fn, spool_dir, sizeof(fn) - 1); mkdir (fn, 0777); /* ensure it exists */ snprintf(fn + strlen(fn), sizeof(fn) - strlen(fn), "/%s.%s", h->smsc ? "me-sc" : "sc-me", h->queue); mkdir (fn, 0777); /* ensure it exists */ @@ -689,7 +693,7 @@ sms_nextoutgoing (sms_t * h) DIR *d; char more = 0; - strncpy(fn, "/var/spool/asterisk/sms", sizeof(fn) - 1); + strncpy(fn, spool_dir, sizeof(fn) - 1); mkdir(fn, 0777); /* ensure it exists */ snprintf(fn + strlen (fn), sizeof(fn) - strlen(fn), "/%s.%s", h->smsc ? "sc-me" : "me-sc", h->queue); mkdir (fn, 0777); /* ensure it exists */ @@ -769,6 +773,7 @@ sms_nextoutgoing (sms_t * h) { /* no message */ h->omsg[0] = 0x94; /* SMS_REL */ h->omsg[1] = 0; + h->hangup = 1; sms_messagetx (h); } } @@ -1204,6 +1209,8 @@ load_module (void) for (p = 0; p < 128; p++) sms8to7[sms7to8[p]] = p; } + snprintf(log_file, sizeof(log_file), "%s/sms", ast_config_AST_LOG_DIR); + snprintf(spool_dir, sizeof(spool_dir), "%s/sms", ast_config_AST_SPOOL_DIR); return ast_register_application (app, sms_exec, synopsis, descrip); } diff --git a/apps/app_test.c b/apps/app_test.c index 3743b67713..702dbdc3f2 100755 --- a/apps/app_test.c +++ b/apps/app_test.c @@ -1,7 +1,7 @@ /* * Asterisk -- A telephony toolkit for Linux. * - * Applictions connected with CDR engine + * Applications to test connection and produce report in text file * * Copyright (C) 2004, Digium, Inc. * @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -29,13 +30,15 @@ static char *tdesc = "Interface Test Application"; -static char *tests_descrip = "TestServer(): Perform test server function and write call report" - "Results stored in /var/log/asterisk/testreports/.txt"; +static char *tests_descrip = + "TestServer(): Perform test server function and write call report.\n" + "Results stored in /var/log/asterisk/testreports/-server.txt"; static char *tests_app = "TestServer"; static char *tests_synopsis = "Execute Interface Test Server"; -static char *testc_descrip = "TestClient(testid): Executes test client with given testid.\n" - "Results stored in /var/log/asterisk/testreports/.txt"; +static char *testc_descrip = + "TestClient(testid): Executes test client with given testid.\n" + "Results stored in /var/log/asterisk/testreports/-client.txt"; static char *testc_app = "TestClient"; static char *testc_synopsis = "Execute Interface Test Client"; diff --git a/apps/app_userevent.c b/apps/app_userevent.c index e1a87c2a4a..82de783b32 100755 --- a/apps/app_userevent.c +++ b/apps/app_userevent.c @@ -54,12 +54,12 @@ static int userevent_exec(struct ast_channel *chan, void *data) } strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1); + snprintf(eventname, sizeof(eventname), "UserEvent%s", info); eventbody = strchr(eventname, '|'); if (eventbody) { *eventbody = '\0'; eventbody++; } - snprintf(eventname, sizeof(eventname), "UserEvent%s", info); LOCAL_USER_ADD(u); if(eventbody) { diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c index fe13a7fca6..391130eaa8 100755 --- a/apps/app_voicemail.c +++ b/apps/app_voicemail.c @@ -178,6 +178,7 @@ static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, s static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context); static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration); static int vm_delete(char *file); +static int vm_play_folder_name(struct ast_channel *chan, char *mbox); static char ext_pass_cmd[128]; @@ -215,7 +216,10 @@ static char *descrip_vmain = "for the checking of voicemail. The mailbox can be passed as the option,\n" "which will stop the voicemail system from prompting the user for the mailbox.\n" "If the mailbox is preceded by 's' then the password check will be skipped. If\n" -"a context is specified, logins are considered in that voicemail context only.\n" +"the mailbox is preceded by 'p' then the supplied mailbox is prepended to the\n" +"user's entry and the resulting string is used as the mailbox number. This is\n" +"useful for virtual hosting of voicemail boxes. If a context is specified,\n" +"logins are considered in that voicemail context only.\n" "Returns -1 if the user hangs up or 0 otherwise.\n"; static char *synopsis_vm_box_exists = @@ -705,6 +709,18 @@ static int make_file(char *dest, int len, char *dir, int num) return snprintf(dest, len, "%s/msg%04d", dir, num); } +static int last_message_index(char *dir) +{ + int x; + char fn[256]; + for (x=0;xemail, mailcmd); } else { ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd); return -1; @@ -1076,7 +1092,7 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char fclose(p); snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp); ast_safe_system(tmp2); - ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", who, mailcmd); + ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd); } else { ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd); return -1; @@ -1356,7 +1372,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1); ausemacro = 1; } - + /* Play the beginning intro if desired */ if (!ast_strlen_zero(prefile)) { if (ast_fileexists(prefile, NULL, NULL) > 0) { @@ -1403,22 +1419,22 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int } /* Check for a '0' here */ if (res == '0') { - transfer: - strncpy(chan->exten, "o", sizeof(chan->exten) - 1); - if (!ast_strlen_zero(vmu->exit)) { - strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1); - } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) { - strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1); + transfer: + if (vmu->operator) { + strncpy(chan->exten, "o", sizeof(chan->exten) - 1); + if (!ast_strlen_zero(vmu->exit)) { + strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1); + } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) { + strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1); + } + ast_play_and_wait(chan, "transfer"); + chan->priority = 0; + free_user(vmu); + return 0; + } else { + ast_play_and_wait(chan, "vm-sorry"); + return 0; } - chan->priority = 0; - free_user(vmu); - return 0; - } - if (res >= 0) { - /* Unless we're *really* silent, try to send the beep */ - res = ast_streamfile(chan, "beep", chan->language); - if (!res) - res = ast_waitstream(chan, ""); } if (res < 0) { free_user(vmu); @@ -1434,6 +1450,12 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int break; msgnum++; } while(msgnum < MAXMSG); + if (res >= 0) { + /* Unless we're *really* silent, try to send the beep */ + res = ast_streamfile(chan, "beep", chan->language); + if (!res) + res = ast_waitstream(chan, ""); + } if (msgnum < MAXMSG) { /* Store information */ snprintf(txtfile, sizeof(txtfile), "%s.txt", fn); @@ -1502,7 +1524,8 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int free_user(recip); } } - notify_new_message(chan, vmu, msgnum, duration, fmt, chan->callerid); + if (ast_fileexists(fn, NULL, NULL)) + notify_new_message(chan, vmu, msgnum, duration, fmt, chan->callerid); } else { res = ast_streamfile(chan, "vm-mailboxfull", chan->language); if (!res) @@ -1525,16 +1548,56 @@ leave_vm_out: static int count_messages(char *dir) { - int x; - char fn[256]; - for (x=0;xd_name) > 7 && !strncmp(vment->d_name + 7,".txt",4)) + { + vmcount++; + } + } + closedir(vmdir); } - return x; + + return vmcount; } +static void resequence_mailbox(char * dir) +{ + /* we know max messages, so stop process when number is hit */ + + int x,dest; + char sfn[256]; + char dfn[256]; + char stxt[256]; + char dtxt[256]; + + for (x=0,dest=0;x 0) { + + if(x != dest) { + make_file(dfn, sizeof(dfn), dir, dest); + ast_filerename(sfn,dfn,NULL); + + snprintf(stxt, sizeof(stxt), "%s.txt", sfn); + snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn); + rename(stxt, dtxt); + } + + dest++; + } + } +} + + static int say_and_wait(struct ast_channel *chan, int num, char *language) { int d; @@ -2073,23 +2136,10 @@ static int get_folder(struct ast_channel *chan, int start) d = ast_play_and_wait(chan, "vm-for"); /* "for" */ if (d) return d; - if (!strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "fr") || !strcasecmp(chan->language, "pt")) { /* Spanish, French or Portuguese syntax */ - d = ast_play_and_wait(chan, "vm-messages"); /* "messages */ - if (d) - return d; - snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */ - d = ast_play_and_wait(chan, fn); - if (d) - return d; - } else { /* Default English */ - snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */ - d = ast_play_and_wait(chan, fn); - if (d) - return d; - d = ast_play_and_wait(chan, "vm-messages"); /* "messages */ - if (d) - return d; - } + snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */ + d = vm_play_folder_name(chan, fn); + if (d) + return d; d = ast_waitfordigit(chan, 500); if (d) return d; @@ -2272,7 +2322,7 @@ static int forward_message(struct ast_channel *chan, char *context, char *dir, i snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox); snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir); snprintf(ext_context, sizeof(ext_context), "%s@%s", vmtmp->mailbox, vmtmp->context); - ast_log(LOG_DEBUG, sys); + ast_log(LOG_DEBUG, "%s", sys); ast_safe_system(sys); todircount = count_messages(todir); @@ -2283,11 +2333,11 @@ static int forward_message(struct ast_channel *chan, char *context, char *dir, i if (!strcasecmp(s, "wav49")) s = "WAV"; snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s); - ast_log(LOG_DEBUG, sys); + ast_log(LOG_DEBUG, "%s", sys); ast_safe_system(sys); } snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount); - ast_log(LOG_DEBUG, sys); + ast_log(LOG_DEBUG, "%s", sys); ast_safe_system(sys); snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount); @@ -2545,6 +2595,20 @@ static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box) strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1); make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox); vms->lastmsg = count_messages(vms->curdir) - 1; + + /* + The following test is needed in case sequencing gets messed up. + There appears to be more than one way to mess up sequence, so + we will not try to find all of the root causes--just fix it when + detected. + */ + + if(vms->lastmsg != last_message_index(vms->curdir)) + { + ast_log(LOG_NOTICE, "Resequencing Mailbox: %s\n", vms->curdir); + resequence_mailbox(vms->curdir); + } + snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox); } @@ -2586,8 +2650,25 @@ static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu) memset(vms->heard, 0, sizeof(vms->heard)); } +static int vm_play_folder_name(struct ast_channel *chan, char *mbox) +{ + int cmd; + + if (!strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "fr") || !strcasecmp(chan->language, "pt")) { /*Spanish, French or Portuguese syntax */ + cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */ + if (cmd) + return cmd; + return ast_play_and_wait(chan, mbox); + } else { /* Default English */ + cmd = ast_play_and_wait(chan, mbox); + if (cmd) + return cmd; + return ast_play_and_wait(chan, "vm-messages"); /* "messages */ + } +} + /* Default English syntax */ -static int vm_intro(struct ast_channel *chan,struct vm_state *vms) +static int vm_intro_en(struct ast_channel *chan,struct vm_state *vms) { /* Introduce messages they have */ int res; @@ -2949,6 +3030,26 @@ static int vm_intro_cz(struct ast_channel *chan,struct vm_state *vms) return res; } +static int vm_intro(struct ast_channel *chan,struct vm_state *vms) +{ + /* Play voicemail intro - syntax is different for different languages */ + if (!strcasecmp(chan->language, "de")) { /* GERMAN syntax */ + return vm_intro_de(chan, vms); + } else if (!strcasecmp(chan->language, "es")) { /* SPANISH syntax */ + return vm_intro_es(chan, vms); + } else if (!strcasecmp(chan->language, "fr")) { /* FRENCH syntax */ + return vm_intro_fr(chan, vms); + } else if (!strcasecmp(chan->language, "nl")) { /* DUTCH syntax */ + return vm_intro_nl(chan, vms); + } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE syntax */ + return vm_intro_pt(chan, vms); + } else if (!strcasecmp(chan->language, "cz")) { /* CZECH syntax */ + return vm_intro_cz(chan, vms); + } else { /* Default to ENGLISH */ + return vm_intro_en(chan, vms); + } +} + static int vm_instructions(struct ast_channel *chan, struct vm_state *vms, int skipadvanced) { int res = 0; @@ -2957,17 +3058,8 @@ static int vm_instructions(struct ast_channel *chan, struct vm_state *vms, int s if (vms->starting) { if (vms->lastmsg > -1) { res = ast_play_and_wait(chan, "vm-onefor"); - if (!strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "fr") || !strcasecmp(chan->language, "pt")) { /* Spanish, French & Portuguese Syntax */ - if (!res) - res = ast_play_and_wait(chan, "vm-messages"); - if (!res) - res = ast_play_and_wait(chan, vms->vmbox); - } else { /* Default English syntax */ - if (!res) - res = ast_play_and_wait(chan, vms->vmbox); - if (!res) - res = ast_play_and_wait(chan, "vm-messages"); - } + if (!res) + res = vm_play_folder_name(chan, vms->vmbox); } if (!res) res = ast_play_and_wait(chan, "vm-opts"); @@ -3048,19 +3140,24 @@ static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct } newpassword[1] = '\0'; newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword"); - if (cmd < 0) - break; - if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) { - break; - } + if (cmd == '#') + newpassword[0] = '\0'; + else { + if (cmd < 0) + break; + if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) + break; + } newpassword2[1] = '\0'; newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword"); - if (cmd < 0) - break; - - if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) { - break; - } + if (cmd == '#') + newpassword2[0] = '\0'; + else { + if (cmd < 0) + break; + if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) + break; + } if (strcmp(newpassword, newpassword2)) { ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2); cmd = ast_play_and_wait(chan, "vm-mismatch"); @@ -3092,7 +3189,7 @@ static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct } /* Default English syntax */ -static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu) +static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu) { int cmd=0; @@ -3150,6 +3247,17 @@ static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, return cmd; } +static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu) +{ + if (!strcasecmp(chan->language, "es")) { /* SPANISH */ + return vm_browse_messages_es(chan, vms, vmu); + } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE */ + return vm_browse_messages_pt(chan, vms, vmu); + } else { /* Default to English syntax */ + return vm_browse_messages_en(chan, vms, vmu); + } +} + static int vm_execmain(struct ast_channel *chan, void *data) { /* XXX This is, admittedly, some pretty horrendus code. For some @@ -3269,7 +3377,7 @@ static int vm_execmain(struct ast_channel *chan, void *data) goto out; } } - if (prefix) { + if (prefix && !ast_strlen_zero(prefixstr)) { char fullusername[80] = ""; strncpy(fullusername, prefixstr, sizeof(fullusername) - 1); strncat(fullusername, vms.username, sizeof(fullusername) - 1); @@ -3333,22 +3441,7 @@ static int vm_execmain(struct ast_channel *chan, void *data) if (useadsi) adsi_status(chan, &vms); res = 0; - /* Play voicemail intro - syntax is different for different languages */ - if (!strcasecmp(chan->language, "de")) { /* GERMAN syntax */ - cmd = vm_intro_de(chan, &vms); - } else if (!strcasecmp(chan->language, "es")) { /* SPANISH syntax */ - cmd = vm_intro_es(chan, &vms); - } else if (!strcasecmp(chan->language, "fr")) { /* FRENCH syntax */ - cmd = vm_intro_fr(chan, &vms); - } else if (!strcasecmp(chan->language, "nl")) { /* DUTCH syntax */ - cmd = vm_intro_nl(chan, &vms); - } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE syntax */ - cmd = vm_intro_pt(chan, &vms); - } else if (!strcasecmp(chan->language, "cz")) { /* CZECH syntax */ - cmd = vm_intro_cz(chan, &vms); - } else { /* Default to ENGLISH */ - cmd = vm_intro(chan, &vms); - } + cmd = vm_intro(chan, &vms); vms.repeats = 0; vms.starting = 1; while((cmd > -1) && (cmd != 't') && (cmd != '#')) { @@ -3358,13 +3451,7 @@ static int vm_execmain(struct ast_channel *chan, void *data) vms.curmsg = 0; /* Fall through */ case '5': - if (!strcasecmp(chan->language, "es")) { /* SPANISH */ - cmd = vm_browse_messages_es(chan, &vms, vmu); - } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE */ - cmd = vm_browse_messages_pt(chan, &vms, vmu); - } else { /* Default to English syntax */ - cmd = vm_browse_messages(chan, &vms, vmu); - } + cmd = vm_browse_messages(chan, &vms, vmu); break; case '2': /* Change folders */ if (useadsi) @@ -3380,17 +3467,8 @@ static int vm_execmain(struct ast_channel *chan, void *data) } if (useadsi) adsi_status2(chan, &vms); - if (!strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "pt")) { /* SPANISH or PORTUGUESE */ - if (!cmd) - cmd = ast_play_and_wait(chan, "vm-messages"); - if (!cmd) - cmd = ast_play_and_wait(chan, vms.vmbox); - } else { /* Default to English syntax */ - if (!cmd) - cmd = ast_play_and_wait(chan, vms.vmbox); - if (!cmd) - cmd = ast_play_and_wait(chan, "vm-messages"); - } + if (!cmd) + cmd = vm_play_folder_name(chan, vms.vmbox); vms.starting = 1; break; case '3': /* Advanced options */ @@ -3549,21 +3627,9 @@ static int vm_execmain(struct ast_channel *chan, void *data) cmd = say_and_wait(chan, vms.curmsg + 1, chan->language); if (!cmd) cmd = ast_play_and_wait(chan, "vm-savedto"); - if (!strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "pt")) { /* SPANISH or PORTUGUESE */ - if (!cmd) - cmd = ast_play_and_wait(chan, "vm-messages"); - if (!cmd) { - snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box)); - cmd = ast_play_and_wait(chan, vms.fn); - } - } else { /* Default to English */ - if (!cmd) { - snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box)); - cmd = ast_play_and_wait(chan, vms.fn); - } - if (!cmd) - cmd = ast_play_and_wait(chan, "vm-messages"); - } + snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box)); + if (!cmd) + cmd = vm_play_folder_name(chan, vms.fn); if (skipaftercmd) { if (vms.curmsg < vms.lastmsg) { vms.curmsg++; @@ -3577,17 +3643,8 @@ static int vm_execmain(struct ast_channel *chan, void *data) case '*': if (!vms.starting) { cmd = ast_play_and_wait(chan, "vm-onefor"); - if (!strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "pt")) { /* Spanish or Portuguese syntax */ - if (!cmd) - cmd = ast_play_and_wait(chan, "vm-messages"); - if (!cmd) - cmd = ast_play_and_wait(chan, vms.vmbox); - } else { - if (!cmd) - cmd = ast_play_and_wait(chan, vms.vmbox); - if (!cmd) - cmd = ast_play_and_wait(chan, "vm-messages"); - } + if (!cmd) + cmd = vm_play_folder_name(chan, vms.vmbox); if (!cmd) cmd = ast_play_and_wait(chan, "vm-opts"); if (!cmd) @@ -3800,7 +3857,7 @@ static int handle_show_voicemail_users(int fd, int argc, char *argv[]) if ((vmdir = opendir(dirname))) { /* No matter what the format of VM, there will always be a .txt file for each message. */ while ((vment = readdir(vmdir))) - if (!strncmp(vment->d_name + 7,".txt",4)) + if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7,".txt",4)) vmcount++; closedir(vmdir); } @@ -4582,11 +4639,9 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re cmd = ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, silencethreshold, maxsilence); if (cmd == -1) /* User has hung up, no options to give */ - return res; + return cmd; if (cmd == '0') { - /* Erase the message if 0 pushed during playback */ - ast_play_and_wait(chan, "vm-deleted"); - vm_delete(recordfile); + break; } else if (cmd == '*') { break; } @@ -4639,13 +4694,20 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re return 1; #endif case '0': - if (outsidecaller && vmu->operator) { - if (message_exists) - ast_play_and_wait(chan, "vm-msgsaved"); - return cmd; - } else - cmd = ast_play_and_wait(chan, "vm-sorry"); - break; + if (message_exists || recorded) { + cmd = ast_play_and_wait(chan, "vm-saveoper"); + if (!cmd) + cmd = ast_waitfordigit(chan, 3000); + if (cmd == '1') { + ast_play_and_wait(chan, "vm-msgsaved"); + cmd = '0'; + } else { + ast_play_and_wait(chan, "vm-deleted"); + vm_delete(recordfile); + cmd = '0'; + } + } + return cmd; default: /* If the caller is an ouside caller, and the review option is enabled, allow them to review the message, but let the owner of the box review @@ -4681,7 +4743,7 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re } } if (outsidecaller) - ast_play_and_wait(chan, "vm-goodbye"); + ast_play_and_wait(chan, "vm-goodbye"); if (cmd == 't') cmd = 0; return cmd; @@ -4703,6 +4765,7 @@ static int vm_delete(char *file) return ast_filedelete(file, NULL); } + int usecount(void) { int res; diff --git a/ast_expr.y b/ast_expr.y index 97b2b021cf..df08f68299 100755 --- a/ast_expr.y +++ b/ast_expr.y @@ -215,6 +215,7 @@ struct val *vp; } if (vp->type == string || vp->type == numeric_string) free (vp->u.s); + free (vp); } @@ -650,19 +651,24 @@ struct val *a, *b; { struct val *r; - if (!to_integer (a) || !to_integer (b)) { + if (!to_integer (a)) { ast_log(LOG_WARNING,"non-numeric argument\n"); - free_value(a); + if (!to_integer (b)) { + free_value(a); + free_value(b); + return make_integer(0); + } else { + free_value(a); + return (b); + } + } else if (!to_integer(b)) { free_value(b); - return(NULL); + return (a); } r = make_integer (/*(quad_t)*/(a->u.i + b->u.i)); if (chk_plus (a->u.i, b->u.i, r->u.i)) { ast_log(LOG_WARNING,"overflow\n"); - free_value(a); - free_value(b); - return(NULL); } free_value (a); free_value (b); @@ -690,19 +696,27 @@ struct val *a, *b; { struct val *r; - if (!to_integer (a) || !to_integer (b)) { - free_value(a); - free_value(b); + if (!to_integer (a)) { ast_log(LOG_WARNING, "non-numeric argument\n"); - return(NULL); + if (!to_integer (b)) { + free_value(a); + free_value(b); + return make_integer(0); + } else { + r = make_integer(0 - b->u.i); + free_value(a); + free_value(b); + return (r); + } + } else if (!to_integer(b)) { + ast_log(LOG_WARNING, "non-numeric argument\n"); + free_value(b); + return (a); } r = make_integer (/*(quad_t)*/(a->u.i - b->u.i)); if (chk_minus (a->u.i, b->u.i, r->u.i)) { - free_value(a); - free_value(b); - ast_log(LOG_WARNING, "overload\n"); - return(NULL); + ast_log(LOG_WARNING, "overflow\n"); } free_value (a); free_value (b); @@ -732,15 +746,12 @@ struct val *a, *b; free_value(a); free_value(b); ast_log(LOG_WARNING, "non-numeric argument\n"); - return(NULL); + return(make_integer(0)); } r = make_integer (/*(quad_t)*/(a->u.i * b->u.i)); if (chk_times (a->u.i, b->u.i, r->u.i)) { ast_log(LOG_WARNING, "overflow\n"); - free_value(a); - free_value(b); - return(NULL); } free_value (a); free_value (b); @@ -765,26 +776,28 @@ struct val *a, *b; { struct val *r; - if (!to_integer (a) || !to_integer (b)) { + if (!to_integer (a)) { free_value(a); free_value(b); ast_log(LOG_WARNING, "non-numeric argument\n"); - return(NULL); + return make_integer(0); + } else if (!to_integer (b)) { + free_value(a); + free_value(b); + ast_log(LOG_WARNING, "non-numeric argument\n"); + return make_integer(INT_MAX); } if (b->u.i == 0) { ast_log(LOG_WARNING, "division by zero\n"); free_value(a); free_value(b); - return(NULL); + return make_integer(INT_MAX); } r = make_integer (/*(quad_t)*/(a->u.i / b->u.i)); if (chk_div (a->u.i, b->u.i)) { ast_log(LOG_WARNING, "overflow\n"); - free_value(a); - free_value(b); - return(NULL); } free_value (a); free_value (b); @@ -801,14 +814,13 @@ struct val *a, *b; ast_log(LOG_WARNING, "non-numeric argument\n"); free_value(a); free_value(b); - return(NULL); + return make_integer(0); } if (b->u.i == 0) { ast_log(LOG_WARNING, "div by zero\n"); free_value(a); - free_value(b); - return(NULL); + return (b); } r = make_integer (/*(quad_t)*/(a->u.i % b->u.i)); @@ -838,7 +850,7 @@ struct val *a, *b; ast_log(LOG_WARNING,"regcomp() error : %s",errbuf); free_value(a); free_value(b); - return(NULL); + return make_str(""); } /* compare string against pattern */ diff --git a/asterisk.c b/asterisk.c index 3c4d230eec..113e5a1725 100755 --- a/asterisk.c +++ b/asterisk.c @@ -247,13 +247,15 @@ static void network_verboser(const char *s, int pos, int replace, int complete) char *t = alloca(strlen(s) + 2); if (t) { sprintf(t, "\r%s", s); - ast_network_puts(t); + if (complete) + ast_network_puts(t); } else { ast_log(LOG_ERROR, "Out of memory\n"); ast_network_puts(s); } } else { - ast_network_puts(s); + if (complete) + ast_network_puts(s); } } @@ -757,12 +759,12 @@ static char shutdown_when_convenient_help[] = static char restart_now_help[] = "Usage: restart now\n" -" Causes Asterisk to hangup all calls and exec() itself performing a cold.\n" +" Causes Asterisk to hangup all calls and exec() itself performing a cold\n" " restart.\n"; static char restart_gracefully_help[] = "Usage: restart gracefully\n" -" Causes Asterisk to stop accepting new calls and exec() itself performing a cold.\n" +" Causes Asterisk to stop accepting new calls and exec() itself performing a cold\n" " restart when all active calls have ended.\n"; static char restart_when_convenient_help[] = @@ -925,7 +927,7 @@ static int ast_el_read_char(EditLine *el, char *cp) usleep(1000000 / reconnects_per_second); } } - if (tries >= 30) { + if (tries >= 30 * reconnects_per_second) { fprintf(stderr, "Failed to reconnect for 30 seconds. Quitting.\n"); quit_handler(0, 0, 0, 0); } @@ -1471,13 +1473,13 @@ static int show_cli_help(void) { printf(" -f Do not fork\n"); printf(" -g Dump core in case of a crash\n"); printf(" -h This help screen\n"); - printf(" -i Initializie crypto keys at startup\n"); + printf(" -i Initialize crypto keys at startup\n"); printf(" -n Disable console colorization\n"); printf(" -p Run as pseudo-realtime thread\n"); - printf(" -q Quiet mode (supress output)\n"); + printf(" -q Quiet mode (suppress output)\n"); printf(" -r Connect to Asterisk on this machine\n"); printf(" -R Connect to Asterisk, and attempt to reconnect if disconnected\n"); - printf(" -t Record soundfiles in /tmp and move them where they belong after they are done.\n"); + printf(" -t Record soundfiles in /var/tmp and move them where they belong after they are done.\n"); printf(" -v Increase verbosity (multiple v's = more verbose)\n"); printf(" -x Execute command (only valid with -r)\n"); printf("\n"); @@ -1579,6 +1581,7 @@ int main(int argc, char *argv[]) int num; char *buf; char *runuser=NULL, *rungroup=NULL; + struct pollfd silly_macos[1]; /* Remember original args for restart */ if (argc > sizeof(_argv) / sizeof(_argv[0]) - 1) { @@ -1728,10 +1731,6 @@ int main(int argc, char *argv[]) printf(term_end()); fflush(stdout); - /* Test recursive mutex locking. */ - if (test_for_thread_safety()) - ast_verbose("Warning! Asterisk is not thread safe.\n"); - if (option_console && !option_verbose) ast_verbose("[ Reading Master Configuration ]"); ast_readconfig(); @@ -1796,6 +1795,10 @@ int main(int argc, char *argv[]) ast_log(LOG_WARNING, "Unable to open pid file '%s': %s\n", (char *)ast_config_AST_PID, strerror(errno)); } + /* Test recursive mutex locking. */ + if (test_for_thread_safety()) + ast_verbose("Warning! Asterisk is not thread safe.\n"); + ast_makesocket(); sigemptyset(&sigs); sigaddset(&sigs, SIGHUP); @@ -1905,15 +1908,24 @@ int main(int argc, char *argv[]) consolehandler((char *)buf); } else { - if (option_remote) - ast_cli(STDOUT_FILENO, "\nUse EXIT or QUIT to exit the asterisk console\n"); + if (write(STDOUT_FILENO, "\nUse EXIT or QUIT to exit the asterisk console\n", + strlen("\nUse EXIT or QUIT to exit the asterisk console\n")) < 0) { + /* Whoa, stdout disappeared from under us... Make /dev/null's */ + int fd; + fd = open("/dev/null", O_RDWR); + if (fd > -1) { + dup2(fd, STDOUT_FILENO); + dup2(fd, STDIN_FILENO); + } else + ast_log(LOG_WARNING, "Failed to open /dev/null to recover from dead console. Bad things will happen!\n"); + break; + } } } - } else { - /* Do nothing */ - for(;;) - poll(NULL,0, -1); } + /* Do nothing */ + for(;;) + poll(silly_macos,0, -1); return 0; } diff --git a/callerid.c b/callerid.c index 348c844043..5481676ecd 100755 --- a/callerid.c +++ b/callerid.c @@ -334,6 +334,8 @@ int callerid_feed(struct callerid_state *cid, unsigned char *ubuf, int len, int cid->number[res] = '\0'; } break; + case 6: /* Stentor Call Qualifier (ie. Long Distance call) */ + break; case 7: /* Name */ case 8: /* Name */ res = cid->rawdata[x]; diff --git a/cdr.c b/cdr.c index f4e97efcec..5a8102f300 100755 --- a/cdr.c +++ b/cdr.c @@ -524,11 +524,16 @@ void ast_cdr_reset(struct ast_cdr *cdr, int flags) } -void ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr) { +struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr) +{ + struct ast_cdr *ret; if (cdr) { + ret = cdr; while(cdr->next) cdr = cdr->next; cdr->next = newcdr; - } else - ast_log(LOG_ERROR, "Can't append a CDR to NULL!\n"); + } else { + ret = newcdr; + } + return ret; } diff --git a/cdr/cdr_csv.c b/cdr/cdr_csv.c index a1d391cfb7..bd18a77753 100755 --- a/cdr/cdr_csv.c +++ b/cdr/cdr_csv.c @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -184,7 +185,7 @@ static int build_csv_record(char *buf, size_t bufsize, struct ast_cdr *cdr) static int writefile(char *s, char *acc) { - char tmp[256]; + char tmp[AST_CONFIG_MAX_PATH]; FILE *f; if (strchr(acc, '/') || (acc[0] == '.')) { ast_log(LOG_WARNING, "Account code '%s' insecure for writing file\n", acc); @@ -195,6 +196,7 @@ static int writefile(char *s, char *acc) if (!f) return -1; fputs(s, f); + fflush(f); fclose(f); return 0; } @@ -217,7 +219,7 @@ static int csv_log(struct ast_cdr *cdr) we open write and close the log file each time */ mf = fopen(csvmaster, "a"); if (!mf) { - ast_log(LOG_ERROR, "Unable to re-open master file %s\n", csvmaster); + ast_log(LOG_ERROR, "Unable to re-open master file %s : %s\n", csvmaster, strerror(errno)); } if (mf) { fputs(buf, mf); @@ -227,7 +229,7 @@ static int csv_log(struct ast_cdr *cdr) } if (!ast_strlen_zero(cdr->accountcode)) { if (writefile(buf, cdr->accountcode)) - ast_log(LOG_WARNING, "Unable to write CSV record to account file '%s'\n", cdr->accountcode); + ast_log(LOG_WARNING, "Unable to write CSV record to account file '%s' : %s\n", cdr->accountcode, strerror(errno)); } } return 0; diff --git a/cdr/cdr_odbc.c b/cdr/cdr_odbc.c index e52730f2ec..80bd973342 100755 --- a/cdr/cdr_odbc.c +++ b/cdr/cdr_odbc.c @@ -3,13 +3,12 @@ * * ODBC CDR Backend * + * Copyright (C) 2003-2005, Digium, Inc. + * * Brian K. West * * This program is free software, distributed under the terms of - * the GNU General Public License. - * - * Copyright (c) 2003 Digium, Inc. - * + * the GNU General Public License */ #include @@ -37,8 +36,9 @@ static char *desc = "ODBC CDR Backend"; static char *name = "ODBC"; static char *config = "cdr_odbc.conf"; -static char *dsn = NULL, *username = NULL, *password = NULL, *loguniqueid = NULL; -static int dsn_alloc = 0, username_alloc = 0, password_alloc = 0, loguniqueid_alloc = 0; +static char *dsn = NULL, *username = NULL, *password = NULL; +static int dsn_alloc = 0, username_alloc = 0, password_alloc = 0; +static int loguniqueid = 0; static int connected = 0; AST_MUTEX_DEFINE_STATIC(odbc_lock); @@ -65,38 +65,31 @@ static int odbc_log(struct ast_cdr *cdr) ast_mutex_lock(&odbc_lock); strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm); memset(sqlcmd,0,2048); - if((loguniqueid != NULL) && ((strcmp(loguniqueid, "1") == 0) || (strcmp(loguniqueid, "yes") == 0))) - { + if (loguniqueid) { snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO cdr " "(calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp," "lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid,userfield) " "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"); - } - else - { + } else { snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO cdr " "(calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata," "duration,billsec,disposition,amaflags,accountcode) " "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)"); } - if(!connected) - { - res = odbc_init(); - if(res < 0) - { + if (!connected) { + res = odbc_init(); + if (res < 0) { connected = 0; ast_mutex_unlock(&odbc_lock); return 0; } - } ODBC_res = SQLAllocHandle(SQL_HANDLE_STMT, ODBC_con, &ODBC_stmt); - if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) - { - if(option_verbose > 10) + if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) { + if (option_verbose > 10) ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Failure in AllocStatement %d\n", ODBC_res); SQLGetDiagRec(SQL_HANDLE_DBC, ODBC_con, 1, ODBC_stat, &ODBC_err, ODBC_msg, 100, &ODBC_mlen); SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt); @@ -111,9 +104,8 @@ static int odbc_log(struct ast_cdr *cdr) ODBC_res = SQLPrepare(ODBC_stmt, sqlcmd, SQL_NTS); - if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) - { - if(option_verbose > 10) + if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) { + if (option_verbose > 10) ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error in PREPARE %d\n", ODBC_res); SQLGetDiagRec(SQL_HANDLE_DBC, ODBC_con, 1, ODBC_stat, &ODBC_err, ODBC_msg, 100, &ODBC_mlen); SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt); @@ -137,46 +129,38 @@ static int odbc_log(struct ast_cdr *cdr) SQLBindParameter(ODBC_stmt, 13, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->amaflags, 0, NULL); SQLBindParameter(ODBC_stmt, 14, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->accountcode), 0, cdr->accountcode, 0, NULL); - if((loguniqueid != NULL) && ((strcmp(loguniqueid, "1") == 0) || (strcmp(loguniqueid, "yes") == 0))) - { + if (loguniqueid) { SQLBindParameter(ODBC_stmt, 15, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->uniqueid), 0, cdr->uniqueid, 0, NULL); SQLBindParameter(ODBC_stmt, 16, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->userfield), 0, cdr->userfield, 0, NULL); } - if(connected) - { + if (connected) { res = odbc_do_query(); - if(res < 0) - { - if(option_verbose > 10) + if (res < 0) { + if (option_verbose > 10) ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Query FAILED Call not logged!\n"); res = odbc_init(); - if(option_verbose > 10) + if (option_verbose > 10) ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Reconnecting to dsn %s\n", dsn); - if(res < 0) - { - if(option_verbose > 10) + if (res < 0) { + if (option_verbose > 10) ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: %s has gone away!\n", dsn); connected = 0; - } - else - { - if(option_verbose > 10) + } else { + if (option_verbose > 10) ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Trying Query again!\n"); res = odbc_do_query(); - if(res < 0) - { - if(option_verbose > 10) + if (res < 0) { + if (option_verbose > 10) ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Query FAILED Call not logged!\n"); } } } - } - else - { - if(option_verbose > 10) + } else { + if (option_verbose > 10) ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Query FAILED Call not logged!\n"); } + SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt); ast_mutex_unlock(&odbc_lock); return 0; } @@ -189,9 +173,8 @@ char *description(void) static int odbc_unload_module(void) { ast_mutex_lock(&odbc_lock); - if (connected) - { - if(option_verbose > 10) + if (connected) { + if (option_verbose > 10) ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Disconnecting from %s\n", dsn); SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt); SQLDisconnect(ODBC_con); @@ -199,38 +182,29 @@ static int odbc_unload_module(void) SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env); connected = 0; } - if (dsn && dsn_alloc) - { - if(option_verbose > 10) + if (dsn && dsn_alloc) { + if (option_verbose > 10) ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: free dsn\n"); free(dsn); dsn = NULL; dsn_alloc = 0; } - if (username && username_alloc) - { - if(option_verbose > 10) + if (username && username_alloc) { + if (option_verbose > 10) ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: free username\n"); free(username); username = NULL; username_alloc = 0; } - if (password && password_alloc) - { - if(option_verbose > 10) + if (password && password_alloc) { + if (option_verbose > 10) ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: free password\n"); free(password); password = NULL; password_alloc = 0; } - if (loguniqueid && loguniqueid_alloc) - { - if(option_verbose > 10) - ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: free loguniqueid\n"); - free(loguniqueid); - loguniqueid = NULL; - loguniqueid_alloc = 0; - } + loguniqueid = 0; + ast_cdr_unregister(name); ast_mutex_unlock(&odbc_lock); return 0; @@ -246,8 +220,7 @@ static int odbc_load_module(void) ast_mutex_lock(&odbc_lock); cfg = ast_load(config); - if (!cfg) - { + if (!cfg) { ast_log(LOG_WARNING, "cdr_odbc: Unable to load config for ODBC CDR's: %s\n", config); goto out; } @@ -259,113 +232,85 @@ static int odbc_load_module(void) } tmp = ast_variable_retrieve(cfg,"global","dsn"); - if (tmp) - { + if (tmp) { dsn = malloc(strlen(tmp) + 1); - if (dsn != NULL) - { + if (dsn != NULL) { memset(dsn, 0, strlen(tmp) + 1); dsn_alloc = 1; strncpy(dsn, tmp, strlen(tmp)); - } - else - { + } else { ast_log(LOG_ERROR,"cdr_odbc: Out of memory error.\n"); - return -1; + res = -1; + goto out; } - } - else - { + } else { ast_log(LOG_WARNING,"cdr_odbc: dsn not specified. Assuming asteriskdb\n"); dsn = "asteriskdb"; } tmp = ast_variable_retrieve(cfg,"global","username"); - if (tmp) - { + if (tmp) { username = malloc(strlen(tmp) + 1); - if (username != NULL) - { + if (username != NULL) { memset(username, 0, strlen(tmp) + 1); username_alloc = 1; strncpy(username, tmp, strlen(tmp)); - } - else - { + } else { ast_log(LOG_ERROR,"cdr_odbc: Out of memory error.\n"); - return -1; + res = -1; + goto out; } - } - else - { + } else { ast_log(LOG_WARNING,"cdr_odbc: username not specified. Assuming root\n"); username = "root"; } tmp = ast_variable_retrieve(cfg,"global","password"); - if (tmp) - { + if (tmp) { password = malloc(strlen(tmp) + 1); - if (password != NULL) - { + if (password != NULL) { memset(password, 0, strlen(tmp) + 1); password_alloc = 1; strncpy(password, tmp, strlen(tmp)); - } - else - { + } else { ast_log(LOG_ERROR,"cdr_odbc: Out of memory error.\n"); - return -1; + res = -1; + goto out; } - } - else - { + } else { ast_log(LOG_WARNING,"cdr_odbc: database password not specified. Assuming blank\n"); password = ""; } tmp = ast_variable_retrieve(cfg,"global","loguniqueid"); - if (tmp) - { - loguniqueid = malloc(strlen(tmp) + 1); - if (loguniqueid != NULL) - { - strcpy(loguniqueid,tmp); - loguniqueid_alloc = 1; + if (tmp) { + loguniqueid = ast_true(tmp); + if (loguniqueid) { ast_log(LOG_NOTICE,"cdr_odbc: Logging uniqueid\n"); - } - else - { + } else { ast_log(LOG_ERROR,"cdr_odbc: Not logging uniqueid\n"); - loguniqueid_alloc = 1; - loguniqueid = NULL; } - } - else - { + } else { ast_log(LOG_WARNING,"cdr_odbc: Not logging uniqueid\n"); - loguniqueid = NULL; + loguniqueid = 0; } ast_destroy(cfg); - if(option_verbose > 3) - { - ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: dsn is %s\n",dsn); - ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: username is %s\n",username); - ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: password is [secret]\n"); - + if (option_verbose > 2) { + ast_verbose( VERBOSE_PREFIX_3 "cdr_odbc: dsn is %s\n",dsn); + ast_verbose( VERBOSE_PREFIX_3 "cdr_odbc: username is %s\n",username); + ast_verbose( VERBOSE_PREFIX_3 "cdr_odbc: password is [secret]\n"); } res = odbc_init(); - if(res < 0) - { + if (res < 0) { ast_log(LOG_ERROR, "cdr_odbc: Unable to connect to datasource: %s\n", dsn); - ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Unable to connect to datasource: %s\n", dsn); + if (option_verbose > 2) { + ast_verbose( VERBOSE_PREFIX_3 "cdr_odbc: Unable to connect to datasource: %s\n", dsn); + } } - res = ast_cdr_register(name, desc, odbc_log); - if (res) - { + if (res) { ast_log(LOG_ERROR, "cdr_odbc: Unable to register ODBC CDR handling\n"); } out: @@ -375,25 +320,22 @@ out: static int odbc_do_query(void) { - long int ODBC_err; + long int ODBC_err; int ODBC_res; - short int ODBC_mlen; - char ODBC_msg[200], ODBC_stat[10]; - + short int ODBC_mlen; + char ODBC_msg[200], ODBC_stat[10]; + ODBC_res = SQLExecute(ODBC_stmt); - - if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) - { - if(option_verbose > 10) + + if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) { + if (option_verbose > 10) ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error in Query %d\n", ODBC_res); SQLGetDiagRec(SQL_HANDLE_DBC, ODBC_con, 1, ODBC_stat, &ODBC_err, ODBC_msg, 100, &ODBC_mlen); SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt); connected = 0; return -1; - } - else - { - if(option_verbose > 10) + } else { + if (option_verbose > 10) ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Query Successful!\n"); connected = 1; } @@ -407,13 +349,10 @@ static int odbc_init(void) int ODBC_res; char ODBC_msg[200], ODBC_stat[10]; - if ( ODBC_env == SQL_NULL_HANDLE || connected == 0 ) - { + if (ODBC_env == SQL_NULL_HANDLE || connected == 0) { ODBC_res = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &ODBC_env); - - if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) - { - if(option_verbose > 10) + if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) { + if (option_verbose > 10) ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error AllocHandle\n"); connected = 0; return -1; @@ -421,9 +360,8 @@ static int odbc_init(void) ODBC_res = SQLSetEnvAttr(ODBC_env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); - if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) - { - if(option_verbose > 10) + if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) { + if (option_verbose > 10) ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error SetEnv\n"); SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env); connected = 0; @@ -432,32 +370,27 @@ static int odbc_init(void) ODBC_res = SQLAllocHandle(SQL_HANDLE_DBC, ODBC_env, &ODBC_con); - if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) - { - if(option_verbose > 10) + if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) { + if (option_verbose > 10) ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error AllocHDB %d\n", ODBC_res); SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env); connected = 0; return -1; } - SQLSetConnectAttr(ODBC_con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)10, 0); } ODBC_res = SQLConnect(ODBC_con, (SQLCHAR*)dsn, SQL_NTS, (SQLCHAR*)username, SQL_NTS, (SQLCHAR*)password, SQL_NTS); - if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) - { - if(option_verbose > 10) + if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) { + if (option_verbose > 10) ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error SQLConnect %d\n", ODBC_res); SQLGetDiagRec(SQL_HANDLE_DBC, ODBC_con, 1, ODBC_stat, &ODBC_err, ODBC_msg, 100, &ODBC_mlen); SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env); connected = 0; return -1; - } - else - { - if(option_verbose > 10) + } else { + if (option_verbose > 10) ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Connected to %s\n", dsn); connected = 1; } diff --git a/cdr/cdr_pgsql.c b/cdr/cdr_pgsql.c index ccec5f9e57..f6af7ff584 100755 --- a/cdr/cdr_pgsql.c +++ b/cdr/cdr_pgsql.c @@ -180,20 +180,13 @@ static int my_unload_module(void) return 0; } -static int my_load_module(void) +static int process_my_load_module(struct ast_config *cfg) { int res; - struct ast_config *cfg; struct ast_variable *var; char *pgerror; char *tmp; - cfg = ast_load(config); - if (!cfg) { - ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config); - return 0; - } - var = ast_variable_browse(cfg, "global"); if (!var) { /* nothing configured */ @@ -280,8 +273,6 @@ static int my_load_module(void) pgdbport = "5432"; } - ast_destroy(cfg); - ast_log(LOG_DEBUG,"cdr_pgsql: got hostname of %s\n",pghostname); ast_log(LOG_DEBUG,"cdr_pgsql: got port of %s\n",pgdbport); if (pgdbsock) @@ -308,6 +299,20 @@ static int my_load_module(void) return res; } +static int my_load_module(void) +{ + struct ast_config *cfg; + int res; + cfg = ast_load(config); + if (!cfg) { + ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config); + return 0; + } + res = process_my_load_module(cfg); + ast_destroy(cfg); + return res; +} + int load_module(void) { return my_load_module(); diff --git a/channel.c b/channel.c index 2f6f28d7f4..8f19ea81b3 100755 --- a/channel.c +++ b/channel.c @@ -1888,7 +1888,7 @@ struct ast_channel *ast_request(char *type, int format, void *data) manager_event(EVENT_FLAG_CALL, "Newchannel", "Channel: %s\r\n" "State: %s\r\n" - "Callerid: %s\r\n" + "CallerID: %s\r\n" "Uniqueid: %s\r\n", c->name, ast_state2str(c->_state), c->callerid ? c->callerid : "", c->uniqueid); } @@ -2205,7 +2205,7 @@ int ast_do_masquerade(struct ast_channel *original) char orig[100]; char masqn[100]; char zombn[100]; - + #if 1 ast_log(LOG_DEBUG, "Actually Masquerading %s(%d) into the structure of %s(%d)\n", clone->name, clone->_state, original->name, original->_state); @@ -2362,6 +2362,9 @@ int ast_do_masquerade(struct ast_channel *original) /* Set the read format */ ast_set_read_format(original, rformat); + /* Copy the music class */ + strncpy(original->musicclass, clone->musicclass, sizeof(original->musicclass) - 1); + ast_log(LOG_DEBUG, "Putting channel %s in %d/%d formats\n", original->name, wformat, rformat); /* Okay. Last thing is to let the channel driver know about all this mess, so he @@ -2387,8 +2390,10 @@ int ast_do_masquerade(struct ast_channel *original) ast_channel_free(clone); manager_event(EVENT_FLAG_CALL, "Hangup", "Channel: %s\r\n", zombn); } else { + struct ast_frame null_frame = { AST_FRAME_NULL, }; ast_log(LOG_DEBUG, "Released clone lock on '%s'\n", clone->name); clone->zombie=1; + ast_queue_frame(clone, &null_frame); ast_mutex_unlock(&clone->lock); } @@ -2419,7 +2424,7 @@ void ast_set_callerid(struct ast_channel *chan, char *callerid, int anitoo) ast_cdr_setcid(chan->cdr, chan); manager_event(EVENT_FLAG_CALL, "Newcallerid", "Channel: %s\r\n" - "Callerid: %s\r\n" + "CallerID: %s\r\n" "Uniqueid: %s\r\n", chan->name, chan->callerid ? chan->callerid : "", @@ -2436,14 +2441,14 @@ int ast_setstate(struct ast_channel *chan, int state) manager_event(EVENT_FLAG_CALL, "Newchannel", "Channel: %s\r\n" "State: %s\r\n" - "Callerid: %s\r\n" + "CallerID: %s\r\n" "Uniqueid: %s\r\n", chan->name, ast_state2str(chan->_state), chan->callerid ? chan->callerid : "", chan->uniqueid); } else { manager_event(EVENT_FLAG_CALL, "Newstate", "Channel: %s\r\n" "State: %s\r\n" - "Callerid: %s\r\n" + "CallerID: %s\r\n" "Uniqueid: %s\r\n", chan->name, ast_state2str(chan->_state), chan->callerid ? chan->callerid : "", chan->uniqueid); } @@ -2899,8 +2904,8 @@ unsigned int ast_get_group(char *s) /* Just one */ finish = start; } else { - ast_log(LOG_ERROR, "Syntax error parsing '%s' at '%s'. Using '0'\n", s,piece); - return 0; + ast_log(LOG_ERROR, "Syntax error parsing '%s' at '%s'.\n", s, piece); + continue; } for (x=start;x<=finish;x++) { if ((x > 31) || (x < 0)) { diff --git a/channels/chan_agent.c b/channels/chan_agent.c index c5dcc891e5..5d3781017c 100755 --- a/channels/chan_agent.c +++ b/channels/chan_agent.c @@ -519,9 +519,26 @@ static int agent_hangup(struct ast_channel *ast) ast->pvt->pvt = NULL; p->app_sleep_cond = 1; p->acknowledged = 0; - if (p->start && (ast->_state != AST_STATE_UP)) + + /* if they really are hung up then set start to 0 so the test + * later if we're called on an already downed channel + * doesn't cause an agent to be logged out like when + * agent_request() is followed immediately by agent_hangup() + * as in apps/app_chanisavail.c:chanavail_exec() + */ + + ast_mutex_lock(&usecnt_lock); + usecnt--; + ast_mutex_unlock(&usecnt_lock); + + ast_log(LOG_DEBUG, "Hangup called for state %s\n", ast_state2str(ast->_state)); + if (p->start && (ast->_state != AST_STATE_UP)) { howlong = time(NULL) - p->start; - time(&p->start); + p->start = 0; + } else if (ast->_state == AST_STATE_RESERVED) { + howlong = 0; + } else + p->start = 0; if (p->chan) { /* If they're dead, go ahead and hang up on the agent now */ if (!ast_strlen_zero(p->loginchan)) { diff --git a/channels/chan_alsa.c b/channels/chan_alsa.c index acf80b857a..fa09cbe0b5 100755 --- a/channels/chan_alsa.c +++ b/channels/chan_alsa.c @@ -863,7 +863,7 @@ static int console_sendtext(int fd, int argc, char *argv[]) struct ast_frame f = { AST_FRAME_TEXT, 0 }; char text2send[256] = ""; text2send[0] = '\0'; - while(tmparg <= argc) { + while(tmparg < argc) { strncat(text2send, argv[tmparg++], sizeof(text2send) - strlen(text2send) - 1); strncat(text2send, " ", sizeof(text2send) - strlen(text2send) - 1); } diff --git a/channels/chan_h323.c b/channels/chan_h323.c index c64a6e254b..a7437b91d3 100755 --- a/channels/chan_h323.c +++ b/channels/chan_h323.c @@ -82,7 +82,7 @@ con_established_cb on_connection_established; clear_con_cb on_connection_cleared; answer_call_cb on_answer_call; -int h323debug; +int h323debug = 0; /** String variables required by ASTERISK */ static char *type = "H323"; @@ -348,7 +348,9 @@ static struct oh323_peer *build_peer(char *name, struct ast_variable *v) } else { ast_mutex_unlock(&peerl.lock); peer = (struct oh323_peer*)malloc(sizeof(struct oh323_peer)); - memset(peer, 0, sizeof(struct oh323_peer)); + if (peer) { + memset(peer, 0, sizeof(struct oh323_peer)); + } } if (peer) { if (!found) { @@ -403,7 +405,7 @@ static int oh323_digit(struct ast_channel *c, char digit) ast_rtp_senddigit(p->rtp, digit); } /* If in-band DTMF is desired, send that */ - if (p->dtmfmode & H323_DTMF_INBAND) + if (!(p->dtmfmode & H323_DTMF_RFC2833)) h323_send_tone(p->cd.call_token, digit); return 0; } @@ -442,14 +444,14 @@ static int oh323_call(struct ast_channel *c, char *dest, int timeout) /* Clear the call token */ if ((p->cd).call_token == NULL) (p->cd).call_token = (char *)malloc(128); - - memset((char *)(p->cd).call_token, 0, 128); - if (p->cd.call_token == NULL) { + if ((p->cd).call_token == NULL) { ast_log(LOG_ERROR, "Not enough memory.\n"); return -1; } + memset((char *)(p->cd).call_token, 0, 128); + /* Build the address to call */ memset(called_addr, 0, sizeof(called_addr)); memcpy(called_addr, dest, strlen(dest)); @@ -601,7 +603,7 @@ static struct ast_frame *oh323_rtp_read(struct oh323_pvt *p) } /* Do in-band DTMF detection */ - if (p->dtmfmode & H323_DTMF_INBAND) { + if ((p->dtmfmode & H323_DTMF_INBAND) && p->vad) { f = ast_dsp_process(p->owner,p->vad,f); if (f->frametype == AST_FRAME_DTMF) ast_log(LOG_DEBUG, "Got in-band digit %c.\n", f->subclass); @@ -1032,8 +1034,6 @@ struct rtp_info *create_connection(unsigned call_reference, const char * token) the oh323_pvt structure XXX */ static char iabuf[INET_ADDRSTRLEN]; - info = (struct rtp_info *) malloc(sizeof(struct rtp_info)); - p = find_call(call_reference, token); if (!p) { @@ -1041,6 +1041,12 @@ struct rtp_info *create_connection(unsigned call_reference, const char * token) return NULL; } + info = (struct rtp_info *) malloc(sizeof(struct rtp_info)); + if (!info) { + ast_log(LOG_ERROR, "Unable to allocate rtp_info, this is very bad.\n"); + return NULL; + } + /* figure out our local RTP port and tell the H.323 stack about it*/ ast_rtp_get_us(p->rtp, &us); ast_rtp_get_peer(p->rtp, &them); @@ -1594,9 +1600,9 @@ static struct ast_cli_entry cli_show_codecs = static struct ast_cli_entry cli_gk_cycle = { { "h.323", "gk", "cycle", NULL }, h323_gk_cycle, "Manually re-register with the Gatekeper", show_cycle_usage }; static struct ast_cli_entry cli_hangup_call = - { { "h.323", "hangup", NULL }, h323_ep_hangup, "Show all active call tokens", show_hangup_usage }; + { { "h.323", "hangup", NULL }, h323_ep_hangup, "Manually try to hang up a call", show_hangup_usage }; static struct ast_cli_entry cli_show_tokens = - { { "h.323", "show", "tokens", NULL }, h323_tokens_show, "Manually try to hang up a call", show_tokens_usage }; + { { "h.323", "show", "tokens", NULL }, h323_tokens_show, "Show all active call tokens", show_tokens_usage }; diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index e8b56282d3..16c911d6a9 100755 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -361,6 +361,8 @@ struct chan_iax2_pvt { unsigned int lastsent; /* Next outgoing timestamp if everything is good */ unsigned int nextpred; + /* True if the last voice we transmitted was not silence/CNG */ + int notsilenttx; /* Ping time */ unsigned int pingtime; /* Max time for initial response */ @@ -536,12 +538,12 @@ AST_MUTEX_DEFINE_STATIC(dpcache_lock); static void iax_debug_output(const char *data) { if (iaxdebug) - ast_verbose(data); + ast_verbose("%s", data); } static void iax_error_output(const char *data) { - ast_log(LOG_WARNING, data); + ast_log(LOG_WARNING, "%s", data); } /* XXX We probably should use a mutex when working with this XXX */ @@ -641,10 +643,9 @@ static int iax2_getpeername(struct sockaddr_in sin, char *host, int len, int loc return res; } -static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, int lockpeer) +static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, int lockpeer, const char *host) { struct chan_iax2_pvt *tmp; - char iabuf[INET_ADDRSTRLEN]; tmp = malloc(sizeof(struct chan_iax2_pvt)); if (tmp) { memset(tmp, 0, sizeof(struct chan_iax2_pvt)); @@ -659,8 +660,7 @@ static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, int lockpeer) tmp->initid = -1; /* strncpy(tmp->context, context, sizeof(tmp->context)-1); */ strncpy(tmp->exten, "s", sizeof(tmp->exten)-1); - if (!iax2_getpeername(*sin, tmp->host, sizeof(tmp->host), lockpeer)) - snprintf(tmp->host, sizeof(tmp->host), "%s:%d", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), ntohs(sin->sin_port)); + strncpy(tmp->host, host, sizeof(tmp->host)-1); } return tmp; } @@ -826,6 +826,8 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc int res = 0; int x; struct timeval now; + char iabuf[INET_ADDRSTRLEN]; + char host[80]; if (new <= NEW_ALLOW) { /* Look for an existing connection first */ for (x=1;(res < 1) && (x= NEW_ALLOW)) { + if (!iax2_getpeername(*sin, host, sizeof(host), lockpeer)) + snprintf(host, sizeof(host), "%s:%d", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), ntohs(sin->sin_port)); gettimeofday(&now, NULL); for (x=1;xcallno]->rxcore.tv_sec || iaxs[fr->callno]->rxcore.tv_usec) { + if ( (!fromtrunk) && (iaxs[fr->callno]->rxcore.tv_sec || iaxs[fr->callno]->rxcore.tv_usec) ) { fr->af.delivery.tv_sec = iaxs[fr->callno]->rxcore.tv_sec; fr->af.delivery.tv_usec = iaxs[fr->callno]->rxcore.tv_usec; fr->af.delivery.tv_sec += fr->ts / 1000; @@ -1807,7 +1811,7 @@ static int schedule_delivery(struct iax_frame *fr, int reallydeliver, int update else { #if 0 if (reallydeliver) - ast_log(LOG_DEBUG, "schedule_delivery: set delivery to 0 as we don't have an rxcore yet.\n"); + ast_log(LOG_DEBUG, "schedule_delivery: set delivery to 0 as we don't have an rxcore yet, or frame is from trunk.\n"); #endif fr->af.delivery.tv_sec = 0; fr->af.delivery.tv_usec = 0; @@ -1888,7 +1892,8 @@ static int schedule_delivery(struct iax_frame *fr, int reallydeliver, int update delay = maxjitterbuffer; /* If jitter buffer is disabled then just pretend the frame is "right on time" */ - if (!iaxs[fr->callno]->usejitterbuf) + /* If frame came from trunk, also don't do any delay */ + if ( (!iaxs[fr->callno]->usejitterbuf) || fromtrunk ) delay = 0; if (option_debug) { @@ -2628,8 +2633,10 @@ tackygoto: cs[1] = cs[2]; } lock_both(callno0, callno1); - iaxs[callno0]->bridgecallno = 0; - iaxs[callno1]->bridgecallno = 0; + if(iaxs[callno0]) + iaxs[callno0]->bridgecallno = 0; + if(iaxs[callno1]) + iaxs[callno1]->bridgecallno = 0; unlock_both(callno0, callno1); return res; } @@ -2708,8 +2715,8 @@ static struct ast_channel *ast_iax2_new(int callno, int state, int capability) tmp->type = type; /* We can support any format by default, until we get restricted */ tmp->nativeformats = capability; - tmp->readformat = 0; - tmp->writeformat = 0; + tmp->readformat = ast_best_codec(capability); + tmp->writeformat = ast_best_codec(capability); tmp->pvt->pvt = CALLNO_TO_PTR(i->callno); tmp->pvt->send_digit = iax2_digit; tmp->pvt->send_text = iax2_sendtext; @@ -2808,6 +2815,18 @@ static unsigned int fix_peerts(struct timeval *tv, int callno, unsigned int ts) return ms + ts; } +static void add_ms(struct timeval *tv, int ms) { + tv->tv_usec += ms * 1000; + if(tv->tv_usec > 1000000) { + tv->tv_usec -= 1000000; + tv->tv_sec++; + } + if(tv->tv_usec < 0) { + tv->tv_usec += 1000000; + tv->tv_sec--; + } +} + static unsigned int calc_timestamp(struct chan_iax2_pvt *p, unsigned int ts, struct ast_frame *f) { struct timeval tv; @@ -2828,6 +2847,8 @@ static unsigned int calc_timestamp(struct chan_iax2_pvt *p, unsigned int ts, str delivery = &f->delivery; } else if (f->frametype == AST_FRAME_IAX) { genuine = 1; + } else if (f->frametype == AST_FRAME_CNG) { + p->notsilenttx = 0; } } if (!p->offset.tv_sec && !p->offset.tv_usec) { @@ -2852,15 +2873,33 @@ static unsigned int calc_timestamp(struct chan_iax2_pvt *p, unsigned int ts, str ms = 0; if (voice) { /* On a voice frame, use predicted values if appropriate */ - if (abs(ms - p->nextpred) <= MAX_TIMESTAMP_SKEW) { + if (p->notsilenttx && abs(ms - p->nextpred) <= MAX_TIMESTAMP_SKEW) { + /* Adjust our txcore, keeping voice and + non-voice synchronized */ + add_ms(&p->offset, (int)(ms - p->nextpred)/10); + if (!p->nextpred) { p->nextpred = ms; /*f->samples / 8;*/ if (p->nextpred <= p->lastsent) p->nextpred = p->lastsent + 3; } ms = p->nextpred; - } else + } else { + /* in this case, just use the actual + * time, since we're either way off + * (shouldn't happen), or we're ending a + * silent period -- and seed the next + * predicted time. Also, round ms to the + * next multiple of frame size (so our + * silent periods are multiples of + * frame size too) */ + int diff = ms % (f->samples / 8); + if(diff) + ms += f->samples/8 - diff; + p->nextpred = ms; + p->notsilenttx = 1; + } } else { /* On a dataframe, use last value + 3 (to accomodate jitter buffer shrinking) if appropriate unless it's a genuine frame */ @@ -3849,12 +3888,15 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies * ast_log(LOG_NOTICE, "Empty registration from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr)); return -1; } - + /* We release the lock for the call to prevent a deadlock, but it's okay because + only the current thread could possibly make it go away or make changes */ + ast_mutex_unlock(&iaxsl[callno]); ast_mutex_lock(&peerl.lock); for (p = peerl.peers; p ; p = p->next) if (!strcasecmp(p->name, peer)) break; ast_mutex_unlock(&peerl.lock); + ast_mutex_lock(&iaxsl[callno]); #ifdef MYSQL_FRIENDS if (!p) p = mysql_peer(peer); @@ -4340,7 +4382,8 @@ static int expire_registry(void *data) p->expire = -1; /* Reset expirey value */ p->expirey = expirey; - ast_db_del("IAX/Registry", p->name); + if (p->temponly !=1) + ast_db_del("IAX/Registry", p->name); register_peer_exten(p, 0); if (iax2_regfunk) iax2_regfunk(p->name, 0); @@ -4356,7 +4399,7 @@ static void reg_source_db(struct iax2_peer *p) struct in_addr in; char iabuf[INET_ADDRSTRLEN]; char *c, *d; - if (!ast_db_get("IAX/Registry", p->name, data, sizeof(data))) { + if ((p->temponly != 1) && (!ast_db_get("IAX/Registry", p->name, data, sizeof(data)))) { c = strchr(data, ':'); if (c) { *c = '\0'; @@ -4416,7 +4459,7 @@ static int update_registry(char *name, struct sockaddr_in *sin, int callno, char if (iax2_regfunk) iax2_regfunk(p->name, 1); snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), ntohs(sin->sin_port), p->expirey); - if (sin->sin_addr.s_addr) { + if ((p->temponly != 1) && (sin->sin_addr.s_addr)) { ast_db_put("IAX/Registry", p->name, data); if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Registered '%s' (%s) at %s:%d\n", p->name, @@ -5079,10 +5122,10 @@ static int socket_read(int *id, int fd, short events, void *cbdata) if (iaxs[fr.callno]->bridgecallno) { forward_delivery(&fr); } else { - schedule_delivery(iaxfrdup2(&fr), 1, updatehistory); + schedule_delivery(iaxfrdup2(&fr), 1, updatehistory, 1); } #else - schedule_delivery(iaxfrdup2(&fr), 1, updatehistory); + schedule_delivery(iaxfrdup2(&fr), 1, updatehistory, 1); #endif } } else { @@ -5148,7 +5191,9 @@ static int socket_read(int *id, int fd, short events, void *cbdata) ast_mutex_unlock(&iaxsl[fr.callno]); return 1; } - if (!inaddrcmp(&sin, &iaxs[fr.callno]->addr) && !minivid) + if (!inaddrcmp(&sin, &iaxs[fr.callno]->addr) && !minivid && + f.subclass != IAX_COMMAND_TXCNT && /* for attended transfer */ + f.subclass != IAX_COMMAND_TXACC) /* for attended transfer */ iaxs[fr.callno]->peercallno = (unsigned short)(ntohs(mh->callno) & ~IAX_FLAG_FULL); if (ntohs(mh->callno) & IAX_FLAG_FULL) { if (option_debug) @@ -5170,12 +5215,18 @@ static int socket_read(int *id, int fd, short events, void *cbdata) if ((iaxs[fr.callno]->iseqno != fr.oseqno) && (iaxs[fr.callno]->iseqno || ((f.subclass != IAX_COMMAND_TXCNT) && + (f.subclass != IAX_COMMAND_TXREADY) && /* for attended transfer */ + (f.subclass != IAX_COMMAND_TXREL) && /* for attended transfer */ + (f.subclass != IAX_COMMAND_UNQUELCH ) && /* for attended transfer */ (f.subclass != IAX_COMMAND_TXACC)) || - (f.subclass != AST_FRAME_IAX))) { + (f.frametype != AST_FRAME_IAX))) { if ( ((f.subclass != IAX_COMMAND_ACK) && (f.subclass != IAX_COMMAND_INVAL) && (f.subclass != IAX_COMMAND_TXCNT) && + (f.subclass != IAX_COMMAND_TXREADY) && /* for attended transfer */ + (f.subclass != IAX_COMMAND_TXREL) && /* for attended transfer */ + (f.subclass != IAX_COMMAND_UNQUELCH ) && /* for attended transfer */ (f.subclass != IAX_COMMAND_TXACC) && (f.subclass != IAX_COMMAND_VNAK)) || (f.frametype != AST_FRAME_IAX)) { @@ -5335,7 +5386,7 @@ retryowner: if (option_debug) ast_log(LOG_DEBUG, "IAX subclass %d received\n", f.subclass); /* Go through the motions of delivering the packet without actually doing so */ - schedule_delivery(&fr, 0, updatehistory); + schedule_delivery(&fr, 0, updatehistory, 0); switch(f.subclass) { case IAX_COMMAND_ACK: /* Do nothing */ @@ -5613,13 +5664,15 @@ retryowner2: if (iaxs[fr.callno]->peerpoke) { peer = iaxs[fr.callno]->peerpoke; if ((peer->lastms < 0) || (peer->lastms > peer->maxms)) { - if (iaxs[fr.callno]->pingtime <= peer->maxms) + if (iaxs[fr.callno]->pingtime <= peer->maxms) { ast_log(LOG_NOTICE, "Peer '%s' is now REACHABLE!\n", peer->name); manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: IAX2/%s\r\nPeerStatus: Reachable\r\nTime: %d\r\n", peer->name,iaxs[fr.callno]->pingtime); + } } else if ((peer->lastms > 0) && (peer->lastms <= peer->maxms)) { - if (iaxs[fr.callno]->pingtime > peer->maxms) + if (iaxs[fr.callno]->pingtime > peer->maxms) { ast_log(LOG_NOTICE, "Peer '%s' is now TOO LAGGED (%d ms)!\n", peer->name, iaxs[fr.callno]->pingtime); manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: IAX2/%s\r\nPeerStatus: Lagged\r\nTime: %d\r\n", peer->name,iaxs[fr.callno]->pingtime); + } } peer->lastms = iaxs[fr.callno]->pingtime; if (peer->pokeexpire > -1) @@ -5885,6 +5938,7 @@ retryowner2: /* Send ack immediately, rather than waiting until we've changed addresses */ send_command_immediate(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.iseqno); complete_transfer(fr.callno, &ies); + stop_stuff(fr.callno); /* for attended transfer to work with libiax */ break; case IAX_COMMAND_DPREP: complete_dpreply(iaxs[fr.callno], &ies); @@ -5929,7 +5983,7 @@ retryowner2: if (iaxs[fr.callno]->videoformat > 0) f.subclass = iaxs[fr.callno]->videoformat | (ntohs(vh->ts) & 0x8000 ? 1 : 0); else { - ast_log(LOG_WARNING, "Received mini frame before first full voice frame\n "); + ast_log(LOG_WARNING, "Received mini frame before first full video frame\n "); iax2_vnak(fr.callno); ast_mutex_unlock(&iaxsl[fr.callno]); return 1; @@ -5993,12 +6047,12 @@ retryowner2: forward_delivery(&fr); } else { duped_fr = iaxfrdup2(&fr); - schedule_delivery(duped_fr, 1, updatehistory); + schedule_delivery(duped_fr, 1, updatehistory, 0); fr.ts = duped_fr->ts; } #else duped_fr = iaxfrdup2(&fr); - schedule_delivery(duped_fr, 1, updatehistory); + schedule_delivery(duped_fr, 1, updatehistory, 0); fr.ts = duped_fr->ts; #endif @@ -6252,7 +6306,7 @@ static struct ast_channel *iax2_request(char *type, int format, void *data) ast_mutex_unlock(&iaxsl[callno]); if (c) { /* Choose a format we can live with */ - if (c->nativeformats & format) + if (c->nativeformats & format) c->nativeformats &= format; else { native = c->nativeformats; @@ -6265,6 +6319,8 @@ static struct ast_channel *iax2_request(char *type, int format, void *data) } c->nativeformats = native; } + c->readformat = ast_best_codec(c->nativeformats); + c->writeformat = c->readformat; } return c; } @@ -6386,9 +6442,11 @@ static struct iax2_peer *build_peer(char *name, struct ast_variable *v) } else { ast_mutex_unlock(&peerl.lock); peer = malloc(sizeof(struct iax2_peer)); - memset(peer, 0, sizeof(struct iax2_peer)); - peer->expire = -1; - peer->pokeexpire = -1; + if (peer) { + memset(peer, 0, sizeof(struct iax2_peer)); + peer->expire = -1; + peer->pokeexpire = -1; + } } if (peer) { peer->messagedetail = globalmessagedetail; @@ -6553,7 +6611,8 @@ static struct iax2_user *build_user(char *name, struct ast_variable *v) } else { ast_mutex_unlock(&userl.lock); user = malloc(sizeof(struct iax2_user)); - memset(user, 0, sizeof(struct iax2_user)); + if (user) + memset(user, 0, sizeof(struct iax2_user)); } if (user) { diff --git a/channels/chan_local.c b/channels/chan_local.c index fc8fd8820a..439bd3dfe8 100755 --- a/channels/chan_local.c +++ b/channels/chan_local.c @@ -304,6 +304,10 @@ static int local_hangup(struct ast_channel *ast) p->owner = NULL; ast->pvt->pvt = NULL; + ast_mutex_lock(&usecnt_lock); + usecnt--; + ast_mutex_unlock(&usecnt_lock); + if (!p->owner && !p->chan) { /* Okay, done with the private part now, too. */ glaredetect = p->glaredetect; @@ -371,7 +375,7 @@ static struct local_pvt *local_alloc(char *data, int format) strncpy(tmp->context, "default", sizeof(tmp->context) - 1); tmp->reqformat = format; if (!ast_exists_extension(NULL, tmp->context, tmp->exten, 1, NULL)) { - ast_log(LOG_NOTICE, "No such extension/context %s@%s creating local channel\n", tmp->context, tmp->exten); + ast_log(LOG_NOTICE, "No such extension/context %s@%s creating local channel\n", tmp->exten, tmp->context); ast_mutex_destroy(&tmp->lock); free(tmp); tmp = NULL; @@ -441,6 +445,7 @@ static struct ast_channel *local_new(struct local_pvt *p, int state) p->chan = tmp2; ast_mutex_lock(&usecnt_lock); usecnt++; + usecnt++; ast_mutex_unlock(&usecnt_lock); ast_update_use_count(); strncpy(tmp->context, p->context, sizeof(tmp->context)-1); diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c index 22058c7752..102c28b146 100755 --- a/channels/chan_mgcp.c +++ b/channels/chan_mgcp.c @@ -686,8 +686,7 @@ static int mgcp_postrequest(struct mgcp_endpoint *p, struct mgcp_subchannel *sub if (gettimeofday(&tv, NULL) < 0) { /* This shouldn't ever happen, but let's be sure */ ast_log(LOG_NOTICE, "gettimeofday() failed!\n"); - } - else { + } else { msg->expire = tv.tv_sec * 1000 + tv.tv_usec / 1000 + DEFAULT_RETRANS; if (gw->retransid == -1) @@ -862,8 +861,9 @@ static int mgcp_hangup(struct ast_channel *ast) struct mgcp_subchannel *sub = ast->pvt->pvt; struct mgcp_endpoint *p = sub->parent; - if (option_debug) + if (option_debug) { ast_log(LOG_DEBUG, "mgcp_hangup(%s)\n", ast->name); + } if (!ast->pvt->pvt) { ast_log(LOG_DEBUG, "Asked to hangup channel not connected\n"); return 0; @@ -872,14 +872,14 @@ static int mgcp_hangup(struct ast_channel *ast) ast_log(LOG_DEBUG, "Invalid magic. MGCP subchannel freed up already.\n"); return 0; } + ast_mutex_lock(&sub->lock); if (mgcpdebug) { ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_hangup(%s) on %s@%s\n", ast->name, p->name, p->parent->name); } - if ((p->dtmfmode & MGCP_DTMF_INBAND) && (p->dsp != NULL)){ + if ((p->dtmfmode & MGCP_DTMF_INBAND) && p->dsp) { /* SC: check whether other channel is active. */ - if (!sub->next->owner) - { + if (!sub->next->owner) { if (mgcpdebug) { ast_verbose(VERBOSE_PREFIX_2 "MGCP free dsp on %s@%s\n", p->name, p->parent->name); } @@ -887,7 +887,6 @@ static int mgcp_hangup(struct ast_channel *ast) p->dsp = NULL; } } - ast_mutex_lock(&sub->lock); sub->owner = NULL; if (strlen(sub->cxident)) { @@ -967,7 +966,7 @@ static int mgcp_show_endpoints(int fd, int argc, char *argv[]) e = g->endpoints; ast_cli(fd, "Gateway '%s' at %s (%s)\n", g->name, g->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), g->addr.sin_addr) : ast_inet_ntoa(iabuf, sizeof(iabuf), g->defaddr.sin_addr), g->dynamic ? "Dynamic" : "Static"); while(e) { - // JS: Don't show wilcard endpoint + /* JS: Don't show wilcard endpoint */ if (strcmp(e->name, g->wcardep) !=0) ast_cli(fd, " -- '%s@%s in '%s' is %s\n", e->name, g->name, e->context, e->sub->owner ? "active" : "idle"); hasendpoints = 1; @@ -1078,7 +1077,11 @@ static struct ast_frame *mgcp_rtp_read(struct mgcp_subchannel *sub) { /* Retrieve audio/etc from channel. Assumes sub->lock is already held. */ struct ast_frame *f; + static struct ast_frame null_frame = { AST_FRAME_NULL, }; f = ast_rtp_read(sub->rtp); + /* Don't send RFC2833 if we're not supposed to */ + if (f && (f->frametype == AST_FRAME_DTMF) && !(sub->parent->dtmfmode & MGCP_DTMF_RFC2833)) + return &null_frame; if (sub->owner) { /* We already hold the channel lock */ if (f->frametype == AST_FRAME_VOICE) { @@ -1089,7 +1092,7 @@ static struct ast_frame *mgcp_rtp_read(struct mgcp_subchannel *sub) ast_set_write_format(sub->owner, sub->owner->writeformat); } /* Courtesy fearnor aka alex@pilosoft.com */ - if (sub->parent->dtmfmode & MGCP_DTMF_INBAND) { + if ((sub->parent->dtmfmode & MGCP_DTMF_INBAND) && (sub->parent->dsp)) { #if 0 ast_log(LOG_NOTICE, "MGCP ast_dsp_process\n"); #endif @@ -1103,18 +1106,12 @@ static struct ast_frame *mgcp_rtp_read(struct mgcp_subchannel *sub) static struct ast_frame *mgcp_read(struct ast_channel *ast) { - struct ast_frame *fr; + struct ast_frame *f; struct mgcp_subchannel *sub = ast->pvt->pvt; - static struct ast_frame null_frame = { AST_FRAME_NULL, }; ast_mutex_lock(&sub->lock); - fr = mgcp_rtp_read(sub); - if (!(sub->parent->dtmfmode & MGCP_DTMF_RFC2833)) { - if (fr && (fr->frametype == AST_FRAME_DTMF)) { - fr = &null_frame; - } - } + f = mgcp_rtp_read(sub); ast_mutex_unlock(&sub->lock); - return fr; + return f; } static int mgcp_write(struct ast_channel *ast, struct ast_frame *frame) @@ -1313,7 +1310,8 @@ static struct ast_channel *mgcp_new(struct mgcp_subchannel *sub, int state) return tmp; } -static char* get_sdp_by_line(char* line, char *name, int nameLen) { +static char* get_sdp_by_line(char* line, char *name, int nameLen) +{ if (strncasecmp(line, name, nameLen) == 0 && line[nameLen] == '=') { char* r = line + nameLen + 1; while (*r && (*r < 33)) ++r; @@ -1323,7 +1321,8 @@ static char* get_sdp_by_line(char* line, char *name, int nameLen) { return ""; } -static char *get_sdp(struct mgcp_request *req, char *name) { +static char *get_sdp(struct mgcp_request *req, char *name) +{ int x; int len = strlen(name); char *r; @@ -1335,12 +1334,13 @@ static char *get_sdp(struct mgcp_request *req, char *name) { return ""; } -static void sdpLineNum_iterator_init(int* iterator) { +static void sdpLineNum_iterator_init(int* iterator) +{ *iterator = 0; } -static char* get_sdp_iterate(int* iterator, - struct mgcp_request *req, char *name) { +static char* get_sdp_iterate(int* iterator, struct mgcp_request *req, char *name) +{ int len = strlen(name); char *r; while (*iterator < req->lines) { @@ -1658,7 +1658,7 @@ static int process_sdp(struct mgcp_subchannel *sub, struct mgcp_request *req) #if 0 printf("Peer RTP is at port %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port)); #endif - // Scan through the RTP payload types specified in a "m=" line: + /* Scan through the RTP payload types specified in a "m=" line: */ ast_rtp_pt_clear(sub->rtp); codecs = m + len; while(strlen(codecs)) { @@ -1670,18 +1670,18 @@ static int process_sdp(struct mgcp_subchannel *sub, struct mgcp_request *req) codecs += len; } - // Next, scan through each "a=rtpmap:" line, noting each - // specified RTP payload type (with corresponding MIME subtype): + /* Next, scan through each "a=rtpmap:" line, noting each + specified RTP payload type (with corresponding MIME subtype): */ sdpLineNum_iterator_init(&iterator); while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') { char* mimeSubtype = strdup(a); // ensures we have enough space if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) != 2) continue; - // Note: should really look at the 'freq' and '#chans' params too + /* Note: should really look at the 'freq' and '#chans' params too */ ast_rtp_set_rtpmap_type(sub->rtp, codec, "audio", mimeSubtype); free(mimeSubtype); } - // Now gather all of the codecs that were asked for: + /* Now gather all of the codecs that were asked for: */ ast_rtp_get_current_formats(sub->rtp, &peercapability, &peerNonCodecCapability); p->capability = capability & peercapability; @@ -1696,7 +1696,6 @@ static int process_sdp(struct mgcp_subchannel *sub, struct mgcp_request *req) return -1; } return 0; - } static int add_header(struct mgcp_request *req, char *var, char *value) @@ -1712,9 +1711,9 @@ static int add_header(struct mgcp_request *req, char *var, char *value) req->header[req->headers] = req->data + req->len; snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s: %s\r\n", var, value); req->len += strlen(req->header[req->headers]); - if (req->headers < MGCP_MAX_HEADERS) + if (req->headers < MGCP_MAX_HEADERS) { req->headers++; - else { + } else { ast_log(LOG_WARNING, "Out of header space\n"); return -1; } @@ -1735,9 +1734,9 @@ static int add_line(struct mgcp_request *req, char *line) req->line[req->lines] = req->data + req->len; snprintf(req->line[req->lines], sizeof(req->data) - req->len, "%s", line); req->len += strlen(req->line[req->lines]); - if (req->lines < MGCP_MAX_LINES) + if (req->lines < MGCP_MAX_LINES) { req->lines++; - else { + } else { ast_log(LOG_WARNING, "Out of line space\n"); return -1; } @@ -1785,7 +1784,7 @@ static int init_req(struct mgcp_endpoint *p, struct mgcp_request *req, char *ver static int respprep(struct mgcp_request *resp, struct mgcp_endpoint *p, char *msg, struct mgcp_request *req, char *msgrest) { - memset(resp, 0, sizeof(*resp)); + memset(resp, 0, sizeof(struct mgcp_request)); init_resp(resp, msg, req, msgrest); return 0; } @@ -1947,7 +1946,7 @@ static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp add_header(&resp, "X", sub->txident); add_header(&resp, "I", sub->cxident); /*add_header(&resp, "S", "");*/ - ast_rtp_offered_from_local(rtp, 0); + ast_rtp_offered_from_local(sub->rtp, 0); add_sdp(&resp, sub, rtp); /* SC: fill in new fields */ resp.cmd = MGCP_CMD_MDCX; @@ -1981,7 +1980,7 @@ static int transmit_connect_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp /* SC: X header should not be sent. kept for compatibility */ add_header(&resp, "X", sub->txident); /*add_header(&resp, "S", "");*/ - ast_rtp_offered_from_local(rtp, 1); + ast_rtp_offered_from_local(sub->rtp, 1); add_sdp(&resp, sub, rtp); /* SC: fill in new fields */ resp.cmd = MGCP_CMD_CRCX; @@ -2115,7 +2114,7 @@ static int transmit_audit_endpoint(struct mgcp_endpoint *p) struct mgcp_request resp; reqprep(&resp, p, "AUEP"); /* SC: removed unknown param VS */ - //add_header(&resp, "F", "A,R,D,S,X,N,I,T,O,ES,E,MD,M"); + /*add_header(&resp, "F", "A,R,D,S,X,N,I,T,O,ES,E,MD,M");*/ add_header(&resp, "F", "A"); /* SC: fill in new fields */ resp.cmd = MGCP_CMD_AUEP; @@ -2411,7 +2410,7 @@ static void start_rtp(struct mgcp_subchannel *sub) sub->rtp = NULL; } /* Allocate the RTP now */ - sub->rtp = ast_rtp_new(sched, io, 1, 0); + sub->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); if (sub->rtp && sub->owner) sub->owner->fds[0] = ast_rtp_fd(sub->rtp); if (sub->rtp) @@ -2860,7 +2859,7 @@ static int handle_request(struct mgcp_subchannel *sub, struct mgcp_request *req, if (option_verbose > 2 && (strcmp(p->name, p->parent->wcardep) != 0)) { ast_verbose(VERBOSE_PREFIX_3 "Resetting interface %s@%s\n", p->name, p->parent->name); } - // JS: For RSIP on wildcard we reset all endpoints + /* JS: For RSIP on wildcard we reset all endpoints */ if (!strcmp(p->name, p->parent->wcardep)) { /* Reset all endpoints */ struct mgcp_endpoint *tmp_ep; @@ -2868,7 +2867,7 @@ static int handle_request(struct mgcp_subchannel *sub, struct mgcp_request *req, g = p->parent; tmp_ep = g->endpoints; while (tmp_ep) { - //if ((strcmp(tmp_ep->name, "*") != 0) && (strcmp(tmp_ep->name, "aaln/*") != 0)) { + /*if ((strcmp(tmp_ep->name, "*") != 0) && (strcmp(tmp_ep->name, "aaln/" "*") != 0)) {*/ if (strcmp(tmp_ep->name, g->wcardep) != 0) { struct mgcp_subchannel *tmp_sub, *first_sub; if (option_verbose > 2) { @@ -2879,7 +2878,7 @@ static int handle_request(struct mgcp_subchannel *sub, struct mgcp_request *req, tmp_sub = tmp_ep->sub; while (tmp_sub) { if (tmp_sub->owner) - ast_softhangup(sub->owner, AST_SOFTHANGUP_DEV); + ast_softhangup(tmp_sub->owner, AST_SOFTHANGUP_DEV); tmp_sub = tmp_sub->next; if (tmp_sub == first_sub) break; @@ -2928,7 +2927,9 @@ static int handle_request(struct mgcp_subchannel *sub, struct mgcp_request *req, return -1; } /* do not let * confrnce two down channels */ - if( sub->owner && sub->owner->_state == AST_STATE_DOWN && !sub->next->owner) return -1; + if( sub->owner && sub->owner->_state == AST_STATE_DOWN && !sub->next->owner) { + return -1; + } if (p->callwaiting || p->transfer || p->threewaycalling) { if (option_verbose > 2) { @@ -3046,9 +3047,13 @@ static int handle_request(struct mgcp_subchannel *sub, struct mgcp_request *req, } else { /* SC: verbose level check */ if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "MGCP handle_request(%s@%s-%d) ast_channel already destroyed\n", - p->name, p->parent->name, sub->id); + ast_verbose(VERBOSE_PREFIX_3 "MGCP handle_request(%s@%s-%d) ast_channel already destroyed, resending DLCX.\n", + p->name, p->parent->name, sub->id); } + /* Instruct the other side to remove the connection since it apparently * + * still thinks the channel is active. * + * For Cisco IAD2421 /BAK/ */ + transmit_connection_del(sub); } } if ((p->hookstate == MGCP_ONHOOK) && (!sub->rtp) && (!sub->next->rtp)) { @@ -3081,11 +3086,9 @@ static int handle_request(struct mgcp_subchannel *sub, struct mgcp_request *req, if (strstr(p->curtone, "wt") && (ev[0] == 'A')) { memset(p->curtone, 0, sizeof(p->curtone)); } - } - else if (!strcasecmp(ev, "T")) { + } else if (!strcasecmp(ev, "T")) { /* Digit timeout -- unimportant */ - } - else if (!strcasecmp(ev, "ping")) { + } else if (!strcasecmp(ev, "ping")) { /* ping -- unimportant */ } else { ast_log(LOG_NOTICE, "Received unknown event '%s' from %s@%s\n", ev, p->name, p->parent->name); @@ -3140,7 +3143,7 @@ static int mgcpsock_read(int *id, int fd, short events, void *ignore) int ident; char iabuf[INET_ADDRSTRLEN]; len = sizeof(sin); - memset(&req, 0, sizeof(req)); + memset(&req, 0, sizeof(struct mgcp_request)); res = recvfrom(mgcpsock, req.data, sizeof(req.data) - 1, 0, (struct sockaddr *)&sin, &len); if (res < 0) { if (errno != ECONNREFUSED) @@ -3150,8 +3153,8 @@ static int mgcpsock_read(int *id, int fd, short events, void *ignore) req.data[res] = '\0'; req.len = res; if (mgcpdebug) { - ast_verbose("MGCP read: \n%s\nfrom %s:%d", req.data, ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port)); - } + ast_verbose("MGCP read: \n%s\nfrom %s:%d\n", req.data, ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port)); + } parse(&req); if (req.headers < 1) { /* Must have at least one header */ @@ -3257,10 +3260,10 @@ static void *do_monitor(void *data) /* Lock the network interface */ ast_mutex_lock(&netlock); +#if 0 /* XXX THIS IS COMPLETELY HOSED */ /* The gateway goes into a state of panic */ /* If the vmwi indicator is sent while it is reseting interfaces */ -#if 0 lastpass = thispass; thispass = time(NULL); g = gateways; @@ -3385,8 +3388,9 @@ static struct ast_channel *mgcp_request(char *type, int format, void *data) return NULL; } tmpc = mgcp_new(sub->owner ? sub->next : sub, AST_STATE_DOWN); - if (!tmpc) + if (!tmpc) { ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp); + } restart_monitor(); return tmpc; } @@ -3516,8 +3520,6 @@ static struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v) adsi = ast_true(v->value); } else if (!strcasecmp(v->name, "callreturn")) { callreturn = ast_true(v->value); - } else if (!strcasecmp(v->name, "immediate")) { - immediate = ast_true(v->value); } else if (!strcasecmp(v->name, "callwaiting")) { callwaiting = ast_true(v->value); } else if (!strcasecmp(v->name, "slowsequence")) { @@ -3555,7 +3557,7 @@ static struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v) e->needaudit = 1; } strncpy(gw->wcardep, v->value, sizeof(gw->wcardep)-1); - //strncpy(e->name, "aaln/*", sizeof(e->name) - 1); + /*strncpy(e->name, "aaln/" "*", sizeof(e->name) - 1);*/ /* XXX Should we really check for uniqueness?? XXX */ strncpy(e->context, context, sizeof(e->context) - 1); strncpy(e->callerid, callerid, sizeof(e->callerid) - 1); @@ -3748,6 +3750,8 @@ static struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v) } return NULL; } + gw->defaddr.sin_family = AF_INET; + gw->addr.sin_family = AF_INET; if (gw->defaddr.sin_addr.s_addr && !ntohs(gw->defaddr.sin_port)) gw->defaddr.sin_port = htons(DEFAULT_MGCP_GW_PORT); if (gw->addr.sin_addr.s_addr && !ntohs(gw->addr.sin_port)) @@ -4226,4 +4230,3 @@ char *description() { return desc; } - diff --git a/channels/chan_modem.c b/channels/chan_modem.c index c2f3bf0f9e..50a63bb756 100755 --- a/channels/chan_modem.c +++ b/channels/chan_modem.c @@ -509,6 +509,18 @@ static int modem_write(struct ast_channel *ast, struct ast_frame *frame) return res; } +static int modem_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) +{ + struct ast_modem_pvt *p = newchan->pvt->pvt; +ast_log(LOG_WARNING, "fixup called\n"); + if (p->owner!=oldchan) { + ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n",oldchan,p->owner); + return -1; + } + p->owner = newchan; + return 0; +} + struct ast_channel *ast_modem_new(struct ast_modem_pvt *i, int state) { struct ast_channel *tmp; @@ -528,6 +540,7 @@ struct ast_channel *ast_modem_new(struct ast_modem_pvt *i, int state) tmp->pvt->answer = modem_answer; tmp->pvt->read = modem_read; tmp->pvt->write = modem_write; + tmp->pvt->fixup = modem_fixup; strncpy(tmp->context, i->context, sizeof(tmp->context)-1); if (strlen(i->cid)) tmp->callerid = strdup(i->cid); @@ -707,8 +720,8 @@ static struct ast_modem_pvt *mkif(char *iface) #endif tmp = malloc(sizeof(struct ast_modem_pvt)); - memset(tmp, 0, sizeof(struct ast_modem_pvt)); if (tmp) { + memset(tmp, 0, sizeof(struct ast_modem_pvt)); tmp->fd = open(iface, O_RDWR | O_NONBLOCK); if (tmp->fd < 0) { ast_log(LOG_WARNING, "Unable to open '%s'\n", iface); diff --git a/channels/chan_phone.c b/channels/chan_phone.c index bacfaa3c48..d817776e75 100755 --- a/channels/chan_phone.c +++ b/channels/chan_phone.c @@ -33,6 +33,10 @@ #include #include /* Still use some IXJ specific stuff */ +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +# include +#endif #include #include "DialTone.h" diff --git a/channels/chan_sip.c b/channels/chan_sip.c index be81665c8e..51f6dada57 100755 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -129,7 +129,9 @@ static char *tdesc = "Session Initiation Protocol (SIP)"; static char *config = "sip.conf"; #define DEFAULT_SIP_PORT 5060 /* From RFC 2543 */ -#define SIP_MAX_PACKET 1500 /* Also from RFC 2543, should sub headers tho */ +/* From RFC2543, this should be 1500, so it's up to the ability of + remote devices as to whether it will be accepted when more than that*/ +#define SIP_MAX_PACKET 4096 #define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER" @@ -168,6 +170,7 @@ static int global_ospauth = 0; static int usecnt =0; AST_MUTEX_DEFINE_STATIC(usecnt_lock); + /* Protect the interface list (of sip_pvt's) */ AST_MUTEX_DEFINE_STATIC(iflock); @@ -222,10 +225,8 @@ static struct io_context *io; #define DEC_OUT_USE 2 #define INC_OUT_USE 3 -static struct sip_codec_pref { - int codec; - struct sip_codec_pref *next; -} *prefs; +static struct ast_codec_pref prefs; + /* sip_request: The data grabbed from the UDP socket */ struct sip_request { @@ -256,6 +257,7 @@ static struct sip_pvt { ast_mutex_t lock; /* Channel private lock */ char callid[80]; /* Global CallID */ char randdata[80]; /* Random data */ + struct ast_codec_pref prefs; /* codec prefs */ unsigned int ocseq; /* Current outgoing seqno */ unsigned int icseq; /* Current incoming seqno */ unsigned int callgroup; /* Call group */ @@ -321,7 +323,7 @@ static struct sip_pvt { char nonce[256]; /* Authorization nonce */ char opaque[256]; /* Opaque nonsense */ char qop[80]; /* Quality of Protection, since SIP wasn't complicated enough yet. */ - char domain[256]; /* Authorization nonce */ + char domain[256]; /* Authorization domain */ char lastmsg[256]; /* Last Message sent/received */ int amaflags; /* AMA Flags */ int pendinginvite; /* Any pending invite */ @@ -391,6 +393,7 @@ struct sip_user { char useragent[256]; /* User agent in SIP request */ unsigned int callgroup; unsigned int pickupgroup; + struct ast_codec_pref prefs; /* codec prefs */ int nat; int hascallerid; int amaflags; @@ -432,6 +435,7 @@ struct sip_peer { char language[MAX_LANGUAGE]; char musicclass[MAX_LANGUAGE]; /* Music on Hold class */ char useragent[256]; /* User agent in SIP request */ + struct ast_codec_pref prefs; /* codec prefs */ int lastmsgssent; time_t lastmsgcheck; int dynamic; @@ -506,6 +510,15 @@ struct sip_registry { char callid[80]; /* Global CallID for this registry */ unsigned int ocseq; /* Sequence number we got to for REGISTERs for this registry */ struct sockaddr_in us; /* Who the server thinks we are */ + + /* Saved headers */ + char realm[256]; /* Authorization realm */ + char nonce[256]; /* Authorization nonce */ + char domain[256]; /* Authorization domain */ + char opaque[256]; /* Opaque nonsense */ + char qop[80]; /* Quality of Protection. */ + + char lastmsg[256]; /* Last Message sent/received */ struct sip_registry *next; }; @@ -688,6 +701,7 @@ static int retrans_pkt(void *data) ast_mutex_lock(&pkt->owner->lock); } if (pkt->owner->owner) { + pkt->owner->alreadygone=1; ast_queue_hangup(pkt->owner->owner); ast_mutex_unlock(&pkt->owner->owner->lock); } else { @@ -827,6 +841,15 @@ static int __sip_ack(struct sip_pvt *p, int seqno, int resp, const char *msg) return res; } +/* Pretend to ack all packets */ +static int __sip_pretend_ack(struct sip_pvt *p) +{ + while(p->packets) { + __sip_ack(p, p->packets->seqno, (p->packets->flags & FLAG_RESPONSE), p->packets->data); + } + return 0; +} + /*--- __sip_semi_ack: Acks receipt of packet, keep it around (used for provisional responses) ---*/ static int __sip_semi_ack(struct sip_pvt *p, int seqno, int resp, const char *msg) { @@ -1327,7 +1350,7 @@ static int create_addr(struct sip_pvt *r, char *opeer) r->promiscredir = p->promiscredir; strncpy(r->context, p->context,sizeof(r->context)-1); if ((p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) && - (!p->maxms || ((p->lastms > 0) && (p->lastms <= p->maxms)))) { + (!p->maxms || ((p->lastms >= 0) && (p->lastms <= p->maxms)))) { if (p->addr.sin_addr.s_addr) { r->sa.sin_addr = p->addr.sin_addr; r->sa.sin_port = p->addr.sin_port; @@ -1405,71 +1428,8 @@ static int auto_congest(void *nothing) return 0; } -/*--- sip_prefs_free: Free codec list in preference structure ---*/ -static void sip_prefs_free(void) -{ - struct sip_codec_pref *cur, *next; - cur = prefs; - while(cur) { - next = cur->next; - free(cur); - cur = next; - } - prefs = NULL; -} -/*--- sip_pref_remove: Remove codec from pref list ---*/ -static void sip_pref_remove(int format) -{ - struct sip_codec_pref *cur, *prev=NULL; - cur = prefs; - while(cur) { - if (cur->codec == format) { - if (prev) - prev->next = cur->next; - else - prefs = cur->next; - free(cur); - return; - } - prev = cur; - cur = cur->next; - } -} -/*--- sip_pref_append: Append codec to list ---*/ -static int sip_pref_append(int format) -{ - struct sip_codec_pref *cur, *tmp; - sip_pref_remove(format); - tmp = (struct sip_codec_pref *)malloc(sizeof(struct sip_codec_pref)); - if (!tmp) - return -1; - memset(tmp, 0, sizeof(struct sip_codec_pref)); - tmp->codec = format; - if (prefs) { - cur = prefs; - while(cur->next) - cur = cur->next; - cur->next = tmp; - } else - prefs = tmp; - return 0; -} - -/*--- sip_codec_choose: Pick a codec ---*/ -static int sip_codec_choose(int formats) -{ - struct sip_codec_pref *cur; - formats &= ((AST_FORMAT_MAX_AUDIO << 1) - 1); - cur = prefs; - while(cur) { - if (formats & cur->codec) - return cur->codec; - cur = cur->next; - } - return ast_best_codec(formats); -} /*--- sip_call: Initiate SIP call from PBX ---*/ /* used from the dial() application */ @@ -1842,7 +1802,11 @@ static int sip_answer(struct ast_channel *ast) fmt=ast_getformatbyname(codec); if (fmt) { ast_log(LOG_NOTICE, "Changing codec to '%s' for this call because of ${SIP_CODEC) variable\n",codec); - p->jointcapability=fmt; + if (p->jointcapability & fmt) { + p->jointcapability &= fmt; + p->capability &= fmt; + } else + ast_log(LOG_NOTICE, "Ignoring ${SIP_CODEC} variable because it is not shared by both ends.\n"); } else ast_log(LOG_NOTICE, "Ignoring ${SIP_CODEC} variable because of unrecognized/not configured codec (check allow/disallow in sip.conf): %s\n",codec); } @@ -2002,6 +1966,7 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, char *title) { struct ast_channel *tmp; int fmt; + ast_mutex_unlock(&i->lock); /* Don't hold a sip pvt lock while we allocate a channel */ tmp = ast_channel_alloc(1); @@ -2009,12 +1974,14 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, char *title) if (tmp) { /* Select our native format based on codec preference until we receive something from another device to the contrary. */ + ast_mutex_lock(&i->lock); if (i->jointcapability) - tmp->nativeformats = sip_codec_choose(i->jointcapability); + tmp->nativeformats = ast_codec_choose(&i->prefs, i->jointcapability, 1); else if (i->capability) - tmp->nativeformats = sip_codec_choose(i->capability); + tmp->nativeformats = ast_codec_choose(&i->prefs, i->capability, 1); else - tmp->nativeformats = sip_codec_choose(global_capability); + tmp->nativeformats = ast_codec_choose(&i->prefs, global_capability, 1); + ast_mutex_unlock(&i->lock); fmt = ast_best_codec(tmp->nativeformats); if (title) snprintf(tmp->name, sizeof(tmp->name), "SIP/%s-%04x", title, rand() & 0xffff); @@ -2311,9 +2278,11 @@ static struct sip_pvt *sip_alloc(char *callid, struct sockaddr_in *sin, int useg /* Keep track of stuff */ memset(p, 0, sizeof(struct sip_pvt)); ast_mutex_init(&p->lock); + p->initid = -1; p->autokillid = -1; p->stateid = -1; + p->prefs = prefs; #ifdef OSP_SUPPORT p->osphandle = -1; #endif @@ -2540,33 +2509,32 @@ static int lws2sws(char *msgbuf, int len) } /* Check for end-of-line */ if (msgbuf[h] == '\n') { - /* Check for end-of-message */ + /* Check for end-of-message */ if (h + 1 == len) - break; - /* Check for a continuation line */ - if (msgbuf[h + 1] == ' ') { - /* Merge continuation line */ - h++; + break; + /* Check for a continuation line */ + if (msgbuf[h + 1] == ' ' || msgbuf[h + 1] == '\t') { + /* Merge continuation line */ + h++; + continue; + } + /* Propagate LF and start new line */ + msgbuf[t++] = msgbuf[h++]; + lws = 0; continue; } - /* Propagate LF and start new line */ - msgbuf[t++] = msgbuf[h++]; - lws = 0; - continue; - } - - if (msgbuf[h] == ' ' || msgbuf[h] == '\t') { - if (lws) { - h++; + if (msgbuf[h] == ' ' || msgbuf[h] == '\t') { + if (lws) { + h++; + continue; + } + msgbuf[t++] = msgbuf[h++]; + lws = 1; continue; } msgbuf[t++] = msgbuf[h++]; - lws = 1; - continue; - } - msgbuf[t++] = msgbuf[h++]; - if (lws) - lws = 0; + if (lws) + lws = 0; } msgbuf[t] = '\0'; return t; @@ -2786,7 +2754,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) p->noncodeccapability = noncodeccapability & peernoncodeccapability; if (debug) { - const unsigned slen=80; + /* shame on whoever coded this.... */ + const unsigned slen=512; char s1[slen], s2[slen], s3[slen], s4[slen]; ast_verbose("Capabilities: us - %s, peer - audio=%s/video=%s, combined - %s\n", @@ -2801,17 +2770,17 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) ast_getformatname_multiple(s3, slen, p->noncodeccapability)); } if (!p->jointcapability) { - ast_log(LOG_WARNING, "No compatible codecs!\n"); + ast_log(LOG_NOTICE, "No compatible codecs!\n"); return -1; } if (p->owner) { if (!(p->owner->nativeformats & p->jointcapability)) { - const unsigned slen=80; + const unsigned slen=512; char s1[slen], s2[slen]; ast_log(LOG_DEBUG, "Oooh, we need to change our formats since our peer supports only %s and not %s\n", ast_getformatname_multiple(s1, slen, p->jointcapability), ast_getformatname_multiple(s2, slen, p->owner->nativeformats)); - p->owner->nativeformats = sip_codec_choose(p->jointcapability); + p->owner->nativeformats = ast_codec_choose(&p->prefs, p->jointcapability, 1); ast_set_read_format(p->owner, p->owner->readformat); ast_set_write_format(p->owner, p->owner->writeformat); } @@ -2821,6 +2790,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) ast_moh_stop(p->owner->bridge); } else { ast_moh_start(p->owner->bridge, NULL); + if (sendonly) + ast_rtp_stop(p->rtp); } } } @@ -3139,6 +3110,7 @@ static int respprep(struct sip_request *resp, struct sip_pvt *p, char *msg, stru return 0; } +/*--- reqprep: Initialize a SIP request packet ---*/ static int reqprep(struct sip_request *req, struct sip_pvt *p, char *msg, int seqno, int newbranch) { struct sip_request *orig = &p->initreq; @@ -3165,12 +3137,14 @@ static int reqprep(struct sip_request *req, struct sip_pvt *p, char *msg, int se else /* Some implementations (e.g. Uniden UIP200) can't handle rport being in the message!! */ snprintf(p->via, sizeof(p->via), "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); } - if (!strcasecmp(msg, "CANCEL") || !strcasecmp(msg, "ACK")) { - /* MUST use original URI */ + if (!strcasecmp(msg, "CANCEL")) { + c = p->initreq.rlPart2; /* Use original URI */ + } else if (!strcasecmp(msg, "ACK")) { c = p->initreq.rlPart2; } else if (!ast_strlen_zero(p->uri)) { c = p->uri; } else { + /* We have no URI, use To: or From: header as URI (depending on direction) */ if (p->outgoing) strncpy(stripped, get_header(orig, "To"), sizeof(stripped) - 1); else @@ -3340,13 +3314,13 @@ static int add_digit(struct sip_request *req, char digit) /*--- add_sdp: Add Session Description Protocol message ---*/ static int add_sdp(struct sip_request *resp, struct sip_pvt *p) { - int len; - int codec; + int len = 0; + int codec = 0; + int pref_codec = 0; int alreadysent = 0; char costr[80]; struct sockaddr_in sin; struct sockaddr_in vsin; - struct sip_codec_pref *cur; char v[256] = ""; char s[256] = ""; char o[256] = ""; @@ -3357,11 +3331,13 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p) char a[1024] = ""; char a2[1024] = ""; char iabuf[INET_ADDRSTRLEN]; - int x; - int capability; + int x = 0; + int capability = 0 ; struct sockaddr_in dest; struct sockaddr_in vdest = { 0, }; - int debug=sip_debug_test_pvt(p); + int debug=0; + + debug = sip_debug_test_pvt(p); /* XXX We break with the "recommendation" and send our IP, in order that our peer doesn't have to ast_gethostbyname() us XXX */ @@ -3413,9 +3389,10 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p) snprintf(t, sizeof(t), "t=0 0\r\n"); snprintf(m, sizeof(m), "m=audio %d RTP/AVP", ntohs(dest.sin_port)); snprintf(m2, sizeof(m2), "m=video %d RTP/AVP", ntohs(vdest.sin_port)); + /* Prefer the codec we were requested to use, first, no matter what */ if (capability & p->prefcodec) { if (debug) - ast_verbose("Answering/Requesting with root capability %d\n", p->prefcodec); + ast_verbose("Answering/Requesting with root capability 0x%x (%s)\n", p->prefcodec, ast_getformatname(p->prefcodec)); codec = ast_rtp_lookup_code(p->rtp, 1, p->prefcodec); if (codec > -1) { snprintf(costr, sizeof(costr), " %d", codec); @@ -3432,33 +3409,34 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p) alreadysent |= p->prefcodec; } /* Start by sending our preferred codecs */ - cur = prefs; - while(cur) { - if ((capability & cur->codec) && !(alreadysent & cur->codec)) { + for (x = 0 ; x < 32 ; x++) { + if(!(pref_codec = ast_codec_pref_index(&p->prefs,x))) + break; + if ((capability & pref_codec) && !(alreadysent & pref_codec)) { if (debug) - ast_verbose("Answering with preferred capability 0x%x(%s)\n", cur->codec, ast_getformatname(cur->codec)); - codec = ast_rtp_lookup_code(p->rtp, 1, cur->codec); + ast_verbose("Answering with preferred capability 0x%x (%s)\n", pref_codec, ast_getformatname(pref_codec)); + codec = ast_rtp_lookup_code(p->rtp, 1, pref_codec); if (codec > -1) { snprintf(costr, sizeof(costr), " %d", codec); - if (cur->codec <= AST_FORMAT_MAX_AUDIO) { + if (pref_codec <= AST_FORMAT_MAX_AUDIO) { strncat(m, costr, sizeof(m) - strlen(m) - 1); - snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(1, cur->codec)); + snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(1, pref_codec)); strncat(a, costr, sizeof(a) - strlen(a) - 1); } else { strncat(m2, costr, sizeof(m2) - strlen(m2) - 1); - snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/90000\r\n", codec, ast_rtp_lookup_mime_subtype(1, cur->codec)); + snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/90000\r\n", codec, ast_rtp_lookup_mime_subtype(1, pref_codec)); strncat(a2, costr, sizeof(a2) - strlen(a) - 1); } } } - alreadysent |= cur->codec; - cur = cur->next; + alreadysent |= pref_codec; } + /* Now send any other common codecs, and non-codec formats: */ for (x = 1; x <= ((videosupport && p->vrtp) ? AST_FORMAT_MAX_VIDEO : AST_FORMAT_MAX_AUDIO); x <<= 1) { if ((capability & x) && !(alreadysent & x)) { if (debug) - ast_verbose("Answering with capability 0x%x(%s)\n", x, ast_getformatname(x)); + ast_verbose("Answering with capability 0x%x (%s)\n", x, ast_getformatname(x)); codec = ast_rtp_lookup_code(p->rtp, 1, x); if (codec > -1) { snprintf(costr, sizeof(costr), " %d", codec); @@ -3477,7 +3455,7 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p) for (x = 1; x <= AST_RTP_MAX; x <<= 1) { if (p->noncodeccapability & x) { if (debug) - ast_verbose("Answering with non-codec capability 0x%x(%s)\n", x, ast_getformatname(x)); + ast_verbose("Answering with non-codec capability 0x%x (%s)\n", x, ast_rtp_lookup_mime_subtype(0, x)); codec = ast_rtp_lookup_code(p->rtp, 0, x); if (codec > -1) { snprintf(costr, sizeof(costr), " %d", codec); @@ -3485,7 +3463,7 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p) snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(0, x)); strncat(a, costr, sizeof(a) - strlen(a) - 1); if (x == AST_RTP_DTMF) { - /* Indicate we support DTMF... Not sure about 16, but MSN supports it so dang it, we will too... */ + /* Indicate we support DTMF and FLASH... */ snprintf(costr, sizeof costr, "a=fmtp:%d 0-16\r\n", codec); strncat(a, costr, sizeof(a) - strlen(a) - 1); @@ -3598,7 +3576,7 @@ static int determine_firstline_parts( struct sip_request *req ) { e++; if( !*e ) { return -1; } } - req->rlPart2= e; + req->rlPart2= e; /* URI */ if( ( e= strrchr( req->rlPart2, 'S' ) ) == NULL ) { return -1; } @@ -3612,9 +3590,12 @@ static int determine_firstline_parts( struct sip_request *req ) { return 1; } -/* transmit_reinvite_with_sdp: Transmit reinvite with SDP :-) ---*/ -/* A re-invite is basically a new INVITE with the same CALL-ID and TAG as the - INVITE that opened the SIP dialogue */ +/*--- transmit_reinvite_with_sdp: Transmit reinvite with SDP :-) ---*/ +/* A re-invite is basically a new INVITE with the same CALL-ID and TAG as the + INVITE that opened the SIP dialogue + We reinvite so that the audio stream (RTP) go directly between + the SIP UAs. SIP Signalling stays with * in the path. +*/ static int transmit_reinvite_with_sdp(struct sip_pvt *p) { struct sip_request req; @@ -3707,9 +3688,11 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, char *cmd, c else snprintf(from, sizeof(from), "\"%s\" ;tag=as%08x", n, l, ast_strlen_zero(p->fromdomain) ? ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip) : p->fromdomain, p->tag); + /* If we're calling a registred SIP peer, use the fullcontact to dial to the peer */ if (!ast_strlen_zero(p->fullcontact)) { /* If we have full contact, trust it */ strncpy(invite, p->fullcontact, sizeof(invite) - 1); + /* Otherwise, use the username while waiting for registration */ } else if (!ast_strlen_zero(p->username)) { if (ntohs(p->sa.sin_port) != DEFAULT_SIP_PORT) { snprintf(invite, sizeof(invite), "sip:%s@%s:%d",p->username, p->tohost, ntohs(p->sa.sin_port)); @@ -3782,7 +3765,7 @@ static int transmit_invite(struct sip_pvt *p, char *cmd, int sdp, char *auth, ch #endif if (distinctive_ring && !ast_strlen_zero(distinctive_ring)) { - add_header(&req, "Alert-info",distinctive_ring); + add_header(&req, "Alert-Info",distinctive_ring); } add_header(&req, "Allow", ALLOWED_METHODS); if (sdp) { @@ -3846,6 +3829,8 @@ static int transmit_state_notify(struct sip_pvt *p, int state, int full) } mto = c; + add_header(&req, "Event", "presence"); + add_header(&req, "Subscription-State", "active"); add_header(&req, "Content-Type", "application/xpidf+xml"); if ((state==AST_EXTENSION_UNAVAILABLE) || (state==AST_EXTENSION_BUSY)) @@ -3928,8 +3913,8 @@ static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs) add_header(&req, "Event", "message-summary"); add_header(&req, "Content-Type", notifymime); - snprintf(tmp, sizeof(tmp), "Messages-Waiting: %s\n", newmsgs ? "yes" : "no"); - snprintf(tmp2, sizeof(tmp2), "Voicemail: %d/%d\n", newmsgs, oldmsgs); + snprintf(tmp, sizeof(tmp), "Messages-Waiting: %s\r\n", newmsgs ? "yes" : "no"); + snprintf(tmp2, sizeof(tmp2), "Voice-Message: %d/%d\r\n", newmsgs, oldmsgs); snprintf(clen, sizeof(clen), "%d", (int)(strlen(tmp) + strlen(tmp2))); add_header(&req, "Content-Length", clen); add_line(&req, tmp); @@ -3957,7 +3942,7 @@ static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq) struct sip_request req; char tmp[256]; char clen[20]; - initreqprep(&req, p, "NOTIFY", NULL); + reqprep(&req, p, "NOTIFY", 0, 1); snprintf(tmp, sizeof(tmp), "refer;id=%d", cseq); add_header(&req, "Event", tmp); add_header(&req, "Subscription-state", "terminated;reason=noresource"); @@ -4009,6 +3994,10 @@ static int sip_reregister(void *data) { /* if we are here, we know that we need to reregister. */ struct sip_registry *r=(struct sip_registry *)data; + + if (sipdebug) + ast_log(LOG_NOTICE, " -- Re-registration for %s@%s\n", r->username, r->hostname); + ast_mutex_lock(®l.lock); r->expire = -1; __sip_do_register(r); @@ -4027,12 +4016,15 @@ static int __sip_do_register(struct sip_registry *r) /*--- sip_reg_timeout: Registration timeout, register again */ static int sip_reg_timeout(void *data) { + /* if we are here, our registration timed out, so we'll just do it over */ struct sip_registry *r=data; struct sip_pvt *p; int res; + ast_mutex_lock(®l.lock); - ast_log(LOG_NOTICE, "Registration for '%s@%s' timed out, trying again\n", r->username, r->hostname); + + ast_log(LOG_NOTICE, " -- Registration for '%s@%s' timed out, trying again\n", r->username, r->hostname); if (r->call) { /* Unlink us, destroy old call. Locking is not relevent here because all this happens in the single SIP manager thread. */ @@ -4040,16 +4032,18 @@ static int sip_reg_timeout(void *data) p->registry = NULL; r->call = NULL; p->needdestroy = 1; + /* Pretend to ACK anything just in case */ + __sip_pretend_ack(p); } r->regstate=REG_STATE_UNREGISTERED; - manager_event(EVENT_FLAG_SYSTEM, "Registry", "Channel: SIP\r\nDomain: %s\r\nStatus: %s\r\n", r->hostname, regstate2str(r->regstate)); + manager_event(EVENT_FLAG_SYSTEM, "Registry", "Channel: SIP\r\nUser: %s\r\nDomain: %s\r\nStatus: %s\r\n", r->username, r->hostname, regstate2str(r->regstate)); r->timeout = -1; res=transmit_register(r, "REGISTER", NULL, NULL); ast_mutex_unlock(®l.lock); return 0; } -/*--- transmit_register: Transmit register to SIP proxy ---*/ +/*--- transmit_register: Transmit register to SIP proxy or UA ---*/ static int transmit_register(struct sip_registry *r, char *cmd, char *auth, char *authheader) { struct sip_request req; @@ -4074,41 +4068,49 @@ static int transmit_register(struct sip_registry *r, char *cmd, char *auth, char } else p = r->call; } else { + /* Build callid for registration if we haven't registred before */ if (!r->callid_valid) { build_callid(r->callid, sizeof(r->callid), __ourip, default_fromdomain); r->callid_valid = 1; } + /* Allocate SIP packet for registration */ p=sip_alloc( r->callid, NULL, 0); if (!p) { ast_log(LOG_WARNING, "Unable to allocate registration call\n"); return 0; } + /* Find address to hostname */ if (create_addr(p,r->hostname)) { sip_destroy(p); return 0; } + /* Copy back Call-ID in case create_addr changed it */ strncpy(r->callid, p->callid, sizeof(r->callid) - 1); if (r->portno) p->sa.sin_port = htons(r->portno); - p->outgoing = 1; - r->call=p; - p->registry=r; - if (!ast_strlen_zero(r->secret)) + p->outgoing = 1; /* Registration is outgoing call */ + r->call=p; /* Save pointer to SIP packet */ + p->registry=r; /* Add pointer to registry in packet */ + if (!ast_strlen_zero(r->secret)) /* Secret (password) */ strncpy(p->peersecret, r->secret, sizeof(p->peersecret)-1); if (!ast_strlen_zero(r->md5secret)) strncpy(p->peermd5secret, r->md5secret, sizeof(p->peermd5secret)-1); - if (!ast_strlen_zero(r->authuser)) { + /* User name in this realm + - if authuser is set, use that, otherwise use username */ + if (!ast_strlen_zero(r->authuser)) { strncpy(p->peername, r->authuser, sizeof(p->peername)-1); strncpy(p->authname, r->authuser, sizeof(p->authname)-1); } else { if (!ast_strlen_zero(r->username)) { strncpy(p->peername, r->username, sizeof(p->peername)-1); strncpy(p->authname, r->username, sizeof(p->authname)-1); + strncpy(p->fromuser, r->username, sizeof(p->fromuser)-1); } } if (!ast_strlen_zero(r->username)) strncpy(p->username, r->username, sizeof(p->username)-1); + /* Save extension in packet */ strncpy(p->exten, r->contact, sizeof(p->exten) - 1); /* @@ -4124,11 +4126,11 @@ static int transmit_register(struct sip_registry *r, char *cmd, char *auth, char /* set up a timeout */ if (auth==NULL) { if (r->timeout > -1) { - ast_log(LOG_WARNING, "Still have a timeout, %d\n", r->timeout); + ast_log(LOG_WARNING, "Still have a registration timeout, %d\n", r->timeout); ast_sched_del(sched, r->timeout); } r->timeout = ast_sched_add(sched, 20*1000, sip_reg_timeout, r); - ast_log(LOG_DEBUG, "Scheduled a timeout # %d\n", r->timeout); + ast_log(LOG_DEBUG, "Scheduled a registration timeout # %d\n", r->timeout); } if (strchr(r->username, '@')) { @@ -4153,6 +4155,7 @@ static int transmit_register(struct sip_registry *r, char *cmd, char *auth, char memset(&req, 0, sizeof(req)); init_req(&req, cmd, addr); + /* Add to CSEQ */ snprintf(tmp, sizeof(tmp), "%u %s", ++r->ocseq, cmd); p->ocseq = r->ocseq; @@ -4167,8 +4170,27 @@ static int transmit_register(struct sip_registry *r, char *cmd, char *auth, char add_header(&req, "Call-ID", p->callid); add_header(&req, "CSeq", tmp); add_header(&req, "User-Agent", default_useragent); - if (auth) + + + if (auth) /* Add auth header */ add_header(&req, authheader, auth); + else if ( !ast_strlen_zero(r->nonce) ) { + char digest[1024]; + + /* We have auth data to reuse, build a digest header! */ + if (sipdebug) + ast_log(LOG_DEBUG, " >>> Re-using Auth data for %s@%s\n", r->username, r->hostname); + strncpy(p->realm, r->realm, sizeof(p->realm)-1); + strncpy(p->nonce, r->nonce, sizeof(p->nonce)-1); + strncpy(p->domain, r->domain, sizeof(p->domain)-1); + strncpy(p->opaque, r->opaque, sizeof(p->opaque)-1); + strncpy(p->qop, r->qop, sizeof(p->qop)-1); + + memset(digest,0,sizeof(digest)); + build_reply_digest(p, "REGISTER", digest, sizeof(digest)); + add_header(&req, "Authorization", digest); + + } snprintf(tmp, sizeof(tmp), "%d", default_expiry); add_header(&req, "Expires", tmp); @@ -4263,6 +4285,7 @@ static int transmit_request_with_auth(struct sip_pvt *p, char *msg, int seqno, i if (*p->realm) { char digest[1024]; + memset(digest,0,sizeof(digest)); build_reply_digest(p, msg, digest, sizeof(digest)); add_header(&resp, "Proxy-Authorization", digest); @@ -4347,8 +4370,9 @@ static void reg_source_db(struct sip_peer *p) strncpy(p->username, u, sizeof(p->username) - 1); } - ast_verbose(VERBOSE_PREFIX_3 "SIP Seeding '%s' at %s@%s:%d for %d\n", p->name, - p->username, ast_inet_ntoa(iabuf, sizeof(iabuf), in), atoi(c), atoi(d)); + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "SIP Seeding '%s' at %s@%s:%d for %d\n", p->name, + p->username, ast_inet_ntoa(iabuf, sizeof(iabuf), in), atoi(c), atoi(d)); expiry = atoi(d); memset(&p->addr, 0, sizeof(p->addr)); p->addr.sin_family = AF_INET; @@ -4372,6 +4396,7 @@ static void reg_source_db(struct sip_peer *p) } } + /*--- parse_contact: Parse contact header and save registration ---*/ static int parse_contact(struct sip_pvt *pvt, struct sip_peer *p, struct sip_request *req) { @@ -4524,7 +4549,7 @@ static void list_route(struct sip_route *route) } } -/*--- build_route: Build route headers ---*/ +/*--- build_route: Build route list from Record-Route header ---*/ static void build_route(struct sip_pvt *p, struct sip_request *req, int backwards) { struct sip_route *thishop, *head, *tail; @@ -5110,9 +5135,13 @@ static int get_refer_info(struct sip_pvt *p, struct sip_request *oreq) p2 = p2->next; } ast_mutex_unlock(&iflock); - if (p->refer_call) - return 0; - else + if (p->refer_call) { + if (p->refer_call == p) { + ast_log(LOG_NOTICE, "Supervised transfer attempted to transfer into same call id (%s == %s)!\n", tmp5, p->callid); + p->refer_call = NULL; + } else + return 0; + } else ast_log(LOG_NOTICE, "Supervised transfer requested, but unable to find callid '%s'\n", tmp5); } else if (ast_exists_extension(NULL, p->context, c, 1, NULL) || !strcmp(c, ast_parking_ext())) { /* This is an unsupervised transfer */ @@ -5292,7 +5321,8 @@ static int get_rpid_num(char *input,char *output, int maxlen) end = strchr(output,'@'); if (end) *end = '\0'; - + else + output[0] = '\0'; if(strstr(input,"privacy=full") || strstr(input,"privacy=uri")) return 1; @@ -5358,7 +5388,8 @@ static int check_user_full(struct sip_pvt *p, struct sip_request *req, char *cmd ast_mutex_lock(&userl.lock); user = find_user(of); /* Find user based on user name in the from header */ - if (user && ast_apply_ha(user->ha, sin)) { + if (!mailbox && user && ast_apply_ha(user->ha, sin)) { + p->prefs = user->prefs; p->nat = user->nat; #ifdef OSP_SUPPORT p->ospauth = user->ospauth; @@ -5414,7 +5445,7 @@ static int check_user_full(struct sip_pvt *p, struct sip_request *req, char *cmd if (user && debug) ast_verbose("Found user '%s'\n", user->name); } else { - if (user && debug) + if (!mailbox && user && debug) ast_verbose("Found user '%s', but fails host access\n", user->name); user = NULL; } @@ -5480,6 +5511,7 @@ static int check_user_full(struct sip_pvt *p, struct sip_request *req, char *cmd strncpy(p->context, peer->context, sizeof(p->context) - 1); strncpy(p->peersecret, peer->secret, sizeof(p->peersecret) - 1); strncpy(p->peermd5secret, peer->md5secret, sizeof(p->peermd5secret) - 1); + strncpy(p->language, peer->language, sizeof(p->language) -1); p->callgroup = peer->callgroup; p->pickupgroup = peer->pickupgroup; p->capability = peer->capability; @@ -5726,6 +5758,9 @@ static int sip_show_peer(int fd, int argc, char *argv[]) char status[30] = ""; char iabuf[INET_ADDRSTRLEN]; struct sip_peer *peer; + char codec_buf[512]; + struct ast_codec_pref *pref; + int x = 0, codec = 0; if (argc != 4) return RESULT_SHOWUSAGE; @@ -5770,38 +5805,23 @@ static int sip_show_peer(int fd, int argc, char *argv[]) ast_cli(fd, " Defaddr->IP : %s Port %d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), peer->defaddr.sin_addr), ntohs(peer->defaddr.sin_port)); ast_cli(fd, " Username : %s\n", peer->username); ast_cli(fd, " Codecs : "); - /* This should really be a function in frame.c */ - if (peer->capability & AST_FORMAT_G723_1) - ast_cli(fd, "G723 "); - if (peer->capability & AST_FORMAT_GSM) - ast_cli(fd, "GSM "); - if (peer->capability & AST_FORMAT_ULAW) - ast_cli(fd, "ULAW "); - if (peer->capability & AST_FORMAT_ALAW) - ast_cli(fd, "ALAW "); - if (peer->capability & AST_FORMAT_G726) - ast_cli(fd, "G.726 "); - if (peer->capability & AST_FORMAT_SLINEAR) - ast_cli(fd, "SLINR "); - if (peer->capability & AST_FORMAT_LPC10) - ast_cli(fd, "LPC10 "); - if (peer->capability & AST_FORMAT_ADPCM) - ast_cli(fd, "ADPCM "); - if (peer->capability & AST_FORMAT_G729A) - ast_cli(fd, "G.729A "); - if (peer->capability & AST_FORMAT_SPEEX) - ast_cli(fd, "SPEEX "); - if (peer->capability & AST_FORMAT_ILBC) - ast_cli(fd, "ILBC "); - if (peer->capability & AST_FORMAT_JPEG) - ast_cli(fd, "JPEG "); - if (peer->capability & AST_FORMAT_PNG) - ast_cli(fd, "PNG "); - if (peer->capability & AST_FORMAT_H261) - ast_cli(fd, "H.261 "); - if (peer->capability & AST_FORMAT_H263) - ast_cli(fd, "H.263 "); - ast_cli(fd, "\n"); + ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, peer->capability); + ast_cli(fd, "%s\n", codec_buf); + ast_cli(fd, " Codec Order : ("); + pref = &peer->prefs; + for(x = 0; x < 32 ; x++) { + codec = ast_codec_pref_index(pref,x); + if(!codec) + break; + ast_cli(fd, "%s", ast_getformatname(codec)); + if(x < 31 && ast_codec_pref_index(pref,x+1)) + ast_cli(fd, "|"); + } + + if (!x) + ast_cli(fd, "none"); + ast_cli(fd, ")\n"); + ast_cli(fd, " Status : "); if (peer->lastms < 0) strncpy(status, "UNREACHABLE", sizeof(status) - 1); @@ -6042,7 +6062,8 @@ static void receive_info(struct sip_pvt *p, struct sip_request *req) char *c; /* Need to check the media/type */ - if (!strcasecmp(get_header(req, "Content-Type"), "application/dtmf-relay")) { + if ( (!strcasecmp(get_header(req, "Content-Type"), "application/vnd.nortelnetworks.digits")) || + (!strcasecmp(get_header(req, "Content-Type"), "application/dtmf-relay")) ) { /* Try getting the "signal=" part */ if (ast_strlen_zero(c = get_sdp(req, "Signal")) && ast_strlen_zero(c = get_sdp(req, "d"))) { @@ -6210,27 +6231,34 @@ static int sip_no_debug(int fd, int argc, char *argv[]) static int reply_digest(struct sip_pvt *p, struct sip_request *req, char *header, char *respheader, char *digest, int digest_len); -/*--- do_register_auth: Challenge for registration ---*/ +/*--- do_register_auth: Authenticate for outbound registration ---*/ static int do_register_auth(struct sip_pvt *p, struct sip_request *req, char *header, char *respheader) { char digest[1024]; p->authtries++; memset(digest,0,sizeof(digest)); - if (reply_digest(p,req, header, "REGISTER", digest, sizeof(digest))) { + if (reply_digest(p, req, header, "REGISTER", digest, sizeof(digest))) { /* There's nothing to use for authentication */ + /* No digest challenge in request */ + if (sip_debug_test_pvt(p)) + ast_verbose("No authentication challenge, sending blank registration to domain/host name %s\n", p->registry->hostname); + /* No old challenge */ return -1; } + if (sip_debug_test_pvt(p)) + ast_verbose("Responding to challenge, registration to domain/host name %s\n", p->registry->hostname); return transmit_register(p->registry,"REGISTER",digest, respheader); } -/*--- do_proxy_auth: Challenge user ---*/ +/*--- do_proxy_auth: Add authentication on outbound SIP packet ---*/ static int do_proxy_auth(struct sip_pvt *p, struct sip_request *req, char *header, char *respheader, char *msg, int init) { char digest[1024]; p->authtries++; memset(digest,0,sizeof(digest)); - if (reply_digest(p,req, header, msg, digest, sizeof(digest) )) { + if (reply_digest(p, req, header, msg, digest, sizeof(digest) )) { /* No way to authenticate */ return -1; } + /* Now we have a reply digest */ return transmit_invite(p,msg,!strcasecmp(msg, "INVITE"),digest, respheader, NULL,NULL,NULL, init); } @@ -6326,6 +6354,15 @@ static int reply_digest(struct sip_pvt *p, struct sip_request *req, char *header strncpy(p->domain, domain, sizeof(p->domain)-1); strncpy(p->opaque, opaque, sizeof(p->opaque)-1); strncpy(p->qop, qop, sizeof(p->qop)-1); + + /* Save auth data for following registrations */ + if (p->registry) { + strncpy(p->registry->realm, realm, sizeof(p->realm)-1); + strncpy(p->registry->nonce, nonce, sizeof(p->nonce)-1); + strncpy(p->registry->domain, domain, sizeof(p->domain)-1); + strncpy(p->registry->opaque, opaque, sizeof(p->opaque)-1); + strncpy(p->registry->qop, qop, sizeof(p->qop)-1); + } build_reply_digest(p, orig_header, digest, digest_len); return 0; } @@ -6487,6 +6524,9 @@ static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req) char *s, *e; strncpy(tmp, get_header(req, "Contact"), sizeof(tmp) - 1); s = ditch_braces(tmp); + e = strchr(s, ';'); + if (e) + *e = '\0'; if (p->promiscredir) { if (!strncasecmp(s, "sip:", 4)) s += 4; @@ -6511,6 +6551,7 @@ static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req) } } +/*--- check_pendings: Check pending actions on SIP call ---*/ static void check_pendings(struct sip_pvt *p) { /* Go ahead and send bye at this point */ @@ -6660,6 +6701,7 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ sip_cancel_destroy(p); if (!ast_strlen_zero(get_header(req, "Content-Type"))) process_sdp(p, req); + /* Save Record-Route for any later requests we make on this dialogue */ build_route(p, req, 1); if (p->owner) { @@ -6727,20 +6769,25 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ if (!expires) expires=atoi(get_header(req, "expires")); if (!expires) expires=default_expiry; + expires_ms = expires * 1000; if (expires <= EXPIRY_GUARD_LIMIT) expires_ms -= MAX((expires_ms * EXPIRY_GUARD_PCT),EXPIRY_GUARD_MIN); else expires_ms -= EXPIRY_GUARD_SECS * 1000; + if (sipdebug) + ast_log(LOG_NOTICE, "Outbound Registration: Expiry for %s is %d sec (Scheduling reregistration in %d ms)\n", r->hostname, expires, expires_ms); r->refresh= (int) expires_ms / 1000; + + /* Schedule re-registration before we expire */ r->expire=ast_sched_add(sched, expires_ms, sip_reregister, r); } else ast_log(LOG_WARNING, "Got 200 OK on REGISTER that isn't a register\n"); } break; - case 401: /* Not authorized on REGISTER */ + case 401: /* Not www-authorized on REGISTER */ if (!strcasecmp(msg, "INVITE")) { /* First we ACK */ transmit_request(p, "ACK", seqno, 0, 0); @@ -6757,6 +6804,21 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ } else p->needdestroy = 1; break; + case 403: /* Forbidden - we failed authentication */ + if (!strcasecmp(msg, "INVITE")) { + /* First we ACK */ + transmit_request(p, "ACK", seqno, 0, 0); + ast_log(LOG_WARNING, "Forbidden - wrong password on authentication for INVITE to '%s'\n", get_header(&p->initreq, "From")); + if (owner) + ast_queue_control(p->owner, AST_CONTROL_CONGESTION); + p->needdestroy = 1; + } else if (p->registry && !strcasecmp(msg, "REGISTER")) { + ast_log(LOG_WARNING, "Forbidden - wrong password on authentication for REGISTER for '%s' to '%s'\n", p->registry->username, p->registry->hostname); + p->needdestroy = 1; + } else { + ast_log(LOG_WARNING, "Forbidden - wrong password on authentication for %s\n", msg); + } + break; case 407: if (!strcasecmp(msg, "INVITE")) { /* First we ACK */ @@ -6860,8 +6922,18 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ p->alreadygone = 1; if (!p->owner) p->needdestroy = 1; + } else if ((resp >= 100) && (resp < 200)) { + if (!strcasecmp(msg, "INVITE")) { + sip_cancel_destroy(p); + if (!ast_strlen_zero(get_header(req, "Content-Type"))) + process_sdp(p, req); + if (p->owner) { + /* Queue a progress frame */ + ast_queue_control(p->owner, AST_CONTROL_PROGRESS); + } + } } else - ast_log(LOG_NOTICE, "Dunno anything about a %d %s response from %s\n", resp, rest, p->owner ? p->owner->name : ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr)); + ast_log(LOG_NOTICE, "Don't know anything about a %d %s response from %s\n", resp, rest, p->owner ? p->owner->name : ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr)); } } else { if (sip_debug_test_pvt(p)) @@ -6973,38 +7045,73 @@ static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct return -1; } +static void ast_quiet_chan(struct ast_channel *chan) { + if(chan && chan->_state == AST_STATE_UP) { + if(chan->generatordata) + ast_deactivate_generator(chan); + } +} /*--- attempt_transfer: Attempt transfer of SIP call ---*/ static int attempt_transfer(struct sip_pvt *p1, struct sip_pvt *p2) { + int res = 0; + struct ast_channel + *chana = NULL, + *chanb = NULL, + *bridgea = NULL, + *bridgeb = NULL, + *peera = NULL, + *peerb = NULL, + *peerc = NULL, + *peerd = NULL; + if (!p1->owner || !p2->owner) { ast_log(LOG_WARNING, "Transfer attempted without dual ownership?\n"); return -1; } - if (p1->owner->bridge) { - if (p2->owner->bridge) - ast_moh_stop(p2->owner->bridge); - ast_moh_stop(p1->owner->bridge); - ast_moh_stop(p1->owner); - ast_moh_stop(p2->owner); - if (ast_channel_masquerade(p2->owner, p1->owner->bridge)) { - ast_log(LOG_WARNING, "Failed to masquerade %s into %s\n", p2->owner->name, p1->owner->bridge->name); - return -1; + chana = p1->owner; + chanb = p2->owner; + bridgea = p1->owner->bridge; + bridgeb = p2->owner->bridge; + + if (bridgea) { + peera = chana; + peerb = chanb; + peerc = bridgea; + peerd = bridgeb; + } else if(bridgeb) { + peera = chanb; + peerb = chana; + peerc = bridgeb; + peerd = bridgea; + } + + if(peera && peerb && peerc && (peerb != peerc)) { + ast_quiet_chan(peera); + ast_quiet_chan(peerb); + ast_quiet_chan(peerc); + ast_quiet_chan(peerd); + + if (peera->cdr && peerb->cdr) { + peerb->cdr = ast_cdr_append(peerb->cdr, peera->cdr); + } else if(peera->cdr) { + peerb->cdr = peera->cdr; } - } else if (p2->owner->bridge) { - ast_moh_stop(p2->owner->bridge); - ast_moh_stop(p2->owner); - ast_moh_stop(p1->owner); - if (ast_channel_masquerade(p1->owner, p2->owner->bridge)) { - ast_log(LOG_WARNING, "Failed to masquerade %s into %s\n", p1->owner->name, p2->owner->bridge->name); - return -1; + peera->cdr = NULL; + + if (peerb->cdr && peerc->cdr) { + peerb->cdr = ast_cdr_append(peerb->cdr, peerc->cdr); + } else if(peerc->cdr) { + peerb->cdr = peerc->cdr; } + peerc->cdr = NULL; + if (ast_channel_masquerade(peerb, peerc)) { + ast_log(LOG_WARNING, "Failed to masquerade %s into %s\n", peerb->name, peerc->name); + res = -1; + } + return res; } else { - ast_log(LOG_NOTICE, "Transfer attempted with no bridged calls to transfer\n"); - if (p1->owner) - ast_softhangup_nolock(p1->owner, AST_SOFTHANGUP_DEV); - if (p2->owner) - ast_softhangup_nolock(p2->owner, AST_SOFTHANGUP_DEV); return -1; } return 0; @@ -7066,7 +7173,7 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc if (p->icseq && (p->icseq > seqno)) { ast_log(LOG_DEBUG, "Ignoring too old packet packet %d (expecting >= %d)\n", seqno, p->icseq); return -1; - } else if (p->icseq && (p->icseq == seqno)) { + } else if (p->icseq && (p->icseq == seqno) && (strcasecmp(cmd, "CANCEL") || p->alreadygone)) { /* ignore means "don't do anything with it" but still have to respond appropriately. We do this if we receive a repeat of the last sequence number */ @@ -7156,7 +7263,7 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc ast_queue_frame(p->owner, &af); } else if (debug) ast_verbose("Ignoring this request\n"); - if (!p->lastinvite) { + if (!p->lastinvite && !ignore && !p->owner) { /* Handle authentication if this is our first invite */ res = check_user(p, req, cmd, e, 1, sin, ignore); if (res) { @@ -7282,12 +7389,20 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc } } else { if (p && !p->needdestroy) { - ast_log(LOG_NOTICE, "Unable to create/find channel\n"); - if (ignore) - transmit_response(p, "503 Unavailable", req); - else - transmit_response_reliable(p, "503 Unavailable", req, 1); - p->needdestroy = 1; + if (!p->jointcapability) { + if (ignore) + transmit_response(p, "488 Not Acceptable Here", req); + else + transmit_response_reliable(p, "488 Not Acceptable Here", req, 1); + p->needdestroy = 1; + } else { + ast_log(LOG_NOTICE, "Unable to create/find channel\n"); + if (ignore) + transmit_response(p, "503 Unavailable", req); + else + transmit_response_reliable(p, "503 Unavailable", req, 1); + p->needdestroy = 1; + } } } } else if (!strcasecmp(cmd, "REFER")) { @@ -7407,12 +7522,17 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc p->needdestroy = 1; transmit_response(p, "200 OK", req); } else if (!strcasecmp(cmd, "MESSAGE")) { - if (!ignore) { - if (debug) - ast_verbose("Receiving message!\n"); - receive_message(p, req); + if (p->lastinvite) { + if (!ignore) { + if (debug) + ast_verbose("Receiving message!\n"); + receive_message(p, req); + } + transmit_response(p, "200 OK", req); + } else { + transmit_response(p, "405 Method Not Allowed", req); + p->needdestroy = 1; } - transmit_response(p, "200 OK", req); } else if (!strcasecmp(cmd, "SUBSCRIBE")) { if (!ignore) { /* Use this as the basis */ @@ -7427,8 +7547,8 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc if (!p->lastinvite) { char mailbox[256]=""; - char rbox[256]; int found = 0; + /* Handle authentication if this is our first subscribe */ res = check_user_full(p, req, cmd, e, 0, sin, ignore, mailbox, sizeof(mailbox)); if (res) { @@ -7457,21 +7577,33 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc p->subscribed = 2; else if (!strcmp(get_header(req, "Accept"), "application/simple-message-summary")) { /* Looks like they actually want a mailbox */ - snprintf(rbox, sizeof(rbox), ",%s@%s,", p->exten, p->context); - if (strstr(mailbox, rbox)) + + /* At this point, we should check if they subscribe to a mailbox that + has the same extension as the peer or the mailbox id. If we configure + the context to be the same as a SIP domain, we could check mailbox + context as well. To be able to securely accept subscribes on mailbox + IDs, not extensions, we need to check the digest auth user to make + sure that the user has access to the mailbox. + + Since we do not act on this subscribe anyway, we might as well + accept any authenticated peer with a mailbox definition in their + config section. + + */ + if (!ast_strlen_zero(mailbox)) { found++; - if (!found) { - snprintf(rbox, sizeof(rbox), ",%s,", p->exten); - if (strstr(mailbox, rbox)) - found++; } - if (found) + + if (found) { transmit_response(p, "200 OK", req); - else { + p->needdestroy = 1; + } else { transmit_response(p, "403 Forbidden", req); p->needdestroy = 1; } - + + return 0; + } else p->subscribed = 1; if (p->subscribed) @@ -7484,19 +7616,20 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc if (!ignore && p) p->lastinvite = seqno; if (p && !p->needdestroy) { - if (!(p->expiry = atoi(get_header(req, "Expires")))) { + if (!(p->expiry = atoi(get_header(req, "Expires")))) { + transmit_response(p, "200 OK", req); + p->needdestroy = 1; + return 0; + } + /* The next line can be removed if the SNOM200 Expires bug is fixed */ + if (p->subscribed == 1) { + if (p->expiry>max_expiry) { + p->expiry = max_expiry; + } + } transmit_response(p, "200 OK", req); - p->needdestroy = 1; - return 0; - } - /* The next line can be removed if the SNOM200 Expires bug is fixed */ - if (p->subscribed == 1) { - if (p->expiry>max_expiry) - p->expiry = max_expiry; - } - transmit_response(p, "200 OK", req); - sip_scheddestroy(p, (p->expiry+10)*1000); - transmit_state_notify(p, ast_extension_state(NULL, p->context, p->exten),1); + sip_scheddestroy(p, (p->expiry+10)*1000); + transmit_state_notify(p, ast_extension_state(NULL, p->context, p->exten),1); } } else if (!strcasecmp(cmd, "INFO")) { if (!ignore) { @@ -7584,8 +7717,7 @@ static int sipsock_read(int *id, int fd, short events, void *ignore) debug = sip_debug_test_addr(&sin); if (debug) ast_verbose("\n\nSip read: \n%s\n", req.data); - if (pedanticsipchecking) - req.len = lws2sws(req.data, req.len); + req.len = lws2sws(req.data, req.len); parse(&req); if (debug) ast_verbose("%d headers, %d lines\n", req.headers, req.lines); @@ -7850,6 +7982,8 @@ static int sip_poke_noanswer(void *data) } /*--- sip_poke_peer: Check availability of peer, also keep NAT open ---*/ +/* This is done with the interval in qualify= option in sip.conf */ +/* Default is 2 seconds */ static int sip_poke_peer(struct sip_peer *peer) { struct sip_pvt *p; @@ -7875,6 +8009,12 @@ static int sip_poke_peer(struct sip_peer *peer) } memcpy(&p->sa, &peer->addr, sizeof(p->sa)); memcpy(&p->recv, &peer->addr, sizeof(p->sa)); + + /* Send options to peer's fullcontact */ + if (!ast_strlen_zero(peer->fullcontact)) { + strncpy (p->fullcontact, peer->fullcontact, sizeof(p->fullcontact)); + } + if (!ast_strlen_zero(p->tohost)) strncpy(p->tohost, peer->tohost, sizeof(p->tohost) - 1); else @@ -8011,8 +8151,15 @@ static struct ast_channel *sip_request(char *type, int format, void *data) else /* UNIDEN bug */ snprintf(p->via, sizeof(p->via), "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); build_callid(p->callid, sizeof(p->callid), p->ourip, p->fromdomain); - if (ext) + + /* We have an extension to call, don't use the full contact here */ + /* This to enable dialling registred peers with extension dialling, + like SIP/peername/extension + SIP/peername will still use the full contact */ + if (ext) { strncpy(p->username, ext, sizeof(p->username) - 1); + p->fullcontact[0] = 0; + } #if 0 printf("Setting up to call extension '%s' at '%s'\n", ext ? ext : "", host); #endif @@ -8047,6 +8194,8 @@ static struct sip_user *build_user(char *name, struct ast_variable *v) user->trustrpid = global_trustrpid; user->dtmfmode = global_dtmfmode; user->progressinband = global_progressinband; + user->nat = global_nat; + user->prefs = prefs; #ifdef OSP_SUPPORT user->ospauth = global_ospauth; #endif @@ -8120,17 +8269,9 @@ static struct sip_user *build_user(char *name, struct ast_variable *v) user->amaflags = format; } } else if (!strcasecmp(v->name, "allow")) { - format = ast_getformatbyname(v->value); - if (format < 1) - ast_log(LOG_WARNING, "Cannot allow unknown format '%s'\n", v->value); - else - user->capability |= format; + ast_parse_allow_disallow(&user->prefs, &user->capability, v->value, 1); } else if (!strcasecmp(v->name, "disallow")) { - format = ast_getformatbyname(v->value); - if (format < 1) - ast_log(LOG_WARNING, "Cannot disallow unknown format '%s'\n", v->value); - else - user->capability &= ~format; + ast_parse_allow_disallow(&user->prefs, &user->capability, v->value, 0); } else if (!strcasecmp(v->name, "insecure")) { user->insecure = ast_true(v->value); } else if (!strcasecmp(v->name, "restrictcid")) { @@ -8165,6 +8306,9 @@ static struct sip_peer *temp_peer(char *name) { struct sip_peer *peer; peer = malloc(sizeof(struct sip_peer)); + if (!peer) + return NULL; + memset(peer, 0, sizeof(struct sip_peer)); peer->expire = -1; peer->pokeexpire = -1; @@ -8187,6 +8331,7 @@ static struct sip_peer *temp_peer(char *name) peer->dynamic = 1; peer->trustrpid = global_trustrpid; peer->progressinband = global_progressinband; + peer->prefs = prefs; #ifdef OSP_SUPPORT peer->ospauth = global_ospauth; #endif @@ -8201,7 +8346,6 @@ static struct sip_peer *build_peer(char *name, struct ast_variable *v) struct sip_peer *prev; struct ast_ha *oldha = NULL; int maskfound=0; - int format; int found=0; prev = NULL; ast_mutex_lock(&peerl.lock); @@ -8225,12 +8369,14 @@ static struct sip_peer *build_peer(char *name, struct ast_variable *v) } else { ast_mutex_unlock(&peerl.lock); peer = malloc(sizeof(struct sip_peer)); - memset(peer, 0, sizeof(struct sip_peer)); - peer->expire = -1; - peer->pokeexpire = -1; + if (peer) { + memset(peer, 0, sizeof(struct sip_peer)); + peer->expire = -1; + peer->pokeexpire = -1; + } } - peer->lastmsgssent = -1; if (peer) { + peer->lastmsgssent = -1; if (!found) { strncpy(peer->name, name, sizeof(peer->name)-1); strncpy(peer->context, default_context, sizeof(peer->context)-1); @@ -8241,6 +8387,7 @@ static struct sip_peer *build_peer(char *name, struct ast_variable *v) peer->defaddr.sin_family = AF_INET; peer->expiry = expiry; } + peer->prefs = prefs; oldha = peer->ha; peer->ha = NULL; peer->addr.sin_family = AF_INET; @@ -8353,17 +8500,9 @@ static struct sip_peer *build_peer(char *name, struct ast_variable *v) } else if (!strcasecmp(v->name, "pickupgroup")) { peer->pickupgroup = ast_get_group(v->value); } else if (!strcasecmp(v->name, "allow")) { - format = ast_getformatbyname(v->value); - if (format < 1) - ast_log(LOG_WARNING, "Cannot allow unknown format '%s'\n", v->value); - else - peer->capability |= format; + ast_parse_allow_disallow(&peer->prefs, &peer->capability, v->value, 1); } else if (!strcasecmp(v->name, "disallow")) { - format = ast_getformatbyname(v->value); - if (format < 1) - ast_log(LOG_WARNING, "Cannot disallow unknown format '%s'\n", v->value); - else - peer->capability &= ~format; + ast_parse_allow_disallow(&peer->prefs, &peer->capability, v->value, 0); } else if (!strcasecmp(v->name, "insecure")) { if (!strcasecmp(v->value, "very")) { peer->insecure = 2; @@ -8427,7 +8566,7 @@ static int reload_config(void) struct sip_user *user; struct ast_hostent ahp; char *cat; - char *utype; + char *utype; struct hostent *hp; int format; int oldport = ntohs(bindaddr.sin_port); @@ -8450,11 +8589,10 @@ static int reload_config(void) global_nat = SIP_NAT_RFC3581; - sip_prefs_free(); - memset(&bindaddr, 0, sizeof(bindaddr)); memset(&localaddr, 0, sizeof(localaddr)); memset(&externip, 0, sizeof(externip)); + memset(&prefs, 0 , sizeof(struct ast_codec_pref)); /* Initialize some reasonable defaults */ strncpy(default_context, "default", sizeof(default_context) - 1); @@ -8585,21 +8723,9 @@ static int reload_config(void) else memcpy(&externip.sin_addr, hp->h_addr, sizeof(externip.sin_addr)); } else if (!strcasecmp(v->name, "allow")) { - format = ast_getformatbyname(v->value); - if (format < 1) - ast_log(LOG_WARNING, "Cannot allow unknown format '%s'\n", v->value); - else { - global_capability |= format; - sip_pref_append(format); - } + ast_parse_allow_disallow(&prefs, &global_capability, v->value, 1); } else if (!strcasecmp(v->name, "disallow")) { - format = ast_getformatbyname(v->value); - if (format < 1) - ast_log(LOG_WARNING, "Cannot disallow unknown format '%s'\n", v->value); - else { - global_capability &= ~format; - sip_pref_remove(format); - } + ast_parse_allow_disallow(&prefs, &global_capability, v->value, 0); } else if (!strcasecmp(v->name, "register")) { sip_register(v->value, v->lineno); } else if (!strcasecmp(v->name, "recordhistory")) { @@ -8771,6 +8897,7 @@ static struct ast_rtp *sip_get_vrtp_peer(struct ast_channel *chan) return rtp; } +/*--- sip_set_rtp_peer: 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, int codecs) { struct sip_pvt *p; @@ -8949,7 +9076,6 @@ static int sip_do_reload(void) struct sip_peer *peer; delete_users(); reload_config(); - prune_peers(); /* And start the monitor for the first time */ ast_mutex_lock(®l.lock); @@ -8993,9 +9119,9 @@ int load_module() struct sip_peer *peer; struct sip_registry *reg; - ast_mutex_init(&userl.lock); - ast_mutex_init(&peerl.lock); - ast_mutex_init(®l.lock); + ast_mutex_init(&userl.lock); + ast_mutex_init(&peerl.lock); + ast_mutex_init(®l.lock); sched = sched_context_create(); if (!sched) { ast_log(LOG_WARNING, "Unable to create schedule context\n"); @@ -9004,7 +9130,8 @@ int load_module() if (!io) { ast_log(LOG_WARNING, "Unable to create I/O context\n"); } - + + res = reload_config(); if (!res) { /* Make sure we can register our sip channel type */ diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c index c58315b4d0..920899664f 100755 --- a/channels/chan_skinny.c +++ b/channels/chan_skinny.c @@ -1700,7 +1700,7 @@ static int skinny_indicate(struct ast_channel *ast, int ind) } switch(ind) { case AST_CONTROL_RINGING: - if (ast->_state == AST_STATE_RINGING) { + if (ast->_state != AST_STATE_UP) { if (!sub->progress) { transmit_tone(s, SKINNY_ALERT); transmit_callstate(s, l->instance, SKINNY_RINGOUT, sub->callid); diff --git a/channels/chan_zap.c b/channels/chan_zap.c index b4149cfc90..496229207a 100755 --- a/channels/chan_zap.c +++ b/channels/chan_zap.c @@ -153,6 +153,8 @@ static char *config = "zapata.conf"; #define DCHAN_AVAILABLE (DCHAN_PROVISIONED | DCHAN_NOTINALARM | DCHAN_UP) +#define zt_close(fd) if(fd > 0) close(fd); + static char context[AST_MAX_EXTENSION] = "default"; static char callerid[256] = ""; @@ -262,6 +264,9 @@ AST_MUTEX_DEFINE_STATIC(iflock); static int ifcount = 0; +/* When to send the CallerID signals (rings) */ +static int sendcalleridafter = DEFAULT_CIDRINGS; + /* Protect the monitoring thread, so only one process can kill or start it, and not when it's doing something critical. */ AST_MUTEX_DEFINE_STATIC(monlock); @@ -541,6 +546,7 @@ static struct zt_pvt { int dtmfrelax; /* whether to run in relaxed DTMF mode */ int fake_event; int zaptrcallerid; /* should we use the callerid from incoming call on zap transfer or not */ + int sendcalleridafter; #ifdef ZAPATA_PRI struct zt_pri *pri; struct zt_pvt *bearer; @@ -758,7 +764,7 @@ static int zt_open(char *fn) if (chan) { if (ioctl(fd, ZT_SPECIFY, &chan)) { x = errno; - close(fd); + zt_close(fd); errno = x; ast_log(LOG_WARNING, "Unable to specify channel %d: %s\n", chan, strerror(errno)); return -1; @@ -769,11 +775,6 @@ static int zt_open(char *fn) return fd; } -static void zt_close(int fd) -{ - close(fd); -} - int zt_setlinear(int zfd, int linear) { int res; @@ -1535,7 +1536,7 @@ static int zt_call(struct ast_channel *ast, char *rdest, int timeout) } else { if (ioctl(p->subs[SUB_REAL].zfd, ZT_SETCADENCE, NULL)) ast_log(LOG_WARNING, "Unable to reset default ring on '%s'\n", ast->name); - p->cidrings = DEFAULT_CIDRINGS; + p->cidrings = p->sendcalleridafter; } @@ -2886,6 +2887,18 @@ static int attempt_transfer(struct zt_pvt *p) if (p->subs[SUB_THREEWAY].owner->_state == AST_STATE_RINGING) { ast_indicate(p->subs[SUB_REAL].owner->bridge, AST_CONTROL_RINGING); } + if (p->subs[SUB_REAL].owner->cdr) { + /* Move CDR from second channel to current one */ + p->subs[SUB_THREEWAY].owner->cdr = + ast_cdr_append(p->subs[SUB_THREEWAY].owner->cdr, p->subs[SUB_REAL].owner->cdr); + p->subs[SUB_REAL].owner->cdr = NULL; + } + if (p->subs[SUB_REAL].owner->bridge->cdr) { + /* Move CDR from second channel's bridge to current one */ + p->subs[SUB_THREEWAY].owner->cdr = + ast_cdr_append(p->subs[SUB_THREEWAY].owner->cdr, p->subs[SUB_REAL].owner->bridge->cdr); + p->subs[SUB_REAL].owner->bridge->cdr = NULL; + } if (ast_channel_masquerade(p->subs[SUB_THREEWAY].owner, p->subs[SUB_REAL].owner->bridge)) { ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n", p->subs[SUB_REAL].owner->bridge->name, p->subs[SUB_THREEWAY].owner->name); @@ -2899,6 +2912,18 @@ static int attempt_transfer(struct zt_pvt *p) ast_indicate(p->subs[SUB_THREEWAY].owner->bridge, AST_CONTROL_RINGING); } ast_moh_stop(p->subs[SUB_THREEWAY].owner->bridge); + if (p->subs[SUB_THREEWAY].owner->cdr) { + /* Move CDR from second channel to current one */ + p->subs[SUB_REAL].owner->cdr = + ast_cdr_append(p->subs[SUB_REAL].owner->cdr, p->subs[SUB_THREEWAY].owner->cdr); + p->subs[SUB_THREEWAY].owner->cdr = NULL; + } + if (p->subs[SUB_THREEWAY].owner->bridge->cdr) { + /* Move CDR from second channel's bridge to current one */ + p->subs[SUB_REAL].owner->cdr = + ast_cdr_append(p->subs[SUB_REAL].owner->cdr, p->subs[SUB_THREEWAY].owner->bridge->cdr); + p->subs[SUB_THREEWAY].owner->bridge->cdr = NULL; + } if (ast_channel_masquerade(p->subs[SUB_REAL].owner, p->subs[SUB_THREEWAY].owner->bridge)) { ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n", p->subs[SUB_THREEWAY].owner->bridge->name, p->subs[SUB_REAL].owner->name); @@ -5018,9 +5043,11 @@ static void *ss_thread(void *data) p->subs[SUB_THREEWAY].owner; struct zt_pvt *pbridge = NULL; /* set up the private struct of the bridged one, if any */ - if (nbridge && nbridge->bridge) pbridge = nbridge->bridge->pvt->pvt; - if (nbridge && - (!strcmp(nbridge->type,"Zap")) && + if (nbridge && nbridge->bridge) + pbridge = nbridge->bridge->pvt->pvt; + if (nbridge && pbridge && + (!strcmp(nbridge->type,"Zap")) && + (!strcmp(nbridge->bridge->type, "Zap")) && ISTRUNK(pbridge)) { int func = ZT_FLASH; /* Clear out the dial buffer */ @@ -6056,7 +6083,7 @@ static int pri_create_trunkgroup(int trunkgroup, int *channels) x = channels[y]; if (ioctl(fd, ZT_SPECIFY, &x)) { ast_log(LOG_WARNING, "Failed to specify channel %d: %s\n", channels[y], strerror(errno)); - close(fd); + zt_close(fd); return -1; } if (ioctl(fd, ZT_GET_PARAMS, &p)) { @@ -6065,18 +6092,18 @@ static int pri_create_trunkgroup(int trunkgroup, int *channels) } if (ioctl(fd, ZT_SPANSTAT, &si)) { ast_log(LOG_WARNING, "Failed go get span information on channel %d (span %d)\n", channels[y], p.spanno); - close(fd); + zt_close(fd); return -1; } span = p.spanno - 1; if (pris[span].trunkgroup) { ast_log(LOG_WARNING, "Span %d is already provisioned for trunk group %d\n", span + 1, pris[span].trunkgroup); - close(fd); + zt_close(fd); return -1; } if (pris[span].pvts[0]) { ast_log(LOG_WARNING, "Span %d is already provisioned with channels (implicit PRI maybe?)\n", span + 1); - close(fd); + zt_close(fd); return -1; } if (!y) { @@ -6087,7 +6114,7 @@ static int pri_create_trunkgroup(int trunkgroup, int *channels) pris[ospan].dchannels[y] = channels[y]; pris[ospan].dchanavail[y] |= DCHAN_PROVISIONED; pris[span].span = span + 1; - close(fd); + zt_close(fd); } return 0; } @@ -6511,6 +6538,7 @@ static struct zt_pvt *mkintf(int channel, int signalling, int radio, struct zt_p if (working->channel > tmp->channel) { tmp->next = *wlist; tmp->prev = NULL; + (*wlist)->prev = tmp; *wlist = tmp; } else { /* go through all the members and put the member in the right place */ @@ -7012,6 +7040,7 @@ static int pri_fixup_principle(struct zt_pri *pri, int principle, q931_call *c) ast_log(LOG_WARNING, "Whoa, there's no owner, and we're having to fix up channel %d to channel %d\n", pri->pvts[x]->channel, pri->pvts[principle]->channel); pri->pvts[principle]->call = pri->pvts[x]->call; /* Free up the old channel, now not in use */ + pri->pvts[x]->subs[SUB_REAL].owner = NULL; pri->pvts[x]->owner = NULL; pri->pvts[x]->call = NULL; } @@ -7030,7 +7059,7 @@ static int pri_fixup_principle(struct zt_pri *pri, int principle, q931_call *c) else { /* Looks good. Drop the pseudo channel now, clear up the assignment, and wakeup the potential sleeper */ - close(crv->subs[SUB_REAL].zfd); + zt_close(crv->subs[SUB_REAL].zfd); pri->pvts[principle]->call = crv->call; pri_assign_bearer(crv, pri, pri->pvts[principle]); ast_log(LOG_DEBUG, "Assigning bearer %d/%d to CRV %d:%d\n", @@ -7103,8 +7132,7 @@ static void *do_idle_thread(void *vchan) static void zt_pri_message(char *s) { - if (option_verbose) - ast_verbose(s); + ast_verbose("%s", s); } static void zt_pri_error(char *s) @@ -7355,7 +7383,7 @@ static void *pri_dchannel(void *vpri) x = 0; res = ioctl(pri->fds[which], ZT_GETEVENT, &x); if (x) - ast_log(LOG_NOTICE, "PRI got event: %d on %s D-channel of span %d\n", x, pri_order(which), pri->span); + ast_log(LOG_NOTICE, "PRI got event: %s (%d) on %s D-channel of span %d\n", event2str(x), x, pri_order(which), pri->span); /* Keep track of alarm state */ if (x == ZT_EVENT_ALARM) { pri->dchanavail[which] &= ~(DCHAN_NOTINALARM | DCHAN_UP); @@ -8033,13 +8061,13 @@ static int start_pri(struct zt_pri *pri) } res = ioctl(pri->fds[i], ZT_GET_PARAMS, &p); if (res) { - close(pri->fds[i]); + zt_close(pri->fds[i]); pri->fds[i] = -1; ast_log(LOG_ERROR, "Unable to get parameters for D-channel %d (%s)\n", x, strerror(errno)); return -1; } if (p.sigtype != ZT_SIG_HDLCFCS) { - close(pri->fds[i]); + zt_close(pri->fds[i]); pri->fds[i] = -1; ast_log(LOG_ERROR, "D-channel %d is not in HDLC/FCS mode. See /etc/zaptel.conf\n", x); return -1; @@ -8047,7 +8075,7 @@ static int start_pri(struct zt_pri *pri) memset(&si, 0, sizeof(si)); res = ioctl(pri->fds[i], ZT_SPANSTAT, &si); if (res) { - close(pri->fds[i]); + zt_close(pri->fds[i]); pri->fds[i] = -1; ast_log(LOG_ERROR, "Unable to get span state for D-channel %d (%s)\n", x, strerror(errno)); } @@ -8061,7 +8089,7 @@ static int start_pri(struct zt_pri *pri) bi.bufsize = 1024; if (ioctl(pri->fds[i], ZT_SET_BUFINFO, &bi)) { ast_log(LOG_ERROR, "Unable to set appropriate buffering on channel %d\n", x); - close(pri->fds[i]); + zt_close(pri->fds[i]); pri->fds[i] = -1; return -1; } @@ -8074,7 +8102,7 @@ static int start_pri(struct zt_pri *pri) if (i) pri_enslave(pri->dchans[0], pri->dchans[i]); if (!pri->dchans[i]) { - close(pri->fds[i]); + zt_close(pri->fds[i]); pri->fds[i] = -1; ast_log(LOG_ERROR, "Unable to create PRI structure\n"); return -1; @@ -8089,7 +8117,7 @@ static int start_pri(struct zt_pri *pri) for (i=0;idchannels[i]) break; - close(pri->fds[i]); + zt_close(pri->fds[i]); pri->fds[i] = -1; } ast_log(LOG_ERROR, "Unable to spawn D-channel: %s\n", strerror(errno)); @@ -8237,10 +8265,19 @@ static int handle_pri_show_span(int fd, int argc, char *argv[]) } for(x=0;xvalue); } else if (!strcasecmp(v->name, "debounce")) { cur_debounce = atoi(v->value); + } else if (!strcasecmp(v->name, "sendcalleridafter")) { + sendcalleridafter = atoi(v->value); } else ast_log(LOG_WARNING, "Ignoring %s\n", v->name); v = v->next; diff --git a/channels/h323/.cvsignore b/channels/h323/.cvsignore new file mode 100755 index 0000000000..4671378aef --- /dev/null +++ b/channels/h323/.cvsignore @@ -0,0 +1 @@ +.depend diff --git a/channels/h323/Makefile b/channels/h323/Makefile index 52004c1fd7..1ccc8a6578 100755 --- a/channels/h323/Makefile +++ b/channels/h323/Makefile @@ -64,7 +64,7 @@ CFLAGS += -I$(OPENH323DIR)/include CFLAGS += -Wno-missing-prototypes -Wno-missing-declarations LIBS+= -lcrypto -lssl -lexpat -all: libchanh323.a +all: depend libchanh323.a samples: if [ -f $(ASTETCDIR)/h323.conf ]; then \ @@ -73,11 +73,15 @@ samples: install h323.conf.sample $(ASTETCDIR)/h323.conf +libchanh323.a: ast_h323.o + ar cr libchanh323.a ast_h323.o + ast_h323.o: ast_h323.cpp $(CXX) -g -c -fno-rtti -o $@ $(CFLAGS) $< -libchanh323.a: ast_h323.o - ar cr libchanh323.a ast_h323.o +ifneq ($(wildcard .depend),) +include .depend +endif chan_h323.so: $(CXX) -g -shared -Xlinker -x -o chan_h323.so chan_h323.o ast_h323.o -L$(PWLIBDIR)/lib -lpt_linux_x86_r -L$(OPENH323DIR)/lib -lh323_linux_x86_r -L/usr/lib $(CHANH323LIB) @@ -88,6 +92,11 @@ chan_h323_d.so: chan_h323.o ast_h323.o chan_h323_s.so: chan_h323.o ast_h323.o $(CXX) -shared -Xlinker -x -o chan_h323.so chan_h323.o ast_h323.o -L$(PWLIBDIR)/lib -lpt_linux_x86_r_s -L$(OPENH323DIR)/lib -lh323_linux_x86_r_s -L/usr/lib $(CHANH323LIB) clean: - rm -f *.o *.so core.* libchanh323.a + rm -f *.o *.so core.* libchanh323.a .depend + +depend: .depend + +.depend: + ../../mkdep $(CFLAGS) `ls *.cpp` diff --git a/channels/h323/ast_h323.cpp b/channels/h323/ast_h323.cpp index afeee365e1..4c01d95758 100755 --- a/channels/h323/ast_h323.cpp +++ b/channels/h323/ast_h323.cpp @@ -722,7 +722,7 @@ void MyH323Connection::SendUserInputTone(char tone, unsigned duration) if (h323debug) { cout << " -- Sending user input tone (" << tone << ") to remote" << endl; } - on_send_digit(GetCallReference(), tone); + on_send_digit(GetCallReference(), tone, (const char *)GetCallToken()); H323Connection::SendUserInputTone(tone, duration); } @@ -732,7 +732,7 @@ void MyH323Connection::OnUserInputTone(char tone, unsigned duration, unsigned lo if (h323debug) { cout << " -- Received user input tone (" << tone << ") from remote" << endl; } - on_send_digit(GetCallReference(), tone); + on_send_digit(GetCallReference(), tone, (const char *)GetCallToken()); } H323Connection::OnUserInputTone(tone, duration, logicalChannel, rtpTimestamp); } @@ -743,7 +743,7 @@ void MyH323Connection::OnUserInputString(const PString &value) if (h323debug) { cout << " -- Received user input string (" << value << ") from remote." << endl; } - on_send_digit(GetCallReference(), value[0]); + on_send_digit(GetCallReference(), value[0], (const char *)GetCallToken()); } } diff --git a/channels/h323/chan_h323.h b/channels/h323/chan_h323.h index 1b277c4993..e948a1a028 100755 --- a/channels/h323/chan_h323.h +++ b/channels/h323/chan_h323.h @@ -114,7 +114,7 @@ typedef struct rtp_info { /* This is a callback prototype function, called pass DTMF down the RTP. */ -typedef int (*send_digit_cb)(unsigned, char); +typedef int (*send_digit_cb)(unsigned, char, const char *); extern send_digit_cb on_send_digit; /* This is a callback prototype function, called to collect diff --git a/channels/iax2-parser.c b/channels/iax2-parser.c index c54af66a5f..34f251fb04 100755 --- a/channels/iax2-parser.c +++ b/channels/iax2-parser.c @@ -32,7 +32,7 @@ static int oframes = 0; static void internaloutput(const char *str) { - printf(str); + fputs(str, stdout); } static void internalerror(const char *str) diff --git a/cli.c b/cli.c index 613e3a22fc..f72d087f20 100755 --- a/cli.c +++ b/cli.c @@ -26,6 +26,7 @@ #include #include #include +#include /* For rl_filename_completion */ #include "editline/readline/readline.h" /* For module directory */ @@ -147,8 +148,8 @@ static int handle_set_verbose(int fd, int argc, char *argv[]) if (oldval != option_verbose && option_verbose > 0) ast_cli(fd, "Verbosity was %d and is now %d\n", oldval, option_verbose); else if (oldval > 0 && option_verbose > 0) - ast_cli(fd, "Verbosity is atleast %d\n", option_verbose); - else if (oldval > 0 && option_debug == 0) + ast_cli(fd, "Verbosity is at least %d\n", option_verbose); + else if (oldval > 0 && option_verbose == 0) ast_cli(fd, "Verbosity is now OFF\n"); return RESULT_SUCCESS; } @@ -157,6 +158,7 @@ static int handle_set_debug(int fd, int argc, char *argv[]) { int val = 0; int oldval = 0; + /* Has a hidden 'at least' argument */ if ((argc != 3) && (argc != 4)) return RESULT_SHOWUSAGE; @@ -173,7 +175,7 @@ static int handle_set_debug(int fd, int argc, char *argv[]) if (oldval != option_debug && option_debug > 0) ast_cli(fd, "Core debug was %d and is now %d\n", oldval, option_debug); else if (oldval > 0 && option_debug > 0) - ast_cli(fd, "Core debug is atleast %d\n", option_debug); + ast_cli(fd, "Core debug is at least %d\n", option_debug); else if (oldval > 0 && option_debug == 0) ast_cli(fd, "Core debug is now OFF\n"); return RESULT_SUCCESS; @@ -437,11 +439,11 @@ static char *__ast_cli_generator(char *text, char *word, int state, int lock); static int handle_commandmatchesarray(int fd, int argc, char *argv[]) { - char *buf; + char *buf, *obuf; int buflen = 2048; int len = 0; char **matches; - int x; + int x, matchlen; if (argc != 4) return RESULT_SHOWUSAGE; @@ -455,11 +457,17 @@ static int handle_commandmatchesarray(int fd, int argc, char *argv[]) #if 0 printf("command matchesarray for '%s' %s got '%s'\n", argv[2], argv[3], matches[x]); #endif - if (len + strlen(matches[x]) >= buflen) { - buflen += strlen(matches[x]) * 3; - buf = realloc(buf, buflen); + matchlen = strlen(matches[x]) + 1; + if (len + matchlen >= buflen) { + buflen += matchlen * 3; + obuf = buf; + buf = realloc(obuf, buflen); + if (!buf) + /* Out of memory... Just free old buffer and be done */ + free(obuf); } - len += sprintf( buf + len, "%s ", matches[x]); + if (buf) + len += sprintf( buf + len, "%s ", matches[x]); free(matches[x]); matches[x] = NULL; } @@ -863,10 +871,10 @@ int ast_cli_register(struct ast_cli_entry *e) static int help_workhorse(int fd, char *match[]) { - char fullcmd1[80]; - char fullcmd2[80]; + char fullcmd1[80] = ""; + char fullcmd2[80] = ""; char matchstr[80]; - char *fullcmd; + char *fullcmd = NULL; struct ast_cli_entry *e, *e1, *e2; e1 = builtins; e2 = helpers; @@ -999,8 +1007,7 @@ normal: int ast_cli_generatornummatches(char *text, char *word) { int matches = 0, i = 0; - char *buf, *oldbuf = NULL; - + char *buf = NULL, *oldbuf = NULL; while ( (buf = ast_cli_generator(text, word, i)) ) { if (++i > 1 && strcmp(buf,oldbuf) == 0) { @@ -1035,7 +1042,7 @@ char **ast_cli_completion_matches(char *text, char *word) prevstr = match_list[1]; max_equal = strlen(prevstr); for (; which <= matches; which++) { - for (i = 0; i < max_equal && prevstr[i] == match_list[which][i]; i++) + for (i = 0; i < max_equal && toupper(prevstr[i]) == toupper(match_list[which][i]); i++) continue; max_equal = i; } @@ -1059,10 +1066,10 @@ static char *__ast_cli_generator(char *text, char *word, int state, int lock) int x; int matchnum=0; char *dup, *res; - char fullcmd1[80]; - char fullcmd2[80]; + char fullcmd1[80] = ""; + char fullcmd2[80] = ""; char matchstr[80]; - char *fullcmd; + char *fullcmd = NULL; if ((dup = parse_args(text, &x, argv))) { join(matchstr, sizeof(matchstr), argv); diff --git a/codecs/Makefile b/codecs/Makefile index 82d8f458e0..9a3dc8c28e 100755 --- a/codecs/Makefile +++ b/codecs/Makefile @@ -19,10 +19,11 @@ #MODG723=codec_g723_1.so codec_g723_1b.so MODG723=$(shell [ -f g723.1/coder.c ] && echo "codec_g723_1.so") MODG723+=$(shell [ -f g723.1b/coder2.c ] && echo "codec_g723_1b.so") -MODSPEEX=$(shell [ -f /usr/include/speex.h ] || [ -f /usr/local/include/speex.h ] && echo "codec_speex.so") +MODSPEEX=$(shell [ -f /usr/include/speex.h ] || [ -f /usr/local/include/speex.h ] || [ -f /usr/local/include/speex/speex.h ] && echo "codec_speex.so") MODILBC=$(shell [ -f ilbc/iLBC_decode.h ] && echo "codec_ilbc.so") CFLAGS+=-fPIC CFLAGS+=$(shell [ -f /usr/local/include/speex.h ] && echo "-I/usr/local/include") +CFLAGS+=$(shell [ -f /usr/local/include/speex/speex.h ] && echo "-I/usr/local/include/speex") LIBG723=g723.1/libg723.a LIBG723B=g723.1b/libg723b.a diff --git a/codecs/gsm/Makefile b/codecs/gsm/Makefile index a21bd7e52f..06e96414a6 100755 --- a/codecs/gsm/Makefile +++ b/codecs/gsm/Makefile @@ -42,12 +42,14 @@ ifneq (${PROC},x86_64) ifneq (${PROC},ultrasparc) ifneq ($(shell uname -m),ppc) ifneq ($(shell uname -m),alpha) +ifneq (${PROC},sparc64) OPTIMIZE+=-march=$(PROC) endif endif endif endif endif +endif #The problem with sparc is the best stuff is in newer versions of gcc (post 3.0) only. #This works for even old (2.96) versions of gcc and provides a small boost either way. @@ -489,7 +491,7 @@ clean: semi-clean -rm $(RMFLAGS) $(LIBGSM) $(ADDTST)/add \ $(TOAST) $(TCAT) $(UNTOAST) \ $(ROOT)/gsm-1.0.tar.Z - + rm -rf lib # Two tools that helped me generate gsm_encode.c and gsm_decode.c, # but aren't generally needed to port this. diff --git a/configs/agents.conf.sample b/configs/agents.conf.sample index bde3081342..d8641e60b9 100755 --- a/configs/agents.conf.sample +++ b/configs/agents.conf.sample @@ -11,7 +11,7 @@ ;autologoff=15 ; ; Define ackcall to require an acknowledgement by '#' when -; an agent logs in over agentcallpark. Default is "yes". +; an agent logs in using agentcallbacklogin. Default is "yes". ; ;ackcall=yes ; @@ -32,7 +32,7 @@ ; ;updatecdr=no ; -; Group memberships for agents (may change in mid-file just) +; Group memberships for agents (may change in mid-file) ; ;group=3 ;group=1,2 diff --git a/configs/queues.conf.sample b/configs/queues.conf.sample index 7ba488ad38..3a682d1e1d 100755 --- a/configs/queues.conf.sample +++ b/configs/queues.conf.sample @@ -17,10 +17,10 @@ ; ; A sample call queue ; -; Music sets which musiconhold applies for this particular -; call queue +; Musiconhold sets which music applies for this particular +; call queue (configure classes in musiconhold.conf) ; -;music = default +;musiconhold = default ; ; An announcement may be specified which is played for the member as ; soon as they answer a call, typically to indicate to them which queue @@ -92,6 +92,7 @@ ;queue-minutes = queue-minutes ; ("minutes.") ;queue-seconds = queue-seconds ; ("seconds.") ;queue-thankyou = queue-thankyou ; ("Thank you for your patience.") +;queue-lessthan = queue-less-than ; ("less than") ; ; Calls may be recorded using Asterisk's monitor resource ; This can be enabled from within the Queue application, starting recording @@ -113,6 +114,10 @@ ; ; joinempty = yes ; +; If you wish to remove callers from the queue if there are no agents present, set this to yes +; +; leavewhenempty = yes +; ; Asterisk can generate AgentCalled events when an agent is rung, if this is turned on ; (may generate a LOT of extra manager events) ; diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample index de46d94392..f3fc875803 100755 --- a/configs/sip.conf.sample +++ b/configs/sip.conf.sample @@ -131,7 +131,6 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ; context context ; permit permit ; deny deny -; auth auth ; secret secret ; md5secret md5secret ; dtmfmode dtmfmode @@ -229,6 +228,18 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ;mailbox=1234@context,2345 ; Mailbox(-es) for message waiting indicator +;[polycom] +;type=friend ; Friends place calls and receive calls +;context=from-sip ; Context for incoming calls from this user +;secret=blahpoly +;host=dynamic ; This peer register with us +;dtmfmode=rfc2833 ; Choices are inband, rfc2833, or info +;username=polly ; Username to use in INVITE until peer registers +;disallow=all +;allow=ulaw ; dtmfmode=inband only works with ulaw or alaw! +;progressinband=no ; Polycom phones don't work properly with "never" + + ;[pingtel] ;type=friend ;username=pingtel diff --git a/configs/zapata.conf.sample b/configs/zapata.conf.sample index c692a27b86..b7b660318d 100755 --- a/configs/zapata.conf.sample +++ b/configs/zapata.conf.sample @@ -177,6 +177,13 @@ callwaiting=yes ; usecallingpres=yes ; +; Some countries (UK) have ring tones with different ring tones (ring-ring), +; which means the callerid needs to be set later on, and not just after +; the first ring, as per the default. +; +;sendcalleridafter=1 +; +; ; Support Caller*ID on Call Waiting ; callwaitingcallerid=yes diff --git a/contrib/README.festival b/contrib/README.festival index a6a7c067ca..24912827c9 100755 --- a/contrib/README.festival +++ b/contrib/README.festival @@ -11,6 +11,23 @@ You need : 1.4.2 RELEASE are included. The patch adds a new command to festival (asterisk_tts). +It is possible to run Festival without patches in the source-code. Just +add this to your /etc/festival.scm or /usr/share/festival/festival/scm: + + (define (tts_textasterisk string mode) + "(tts_textasterisk STRING MODE) + Apply tts to STRING. This function is specifically designed for + use in server mode so a single function call may synthesize the string. + This function name may be added to the server safe functions." + (let ((wholeutt (utt.synth (eval (list 'Utterance 'Text string))))) + (utt.wave.resample wholeutt 8000) + (utt.wave.rescale wholeutt 5) + (utt.send.wave.client wholeutt))) + +[See the comment with subject "Using Debian + festival >= 1.4.3-15 (no recompiling needed!)" on + http://www.voip-info.org/wiki-Asterisk+festival+installation for the + original mentioning of it] 2) You may wish to obtain and install the asterisk-perl module by James Golovich , from diff --git a/contrib/firmware/iax/iaxy.bin b/contrib/firmware/iax/iaxy.bin index 1bd4ee2084d54c9600b10f3f7dcefcd228e30bd4..74e132ee06bb42e8e99ed1e8c782ec8a07d69496 100755 GIT binary patch literal 39386 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 literal 39241 zcmV(@K-Rx$VR(4}6KP?1c>n+a00000000000001)AmkvgOv=ok(l*;a4-87c0cXkB#DNc7`>ZIg2t z`3Pq=vM6&>)x^Da$vu9_Y#d9k{35rL-Iy0|VXh2!S;ttH1wll3fe5{mCcQ$uql9dp zCo$~&@Kp{N>4!+}ne1L8fa)efb2+cu)ug;C!7x+ifW3lK(Sk0il|M8Jva`XxPv2aR zA+&|#C_U+mT}Y(*)%B4JA1%;{8Y-H#4@1I45MRJ7i@%G^=eyfkeS((j4jMfesJv0w zJSz%sgJIt2>KQYBXzY5E^0_){CPK4OLMFXSz5rz@Y>9$P5cg(EdV;5cQr_o!L+6og zizK`g1sSm@29K{l#TkmQ4%B-(QDVI`%0H--%hdQ2-UNk(6QJnzVoccXGOF;}{fu=Q z$|Ii~St=OO`jL?^*_jkd*%}4Kfv1j%8%-|oyDptjcsO~;m*g!ozGg6fV@MKd&urPxwvi} zKW3dxXL6`vEzi@i71clep;sq*#|+wP>(6;psYiJ(rG+Q1uXXs}jnG|jfaIhvAcvF2 zDA+q%x@ihwsQG5$Ugo7tv|%SaC7Fg=BM%~o0#1sP!g)82jWxMcqRBhmetuU8)hiNf zqLZf0-P2tDfq$wNnnH|{P2`k1gB-FS2wk z7Yc!z5SI2ZXrYe&WrHH0ndS=9lIop%F50JM;htnPMQC>@^cEUs>jAFknnB^P>yz?pkQ(v zOL6;Y60Wj0715G8W7njr0A_b$k9-PZTZ$I>L$TB^L5wtz{y_&Ga^HF?lBkM7<4F<( zPSToA4i5}`3=rPtHoX2m=;xGEmHu>_vhQu^_-ISKm}M@mDHA#`%^!#H;?7GrKn8j# zspN}WpXO}}i8eoB>+XQEb(x$+CukAWEtA>spcAQ>y(JHLe)E5oW5MRiE08ej#YNdp zPldbd;NU@$ipXNO8l{Hl$llO80IjYyeo_#0lON>*u5enQ)#8PJS+h_0Ex6wAY;z+z z+1%7Q_$BpHhvwJZ^{V)I7em>~FYDTZ_cQAxt8ZczN@*}qdkl}SDN2em{ZldK{RyXq zWoht9mAv*%WO`aE@!)Njv3V!X?llanc2I`bQ-)cFF62f5B&BvMCxyH?85bqevC zB~YRKa(lk0+#mF@I6|SKQ@_AMvBrJd<{j&4O=I+NOw()^Gt0JYy7;Gh`aclsN+Mm= zwRY{hl&qvq&ZdhYVHO#t3B%Y!o%1ABYcw@WByv_>AvCCuirCo`Nwe3clRws{<8*$c zT@2xZi&a1Wa!cJmVGCF@P-~0^Wk1#Gz5Qu16(J{97dy#Ay-SC4M5#6pE)h^|J)`sG z!Pu{zF4)v*l{r(XPjefG{b@!4!(#NMn|0)fg`9RR`YM^3wD0{L4??FQF@BGmL0~u7U#xG?O!sBY^&zZpk;8 zpRvL?K4C}m1v4>!4^FRg3Td*{-^?v>A1=iEWLhO}<*-2pllMDI535FOuZ;L-8iw*t zIRf=O)841q!%Z0lGhbpNAPNHT_AUyk>bEkq+W7FF{}m&q=@jN&&okCgqF9jp|6*8m z@SV_KBH9mowz-eCX9bCsz9me;3r*Q1E<~3}TDewiNa1=#aHk*gWZ|qQMz%NF7H{xI zY}FWzW!|1(L~eb5U_C7*Ym`DGob3NlH1xX@ckYK%%9p5=%5#Ad5%j+K3O}Pd*TD?7 z3Yrp#-me)iI^=~k@{eLT2(%zCBUp#wDVLgNU$=P6`92ljY~9ZR(P+UNON1a7K7~w$*I7H*ij&`GW&2T3sf9CJx8C|Bg+7mIJ9A`c)7CkT1RKb{v ziqM)%rH*WA?g1v}YTM*Q&gJWGn;S4ZM+O@wk2sg<1=ar)g_4XGI&EAEKYi0Vw0Bj9 z^94!?P^BzT7~cbCrrXR5;z661zY%Sml}KTelQ6ZycnBG0w1lz@#N@$|SwwLCDut%s zyjXFDMf3gF$g6QDHnYo0FeD5NLRPFO8q)(*R?j4vChEJB;*$>qD^4x!s7M3 z$1~92rkR0##hd6EpZ1c(gJaX$O#jng*-_z}9X3z;#q6fJG16*cf>}k1tUX=O1!6Zv zHTyI;A9QJ6@Xi3TxPLuINrPFVo-SUYN+Nwu~Z`!)7q5B)kjutonbRLadBwEv_Z zSjUa{E98WPXAhkwE$@z)7jBXFKEpbP6{opP+{84AZc)(~Hrte&T7AstE~cucfYHDH z){F+^=wbHsU#AQDDh9WrttT+S{E+Rshv@luV?UiGX}e)L;z5X{Pm>ZdOCWV-U>VVz zf9jASUNE<3Z-4i@ygbf7p4{`|8&>}G=N!|Dj@=Y(W_ldVNrh2s7cZ@~qJh=00FF>EmvSGpBYKb#}zP)&pt zf+#;%x|HZ$#mT0@xnl7oD&0C0FY&3~u!=JmJvn73Cd>QwX?>ARpLQ}pt*j7xE(2{w z)O2$3$Xu|Ak)m zinXj1XkzxJ^|C$iL{>$&u&^*@eP29Si5wJ6F8<%-m!fKWT~$`(>rQ%N?1V42B7qD{ z#2_aW90Iy>Nn>*&qveY!$SLcj<2USF@!FykG*i733zZ)ewUbR<6R$KUV{XlEgSeHm zZl7|S=)u&-9XV$-mS3NUXA{fO&_r`8tkEzUnVnax5jG{m{R9;tizV3tKP%H|%Pb#E zyDOPy1}Aa$a-$`S$E=tE_aRXhQa6@B01zmLGu+69K@M&-K*vYO`B&K&Xb`IZd?ao> zoH@?zpQ!{VIXx|Z67<>Ff!~gNm5r&FP&=oEUe2Lgv@n>0IFdFGk7Le+`qMVy0wt5O z5D9K5{hMB)lNh5ChHYhqbg8DRfd&8$>WIKIJesDk_2oZsc?#4O&7L2BTyKC-hvHCS zmWqJlE@)~3r2iTKxaCZ|QC;Jx0+6W*NJ~fdLIeBqhOfe6#4(m~3!K>+JWFs21 z8}ci=8ThrgryJu4?4l*9I&vI*1rFp-An{TPiur+)GOk!4bW>hrr)BK7x9D+05+pIOx4OUBFyXFFvkeTfPISrnYt9A52O(DX=c@vlue#h8H)^V>gPpfCBezi=Q$K^83^Cn5Mx)t4gj_| z>ic1GeX;qT6)Z(BVz~Z7#^GrD&THCPF~mqr$rsM&*G9B`SuM%zfIJzVeHAw*y!@Q# zziU`cw_yq*;D7z4c)8vRpktkiwgh@ELoPYcvmXjo`;Jy81ESYQZwMdWFz5ZYlZt7d zHR&0&<3Y}Ky{uYyBMaFTGpTZ3+O!f@AYSN$39ssYqC~G*lStZTj*7}bEny}U!ibT( zF6QrO>r1<`X^Wk7=8+)$IN%n#qby)W)nS>B6HrCyAYx~}S$AuZ_uvQtI9sxf7U<%| zX$DQP)sicpK2=fROp8w^^FQ31#b#3g5HiT@48o67Csg!=+80}2cP%^Vcvh9OZbIoe zLJxN%DyB@fcNil<7Go?qq!)@7y_9%f)!1N1~u1(4Fw5>1#t4lk@ z7WAJZAs?6PLcJdyoy;2Np7C*oR!*ao(*O_@6!DdO)TXhswrBbD$88)G`~$C({nzB% z9s~r4rI~3AiTHZE)4O|sER41j7*MCwmt0poRgeD6LUfDoq8W$%GOH5M9R$f5O+MlQMlQTaCsbfS-dWT=uX4>cML+h8b0bD!)Ru4}!T|6A ze19}quc3);gf$GkJMPg^kDHm!fQYi3w&*R3+L#3vYq1 zrFj1zM1K@kZZ~Wylp5v5n~fY;4qU4#@o{E^?0+OX`uJtb|DB!3=$Mx03XbIa3Onm% zB;E!R+%mqUP&}kcynQ|39~jFYZFc5#RRQT|3eC^F22B^jPr~jU<%%2rx)@OW{BL;9 z3K!aX)jHblvm5BU;g8+TUpfm4Uc7@#xP!)-$s<#9K5&waT%eb~G zeSB_cmGjH>SrBgAN8G5wSTtdSXMkQzCG2jC=DjrhP#c*(XHL`Y{2q5ya9X;(Jjmb! zoY;R8Nsfe6Odgga7#3_J2p&qEnb#xzs4J@R@frQ2>8bE=xzqpulYtjMz*FU~LnE&5)5oYuBpB43eN#KGwR8TVMM{XRRo2-CVTq4=Jc zl94I8D`=X+?s&F#F3>(99s|X;Wo)>r!SmkVV4&I*5N}%g5PF`JZ3@WbZxuQ=G>TDgg!B-mz^lWHUh?FT>}ojuHigXAnV`IUt?s-h0$!_& z+7~^`m|@&)I!eg?R3}~{Hls(%qNH>ab)jyo-v0Xi+pmhu>5K{2vVCDYXR z89X8Y{uavtf|+|uSt_i-@QxuqlQS4jR(2EA1Zs^8H5`8(uM~e|f%L+Y94|ar%z`-0 z#1dz{w$(we1$0R8jC%|d|2<>Hq@`!G1jyoIh>TeA1@@*PY7fd!^9>VHXFqSA-$rFdfg`^#%w4rq@NB-lIR#dZUV4>aORcdA zYYX7TE-PK7zc15ukrlNqWCfv{1~6zGL&o~gq&m_gt+wm>wDA|Wro}ySiAM{X8Mm>^ z$yXSz5j%7EVS|_NL4mtM018p&U@$HKT0yL*V9`5(j>$!%0EMWRe2ZRY;#2Bflsor0 z%Uq;$^4TTQmC6{*`DLrA^N9p~a z9%r-Dux8hKMG`h3KWUt`j_BE?r%Im&CJ4~b7DVR|t3WPlCu^)D^p6tB$KFOY^!Q7d@yT6qd0igq_b>?~x!iHaPdJ z{5qwSNvsN0W!Q;y1iX>7p5mfyx8*EZkNLZevfuS8A=c8z%c6NH?_C~>h7-Km;N)-4 zT2h2o;btedtR2Xw^e1JB04#h)Q7K(HFk2WV_{yAIu;0!aY1#tOld0r-H6*z|2axYZ zN1sPebJJU7n;G=W5z7A+uw+{)GCJfky)!?Fw#6nJgGomA8)Go9J+p*cph3)x^}4!_ z<1Te2^{iP4EJU?-X$SNstcE*&a$Xaza!H=2$E@b(j`-)#1duogGjx4rRDE3aA+wG+D&6A86YI)PoDny7nj;Tzp>*Gc#N z%@AWN$`W7ww0MQIk+vk=yXzqLX}{bYkbi%Q^n*8fzt*B(?Uqe!eIJ|v@KneCPBisU z|BUNX10V+{U~&rxo!BBqPoZY{EFlG70Wru9sVk;!WQac-I84e;P6O121zvtYS$YOq7a{Ed{w-CG$%{m2m=e?&|4!eyG;4jZ)oXLnlozK7~|&ZirGBq zh*XKyxASgCj(VaHWI=$<28GhKdcMiX4Ej~5XrtbkL@&r@J0$B6+FbnlZsQh3;iIl&1a#2vxzF)m~Uxt{07V>5$pJ0Le0YpPRUWbeUA#QMI-P`5uwn z8-ps^(lxkcrHYWFAtRumg8}99{;tYZS0$jS3Sr1Zrj5?4Xh#vLS3R7x=hHb^;^x zCSoG%)yKP)jG!Po?7japl{eb!nLHS=fH38*1^fiob|cEoP*}8^SZb%n5OVN>@cBor z0Nu^2TSyT@pptQFMLn0Wdv^?6#Y_||C;s^K?KnK5vatmuOa5>|Thgap_V58Uk%hRZ zAEDP_!IH7Rb)X%S=K8}icyzJ3pOKk#)9RD9mY7k2&reW>N^`r2BG;mrp@MJ|My8Osx{Cyvp&vf_^9Kj0Hb<%-&`Lu{nTE*d!PX0cqfI^zf5m6!5|o@hOnnWA~fJgBG4(*z1g>5b$Cly_^v-m z(Giy@C1+AnCQlEud>(`>sJ)2FSWc4ERFsS;!sEG>=+6t1$gGKOh;jdHS1h*n;CVwN zlF8$)po|zL7s(Utrm5RuRQa|4+Dr$SINk)P2OECAsjuJX3V*=tm30GV#rC?hI3dBn zxuJg!$pOMfM_Af|Pk(Ax)9gWHH{|uuC-!UIMk|=Qf=UY3(0#~n**{gp-L&i~Nu4fw zkNeu#_GNJJAM)V3>B%QR1L7Mt@)M$4^j`UdS|(UzbD=}5Z2>SwMQ~d3&BqKc0lfVp zRT$+IY=h=QwP9e8>m0t5_Tz%o~9_ylegEIDBx9FJrStP8F zaKMroz8f1a^h9{sgI))=)f1v4!(r5szf=A_msU5Ew4C@Iv*^|av`pt&50CWTr$*+K z8~9!3K9`0wpAQ_1m=8wwS?2=QM?m??FBy4*@HcA3Lc96`@(w}4jHoGcHi|VUDGFH9 z({?h(4*@=Yjm|f(W)%xU>d;;8D&IBXk*0?)HnK!*t@dd)S>r=WMq)n2v>VB`q-tqo6xP+%Y#xvNvLAh+cqt z!~84>=iOxt1Z{SrP~qHZ)*LzQYIf#*eVzti``&Ba!FCg7vO$1#|DX=5Lja zyE1Zh7rcqJjwFJwZAFPAm8C`LsvawA-bP6-?%Z0}Jr(SZ_nyz>C*^=31yOi=exhcP zhOVF!HS7lG{*FcIT^H$$>CU2mhaC5(|K&ucCt*wz$AIfP>soR`l zYLzPg>#8){Dpc=}u%ljTksXy>=>g%WgUfwMTtu*}W%s1-lwAUB7_6?b&XEOVZO@i}$9^Tp#~% zceTsg+zBPZFlvCcOS(c@4kOZMQR6G;mg_@QZyl)6Tsd*w%=9NPXd>4tlNYA@4hdL^ zWPU+d${Ra24pTi%YaSdA=n5@J3oOtE<;zdDxer58+#Qufel?rHd?q+da{IWPy99|RRhZCRkGD)zM!ROe>uGVC6nGF z21ziJNQx5IpzWIJ;S`nFRC=-N&j%e{aoCxBF3Mj(#6E-kB4q{0}c8y0~^(rOcQe~(XszKk%d|nJtAC!9Z%T1IAlDIxC&e}T zENAtGR1RNKT^DWiQr>#~Pmf&*cjfePY4paE{rW>4%bT^YhPdTQGfrPQPFYEa^cha4 zy~yo)S_K9^iA#gJjs4PE%jtM3l~FtpFGfwpj!`g9lSl4eK%A4dR(yebrTYDI&VKy9 zy;>FmzpLVzidrA^NJ>}pd$42-d?-&(iP$ZoPlAMeED)9W)K`eBT@pVNw zLvN6C8o@Rl(fXIAyM|So6&c{7Gs)6NZU7!4oxJ|oZ9H|Uq0Y4mX~dEWY}h_EPQeLr zo%BZB#ss0P^edd!0mg(L!PJ49EPCrq5Ez$uWyVnabIYW(X8idiIEUmUrSIMku7on2 zSAP698dSQ&Q}n;WJvO7GT`b|$>RZqmNiy4gMMk{@*^cYH=_c4sojKw^l*y>RT~t5r z5kW`J5&k?+-gkBgQ)D+TXtpJn&7Z|{d?jz>&cCo?t3;WW3VdS4lB<^2H*Gcj-Zz0$ z91Qo;yeWf|uT?LOM__@y*sU}5n5>Vdo_IvBJ_H&B<>n=aahcjA=LDa=iBChj-A1Lf z2fy8VavRrBcZUbK?Gt$l24`h}>pj_Pi4%!#*Pof!->?5%{QF3y38OvgLxVj?)j>g| zo(X#x5nZDaW?yRArQROmBY*B4xOlt^6Ira?oB_3hOSSz{SJd$g6`G zgb{1sV^tSJqW(qjfonr~N2J~+jW_j&m#8}FG1Mk&`wV`ZoomfQ?SAtc#on`q{$qF$ z=IWUKf3y7FeqG%q%>)#YuQN7;I^P3IOooa{RHdZ3p6$P4Uf@X=tYW*;+>n4~e^@xm z-~-VvC|G*xxz^xkPvKh=zt(+UE>#z{6&DcF(ahVam&zf&n!r{42A6stnZ9FDGkoP^ z1}XYxySqv`gp=xfodli5K`8IE<^B`Y1_g;+FAg*pMu&#O`bu4Tla7b)`aY@-17Nf2 zG;A|yXK~8G5s>hA5cJ{XmT9mX-C+ZEH#GQ&TpT}n7ub688!|m304co$;0ijM%5?1< ztg$Yd<+efO$#Dla393IWQ z6P{F{GC*Gaglq1(M{nc&!uF(IEMHMN3YQ{m8Zf-c6*}?H!fB##HC|uSmeDZpoM^t= z3J>=)oR>M{fHnIH4*7Qq;(Iv-%wus$&HM`#@+4E2+jh(~>9D`i?~#T#s^D*%&#`k7Z^iD8~jMNfw;M@)YdQ#-cJUBw(-3uEPQ-zF(xffYG8~~ zKcvb@7*F8gCH5GGB$2mtkZrQxOzQDo6Bg*_2bb3vrAwgr=|S>);*dOjCEd+l58Xhx zFn@w??^Vf6A6P2@oIic;nK&K=*Kf)gq_p2QY*L*#2f=Is0-?ivE<;kW@Sja*3J_FD zcRSJh=~Aedxw^fwc#Be{|(LpR`o;RSx#Hr9R4Om>ddjqK9%nN3CI*7<)(-sMD%u3Qx9CC!h=&cWSBb*Zz2MT+kSGGW%tB8qkLx_~0CcCf*eJ+B?>m4}@nV zAfx(aBHw6}L%1<^&jM3zb202YF_>W1Y6{{4Yb}TepQZbRr;UA}w#ta(Vj(d5KCoH? zVCJiNk$3Q-2-c{11F@=g&;RUya_L#qIA~7zp}z%sJe3%qie+uy`Er6y%1P*_I_dJ0 zbV@)hV2xy?Sf`OMJ?=A(S(D0VNa}0~W+I-xY5uNK^N4qVc1<M zv?*Vlc=kQ%k)bVXDZkZ(sU_quccDIMp$hp-vw@yl^Sh$}sXK-Os5Nw@Nw91Fm6p7> z&~(Mykl`q8wx@&^DeKc00iq9PFN9S56c!Qj#v#fy3%H1PxqL8z$Gdw02-dAa=gYNL z2=Z%@GD|ZUow9Z^)*ed!+p}@@tYQk#yG59ndI2&{S#T9bbVaC}N!Zjqt=v$qXUe$% zHQCFuWiV4FOcvzYvJ%^*2N+K$Bxn03AYhb&f2v&C@s;IAjLHRsl{7hvKm*vZ5(C_Y#Vo zwD*7;D<)y!X5U@EvNSuGAo{d8pHyU2g7IxmMiZC+W=P?}(~@CmVm->dVzwutr3xjg zGGq8!LQ{XDSG5YEE)jrYO4tWLdO0FxeRf>i1QNUC?Xg2rk(jl`9*~U& z8i*`YM}z&+jqL6;jD2pmT;!oYGt+8?h_f>ndBF@`vB>Yie(jH8*kvqlBh0=gE%`os zr!d4s{vqA*x1-#W;*Z?qLWeuPp_oco%Y3|URop#3`|SlQRZe}+PgvI>b@5BGMFa=F zI7H6Gl8%YlLQHHIu98oc?2EPu@ZXVG2iH-nF zPr?efbe{kTP`=`7GuV3g=fKaWk|b9@pOfxylC+GJ_k1=%6drG(Cs4l$fMWW`+M?g) zjgC;`A0}bY1Ayzu#>O^#8O*zQeJr89uRaTdp2?+wE?T7J4)hwn!GM|e4_r!dQb@X( z>^yoMD-5i9i3*>OQAEEpJNct;#vd`mCB~WfS?i8t1mSuxf8k(U59ggd=Os$=>WBmL zCwwt+?5LgnY5i%)iCnx(?8{jpW)l_%RKDl!d`7h& zfoiFf|IG|=C6}j4XB`TnXJ#-4+ECYR(-)=9-5@QJ?<-;)Q23`WpY~R!U6?Xg)zPGZ2f(-6 zJs_tKGKwDTta&s1ete(!cMIm{REb5J_(T_KY(4HCm%e>-QlMU9a~@VW!8gOc_Oh*1 zbGp)qHeLAH9{vg(!2EY^uBp6=p+1K`QcV+Qp(#>4nQ@{VzP;5sF7#+;7Tk}EPbqN9WGAHRm!FIwwFZ) z+~}#gMpoFFNm+~kkYsG}Nd64>w38;>aCilOn(mGMKAKDy4YPj0{%PuFhOwnF^9o<2dVM0o=#zoLRn&?i<4?z5>r# z!;lP{5L<_g{|b{#jzP`T|E@=&cE~B7;5CNDXz^+GZ!16}RmIF;$e;dE&|M6pKoGmg z`dXh%W=m%S3RA?demJbh$ht$rGZ#nF<>=%Z*=c*AskV+^FvaOcwNQ=Vj&iWV1ZfIfe!9&jiG>TLV60ms06l!AiFCRI4v%x-40HHrOrO91A1-o zd;tG9H#c;;Krdrj1X*EHZq@d{`X=-#o4X zsO&4J_CZH$5*yi#{zaQP%vG@A|tO2$Ln9*ykiA(8T>|M*5#~KHEd13M=$zS4*Yp6F}S=NdxC$)M~^Xt~I1R z_2^H>&ogk!6HjuTx~F$#?n2JE7{ZoHR6jWqvHdJt#78qb2it%7h(A} zF?yY(}2Mu99xJ|EF>@1Ru46>L43YsBjVKuS`P<`)M1we1v;pt ziH%(Sn&ut+y+MZfMP1bDrPPn3=jd%=0F(O(D=G^V@E?vqeclHSz%BSbU0M@4CvM2r z@Xw4G$ayp6#-S3x;iTAQf}v6SF6!U#nmbA|v;zwzyhfpQ3rU|pOebQ<^OT%<--y*G}d7Z`_N2egt zrYfU88(QungmVKFlFIn6ff@wE*BEvQ5|6wW&}TCcEs!+5CQVzJ>GQ+4kD}3S@lejtXm=%UT-g(+DU2?KE$w3GPs#lZb>&jSfdeJNt6DH)~ zzExOGf@!C2Q1@?(iSrUua5nR_xl0L{B9NjC+;}73#$T*|2Tj6GLOQ)M(s%q7|8X@7 z>Iuq;sI6}^-?XYAB!Z*^0HFeu6uA5FZ6x&@)%K;CgPUVk6?;Rg`He-i83Nui&_+Wz z6N>Q$N;7f{MCn!h9yf6GKEEn^acNgkhSdpkqPPD;oK}!L;KrcvMfP9TPXG62*LC|B zcow7!)+8=oXm;64n~NyaafhNz`z_Yw74$Uc}c zcy)`|Rti=FFq8F)%qOSzaep7a)^&6q@uX-iF&O{ttT!TL_$fimW>rOjYu@!Zd!9ek z*-a>sld$2Zv0H{{|IIX`um-l&l-w(pttNj>at=wr3fa35_Nt*f13nj9h4V#Ladt1l z!&HK5I-qMbG(J^OhJKWerbNB~d*zGjHVSG_p%ExL%dmn!j53RD?L?ip`3kjlW^mp+ zWf#F={QBS=2JjveECzc$8e)<%jh+%iI$i&hyh7$L*MAW*S#j)QiY5S_Zvmk?$3WtpOKu%rt)ZMekPW968LQ$;6LK*rR139;kdbTj{FU1I; zDlmzxU)9*Tt8*)oy_R7m6Utw5Mn8?(^|v#_(2Px8bm;77_@eC ztsfT>zTi3dbEv0?9b0v?KTF?^W@FPX)=(24S<*uZ-^zxRnI_Q26o&nLRVq^p=1m+% zW=naoA5ZoN_A*9a7!Vjx2>--dO~}2s8*NX+OdEjvR%b>Y?1Z|vYYh9!Ft=YIzz3_B z0MhDi9)Ko+I_#V&rkFqBLk03k>Bgv9np_MugCIwN+DU{@PHU72A&Le&X`rRpnI22z ztNU{%g@c!cJEYt4F&CBFikUP#Z zrR=F<^t+Yw&x87kr7(Q3%<&{RSd`jO zet#AQ|8<4>SYS1~Wf1gLao9t8!_-g9xQ2=L2H@WW`tq5*309)2z^E!xO+zC{LoZTE z9#K+$Ok2_P|4N^~IqO!Nr&lVZSOvq_f#GAleer0o8l9x1!zv2|k{2Nrf4~s4s>o=S zB3VL#%hsghm1kzdkO$OY&dun-K2!n>Szoc;b>xDdV(ztPFgLjVzFKf;8DarLfqmPP$NEwOC0wq3?tuxbPs!ZDZvKqo$3Ql3f1#au z{)4pLQ!G((Yvma|Z=!w)bnbZ7qpT0!&Ix#&Oks>?;%*io*#KOj3X`lps&nhA=;34vi*!e?LHRZCR5GHW>TA0Dk z;JtM1tN9F+grmbY{NPFGS{tCv$%zQceG34l`8w=~o#dq~Hh|;=dW^>B&(8@51y$>n z?XTh`q+33^uZB*A_pA)xr$#r)%m?%3r|98%-bxqVoDf7RCgDY3jD$Xj{Tz=j@ju>A zO-)BTbOfuMzJ#GOXJG-Zgvz@nj-Qo!DX}x$^ykZs3VP^_aEeEw=3RxGN zVd}HmD|5ADZcGnkG{e|1QaN`3+2kyLLUAwg zmhs&7;K09N%W}$P0yhFa=RQJe#$}3b{58rR@{SWa#R}c-<)#8-{&=d-d>{Nu+dMWRc>iA;GW*NKU_|{C4F) zp{J~kEa6>0ABHWYr>ePvqYLh<*0B`9#+&ufhjE=Bg z>6R-?@2#mw^9NKrr8@k-*W)axV>xhO(t$PbLr^oVlD&VLei4Z514hYyq8VgrMJ#0c z`l7HZ3C<)}IvZWV9{(j$Y$(3-F{V~Q+4U}W4y>qsjMxYU)?7cUwjL1a-Dz_YKi2lU z?kQ*+?lsP}GhBvbFJfFBsO4hW1Y%E3kAYYZFl+f5i63wfk9?z9gWg6q*o{5J1QJP& z1>U#P_Zg>Pa?c9iV@+tD;*5RmmGD-%yjx86MA9%-*49KC`m!4T#&T~9K8B(YKVHf> zI(6J)j!UZ01PUa#6m#7Wd=E4-Pm^>hf%Dh#{tsV&>4p}I`@Jkndp(;#B0o+Y!Zb-5 z_bb`OD1!7gMXrYy*j=isnNWA%)A)IKJ;1L7`>c#>ynIx?PIh!VEJ%6~a3D}$t6A^| z7%v$@;G2o7qvZZLw#lky4%CbPM*p+!6RP+suS_o+VAFi&I&81nu&W9K$l#_>uV$WO z^E@@+<5~Wk$4E`!yGmXiB(4H_-Q%0CFu_(ilO_AvNZDDZp}55LqC47R2`X;#f4sgJ z<{Kug^EZje!#AS_X(4Knrx|T0m(6M}Ot=PHF{J0gV6FYOO#Da5i3uf!IB;di0K_k( z6Hk~*ubV{zCrXT9!KV;ja-TnEFy`X4{&_k1t5Jha28MRConQ(n?FPGQdGzVlm2uoo zP}Fg;k*EAw*i3=@9x|i3aWNSNyO&h1m}qra&@@KXxj?OAy&VdDa{|CYQ z(q^~1yMsA)vDZL@y4I-IKaxh0Wv)I_<8zBRMOmH0BUha~w9fFbS&iK4qrAb1U%wgV z>@X^#J_meWnfuyJ@sq*y8Ck&wP8=Rv{HGJXiV69}Xpns;kEUQqE6_FSo_-YzW>4L} z?I_YRuIKXPMOBm-)Vf}aRi2fVl=_0(!~)CNe%yIm=%Ja-k-sp?YL0 zYtuc3N~OhyyB|_H0pN(_XCvilRRNAADhhKo)O&K<>$xRZV~ZFr(PUq3AM{H23Pd2V z?4F?;V-h38K%;a#4DG^pDoib$AoYSSht2Mk1pLCH9!nWGDdFg@pKlR>IZGo~*HqJb z|AqOuoW6x+oz{S8ljHhumuJd=zVT7+C6O^cb&-wDd4qAfp~uhTgmC9wT`5-zOWyexyTSP=pOq4P9^C47^%(d19?9xhOx!(bhl zk4=Y{yrxy>6{W@q7Z`P}P##XKn6K(ZF`4xbY~nZRQ6O&ECZi)rLDyBeY0LLHi9*o& zyD~TTSJ%t2mrOlF&8ulIvn>pD-;S-~MJ53l%3tJ1Gm6wpXI4_&OO##R5va9nzhK~j zF7hh?L`9~ahhAXmsiMon=HqhWZZ>XAZjb6J=v(iU)r{7HDo}$}TDDgbMJfw2LYs1D ze%~NvHhB9~(+X0$Q6ro}7H9m+q-jdB+0NWRLJKNEYJu6 z04hM$zlt|G_dG(o0l>@NS@Pq?uwKb^6!@S0vC1QPFzml$FwrZtwIj{AGekw*)I15a zR~#d>m^0INTMuP;Lh?Ud6uNl9Q`}0x4=Za0{4}RyMfYlp=gP$Mm7N4l5wvR$vhGVW z(RUR;BAIX=veab)+|0#lr1r_i(O4asH1G(^k)JZ?(DBw4ZHLCKpZu5Q09K1P{KEt| zEpfN1|L+nCwodFRFz}#}`op{}H;+-P5pk{;GwaL550UctrehF}%q_Y<-V?UL#KeD` zn#)c8PZ!invX6Y*d|3$1>B@39FPx1Ae^I(Whoy^1m>$ zDf8$3%zB#Rw}aWB4Dr}n8!N`251n4O%g|0HUp9KLR#Mao=S~%Fd;03gvq6sK@VAI; zKBpaiOj}a!KssKUPiQEnu{ihiM{2v-?T$gi{e7Kz@ZMnjQLo6oZPQeV8NxZK%kBLdPdD|sCD&+~>sL*5XxfLwlL2F%? zelbK0#^n);!Uqc0#_AyA?u9NLZpkMMUd-i-KtMu$^NoS+>Ap?wqdrFI zY&XMtf>R~*(LvO@QBhuw+%$PGu0K2fNL<4Yp+j7^9T2lS#XOsX+WgkBCnq5K)isK|G)9BH z+xNTWkxTM5l&j+J7*uKdJbga5^p@+QeT|T3GKqieNeB^MJ?HKK`5=QWw$%XyzXE*j zCHdVRi`hMtfL3xl@7wUgP9WKuYg<_~hIHK`l>!IV1E|kgvLuZhf!F}=1q!M;b+*2t zE^oP?C1X$LK~aj5MtC9pjts(dY8LL%gVg!=9@?&uamLo@V0-*c`Wm4*=0rqjtpJ08 z*v;j_fQcU;@vs0pSL|eG3@TlKYFgRT=)k9{jy*=XSN{&OO|<)F~%=C&ezccs|7#bQE-dWx+uvPA!Wl{=hm+6n)iPrVy?_FQ&lzg zn9}`s1>Om}MvR;8q1@Vz8+}FN;bLslXZC~;A!1r^q@;zig-cQHFS@6Nj3>ohTXAo@ zjXe2XiEoh7X|7F%(BfT4oCbVT8wo+Q?uMPjEaeWObL#A@1vGs>P_S>HTidMw8PyR?gK8l0W*#*rxB_m zIXP_zgEVLlWu!Z89Eo3ZZ{Vc}!|fe|?uJ6{8Gn+)q`QMXt#(`*M4+p9^QRC6=7oUC zUA5Maa%%;ooy<>8BS#!8tzfa<%fgx^v{U5|Nef)6&<-N~DAcVZjtn&^2#%g?*cwy- zZt(!63ezy6(`R^E@JOfAclijQEQ;-4W+xmuGy3K@WW$uJ_7n3YsNE?HW5>+wJN{YE z#NnhQ!BOguK`RnEI@%Z~2$G;)*{}@i%}2dSbxu7QHZM5`6F)LIepSFa}8lh;Gw$I;linkw*uQaKk0V?{SEg8bm zPVSxvwbv;@jy1$SHq*TpGd^N{fz7HOjfKN{Y;Pv@r$To)Pex#xkT#OSS znDcx3AqWZygj>Sq;2Kl08L6Wybx{PCme5pDyq@bl@V+qXoi3E$h!p|#VNWgo42LwEF@|mX;`{TJ96io? z8m`@Y_*6k`{+Be$0%xPj0bbg?b%xGA00KT;<}y{s?dDMV-jjtk6rpYCrR*CZj~gVk-7sAG zC=gwVH$4cI9+FPpZNyUl)aVxPv+RPRQr>EasUcGl&$@)QvXEg2nnEX+FGw8JKyr@6xW6ArxAP$PH` zSyXYhS*RIyg0TLrsP`b~&2q&;fsF4qv>kz}ms?`z($QQ04tAwkx$W?3Is zbLcrA!+rdPP9%SSwMLtTwdckg)s~m14r4L(-`pB50#7>q@gb(R_d$nD?rBujOX1~_ zwY;XuTB(}k3ToB>No3s& z?Y)r;8ZKG1(KQR#5H~?kb_p>MQ!neljz<9xrR<4LVwv`voz|ETSSp{uTD)DvO~UuY4}=b& ze&cRkB&a;?ho6bPuh2by^P1uT%SEy{T`j~v6RG`dGN`V2Lkp>!hJV+JRK6M5OP>cdA~`Y(&MUau5LQ?8 zl&yIN&*vP&A-DWL5RV!P!=|H41|==B_KWW|C}uj9;iU$qcLt5+{1|NRf|Z%DVvb=h zk`8gU9xX@~T_mHX?HCm_a^|9)vgFd5eP3#ftbMEQ(<&mWg)0;){A8sh40{x1eg#=R zGlj=OC`z%sME^~oidRK6m%GW|$Brb67&w4m0Nl9cU8DtU7&;x+#@lZyd`oJR&whXl zrYHg=s8Q5tiQJwQ1JrFkAhNKwy2CM$vh>@bIPW;0vj#b@Ka7HS`^1@qOTIETcg;Um z7+ycQ0;-EAM}+du)ky8XWDDWNrZNK|v-tcg zbYA*GV$V#?;@ztb0p$oS`EcDelYYQ%9?c@=cg1qv`T7u}V#V zfQ>!#LUKCiA~Hn;LB*%+nP`dnZIrCDu*6tT%F;z4QQ>miu1)~TgJ5&$yER5Br6Iw8 zO1JV3Qpg}Y+a(H~&@=GR&A&{gmVXSRGT>JuneS*=I1dbzdV4uSj&zCYGe+pyFl*@J z!PA@u!aCKX49QP8K;|fNrO3=#oPO#Dq(61-v*u^5u>|dg%ZaNj@#m+12l`}hFVm4z zIlJor8OGT%Mdo-eA1h|Usw|3HdXB|%NB=G(D!IY`m@g>Zq-dqH3w3JV77NY9$JLPF z8$nBWyCjV zRuJKlYReVNDKsY$pp0A|qzN$llhMQ zjYULDf|aBE-nj}tfO;Pyg#3{|BC-GECMZXA5|X0Soooy*fnIlcQ{&r%adFSj$}r2z zK-+MD&T=3u0pQ24DMP>@KfhyrL=XzX?C zWUfA92bF_Gxt%OH=jmr!yS7=LsoRW>v;RL4WITj~kV$$sOEHE2Izke`UhC~4DH&56 z^--I+thl05rJJ=J`#7dhBPY3#DkB^Yw-}@u}AW{x~fXA#qZPxkoIwIHgbf9L^ez~ zP!bvV$VY-dU)~2~p3td@%PbvL+ni8ni=*!H^RT+12Fs!t+-wyrfHP#=4}Y2_05R9| zE->s418_o;yV4@lr=gDF&ZVqD>>(+kt5|RP!j{9mo267SRxz{m?==gZKHa1E!OUl3 zxYdGUC2dtbB`xfOO*$);83F!6?@=OmmK^B>B9~Hwi3*LXh*@9z; zFP#2$uL-n9LtLJ#F6_pF4u9O%4=d|`teTaPJlfW9x|GYIeyrjMD1G(lsQ7n17aMKj~CJGjt|vIZ@Q7zP#Q zJLnU!dZFcaw1~W7Bws57yao^AB|!T<^REm-F7JTXh2QNGRBJ&B|bTvbne#OLGfNubC^|~ z-pM~9kiyAlle;J4o*$8>CI2Vs4odp8>8A=*>qcqYY3n;nmD12Co^ya!_29Em=137G zU*Lh7jV)q9vyU-@a3`L+KCvDOjRD1tsOLy-q-M($x^!_sLe9p)?AI}zy|si0`Ci-f zqCVm;@@|{apy!T0ngh=LmGfF{e>2LO@mHURBcQ};+SGlQ`&QRv1&S)VR}W=vpPPdg zj)T7Io4d>HmuxmUleb(Zd%-nod)k|pqy?_q#|yqCWC?>br`55g9MW(A`@iu(v5or& zDmOX#>Y*``;eFj%0tgE|#pU@M%`NrfRAX4OtZNdbvcO(Dxn5viFl(F%ufN}zVeS&u zp!QI!8dw+Z$=OK)QiB6G09AeS9(fE=eS@1D@d-;baF7!7HHZHwVkerXrS_*0L`Vhp ztbCB{8!w>9Fj;}`+e3RWqPHqj-{-;8LE~fB{rNy^dHz)mLL0L`Z=}q1qz? z6*rR|n*YG@59U9JQ*P~f<38268MSPxr4gVX^Ro&)BeJ)E&_wd7*4{y zr58y9&+;(0WYOIEQa$AB-E)<*i%$UCV-i4`RE9{4^@5~N{1uvUOvdWHw54n#lM!>C zFc=kE&S^N6GF-b0_$LmV`l>-LouE3fMcoyNm<}axb%F-5e?I>;YoLJ6>}gh-slD9d zh|pZ8Sb&&F@LNSJ;B7&sPT1x{ZM$KsL+XrG5+5dTxV{d6X_V*ogz+R@(Ddp(U!?sR z-t#RwgFJ=38I2kcO=qwTXH7{hdr>Db|22&!-afG-EqzEt_y*l4YCoeb{ z4_^77js+bonJb0HxP@}+&l@MUt4&T8+KtZVAk_6h5Kt&jShrpxWplDI1u;yVdg;~V z*r3&-yea&Fk%ym!gmnEd2KuuqBK}Yqw!H&X5_1P(AWg^7VaqCc^;skQNxq;p(W(OD z?!2NmZojeruam<8%j7wVGo+Rah6hlB-p)u>!nK%i{zh;dH(@0g+w|m~8eB&%kU$6u z=GX%;hgPjZuuYCpeqzs3ptiBb591Vi$}}^Pu#T-joMlb3XObW*O5v$4$1S9UablhW zhR$?kJ1(J^&-e- zSuF1Nla1={HWOV+LvJkNsV_sN>@|E~+r!K#{7oz=69YHsrh(1wR#B#sTBhGy(o{!- zZVwuo(j<0m5uSVhW{@P@!5UQdp9zy1Gx>SY8woI;z)7({Hb+1&K1u<8AfH-KV4VSY z@2;YqYAAg9euc3u`%a3R#m&cG{)pw+&SvPz&F`OdzS$V8AQUty0ty<8MCgz`4- zZ+U%Auftn3phD}%@N)}g3Rwd>#t{OUo3h-0Z}{0P+74~dbTI|KRT39#Vix$x3Lk@S z5pSNUbv*xM5%d?g%o`IS*h)|@H|XxO^aZlo9j_`uv8Z|ZH1=Si1*RaCOEkzrti4sq}P&D=lA*rh*3Wsf{_P-PgRT0}l zHdmyvZWVa4asO^t3va|rxH4O>!I(bf^kW-U#*;fBw&ZQx>Qehbo0CtUQM8w?IK#gD zDHkNGeOv2TLuICTnV&Y*iB0tc2ycj4*TwQ!1npYo05?&QKar+J5@1G0|cEy7Ts^jOA8l zqFzPI3vO+ku4~7`?0&sw-$xjyu?5Z-4jn;GwgsgziLP%~!H60N^U z+}ohH%q@cqU^J1ViCx>J0C*Mt_x-K7Rk6ueCV*X^xQ%pXB3(p&dLvl*+Bh&N`=4G@ zE~1R5kjR+UeX|P>LgF=U)od-Orf}fYDIkF5DAJnsSJ|@@+##pNnhM2&=Pfn+NYpo# zW;a`hv{(sTr934i^>jWu7U%gva-_2$L{S0hN_5$@lQT8u1z<sk%kN3dAx4wPrgG$P4JZdzn))6`mF)VDAO4i)v8PaZks5?6dsOeGB zGJ^!u-*bmyZ1y0+DJ&0^pkdnkv3G<9--U07@p%u?L-l>!e9D%RnzrAFODYsTbgJrf zAJu+(MYT|;?}Kwsje(fh8+0uWTZ>u0VQ%smLwik+&!PB*hwz}{pf|250#laK<#FIv0{^k^m- zw^=nkT4^i!Ew9WtNQ(Q^kCWu^IxEqJB}+aRUA-9NR$c@x>)&JHIz&S=h*!-q>yE@o zN6F86QzIeKYd$rieLB7`nR&nKFnVuP)!V}>`;bih90zJYyajHa$QLacw(NyYmwc;n zEXJV6G?XBaz1!h;Y0qpQKU}kS=qY~ZL>dRFv(D24?3IhdX|2(*(;W8{9wo3>10)5U zm?hr`is`+N+pcVLbA2P9^kgI(u*s$hCG%2ZG0Vg_Mu$ao%cNe)?565MhPyf4;Ry=F z?FxL2j&|0=)y?IYc5e$@sOeZ81w@#4&r=??=8@CJ68OLi550=!tuG1uZ45@) zMQbk7&ezDPGVN=$4yzNNOjP!GvoCv{ZF~f6#CF9_frcN5K4|BIAC@r5OMwVjm7ivu zJ&la6VG{u(KDWu-#X)wk$lxY=;(79(eHNhypw?!wh12W+&T%ogGjb0tp>08mS&}3k z5K(Yg=|!sNjjFne6_*O9qC{6Gp}{GtM=%c`scD093O=%Npi0VL>W147!NUvO?Zi%GzE|u&!aR*<#f>1D+O|yY6^aVB(H&*Skk~e9#NRR zQE_O`|Gx)dInw?0IN5ka0#$unIb}8z)g`76pMY!ofTDVK@$*6)*FA+57hp$(5+6oR zUvo;R@*1_cHd0yG!A3Zyp*YuqZvneI8(}eMVC$%29E z1tzOlFC5I83b*)tAvd=sYn1b4W2p7lO)$kJ6o16oAT?*BW*DXq(EfDQCgdFql%>Bn#Hc4Op%mXWUARJrbWVA(jMlue zpcq>Uj5P;e)<)v~ogT2N`!^%-~i;NHa?c$Hq;GJ#GJ> zo|nuOF0}T^li<`X?b|zf-{93Vp1uWw<7Wp%-(QZ>fAX6#rlYUQFEQfx(0|8H7&2nt zlS79SCS*c2&C}z^VQXS!%*EF!ReBaOuC%TkCTYB5<+3e2*G^jV|` zD3UN)oLD>v6#TxPbC^OHBznLj(-}Ek+eB)!f6YBN@A!t)#p?34VB<)p<|lnwt@?S| z{MzfdDE4E8xTh|6SSmvEyrdO-UKu^reyK8!f7#Ahf6dihN;j9PJsfhKRpx%0VBczQ z{0Bs|eVDJ2t5TSGfk}Ct3<_~5t>(yX)81NtcyhY!QH?gz0?s*BU-6;@Sc+>fV7~Br z+H)4~X`*yhzyu>Tw}KuQsE?(S#TdWzww?T+0@b(*Pr#nInScGoh9HOP`X95T5^Cgk zGrzu4pS)U;LUFVGr-@|wt*|#S(ld-J&>n8Q@R^)>>`Fcug^bbZ{luP`g)rh58U;hb zwb+P0SU9%_#R=mCiamGJ&p*dM!d+O{YI`+s$&rE7RLJBDl!5=}2r+y=V{g26{i@lc z@ zox!#D*6$WFy6veI&VTWMs>gRI_B%FVx}l~nDHf_5_oN>opV>jnP7|)=jCG4awTzS; zob@Ag+)clXsPHk8Mmp)w69Xqho7dsbITwPqZ1JJB=O%6`R2mboxt zNE@krIHPmjUrC!4Gk_w9C10mN^idjxXHO`+f#8i^SH$RWgljrB%RV}y2*np|J zB$!hSm!HZ#?mZ`S#^!po!|=o~@^Oa&J`#`$ypVf7(YM67v_HWm1>)9^Ctmue#WyMbsHaT+2u z_pa+Z^UkxZSqls~x+F?cwHM!3_H&hIWE9uR^h;mq^JS2n-QFbAOAHwWy466cj3rpRHEWpITp}*!H_uxs+SbT z*Xr^w(uTkKD^n4jXijGgx^0|Qff+Qm$$l?+D@Co`ZE^P15?S+iKzvRrz5$&?NPMz7 zjwENG>G4NK?rhbU!L zEy1HKx-6#ZzNLO|S!MyC=is(sChdVAwSu!49}aQP778Io;F^zgpbKHaDuU1;*IW+M zWuQZUNE8aX*L#R-Jzu#r2ZGNY{-!-cK=fRZP!)UmAQR;ExfXiNxdJTUxL7XyRIT^R z3V9f@;j^2x)>{dYlHvM>kTY1(tt2orCA@D835D$o6zeTd@-D){QQWeJZ+`# zl>yVY`DD1&Lw2}J1xZV-L#M%yksKP)n7cwB8W&7l!#;$4vkc|i;g=r5h z=m`v{IM8qlWeQCJnKQ=5?Sm6~1C&j`knq3i9hd+QSI-~kZHEkZpMdeSJMEa8dUxfQFAZI zRe)?A$O43}sA4&|Kj4p2KMHn%?xc55p$?nrk!Hu(wfBRaVv&RD4Zor3JH zN(SLI+o{ys)do4OgUI4UeRxoAFM(vD>HW@B{@DQ?Bw&?@wVHbCWusYBNxBqM-I{nv zstS!QNZuo(-I3bwT=o-*-HqNFO@Z8B&8#L%QJJUc4%AS^Y_A^z!h(=W(mo}V)Zf>U z_71D}D8v3XmRh=G%LOwRbcyM(r@3rFU1TYl%74$xw&(pI%A-f5g{>2BT5!B>8H8lf zwUR&z=Tl&55Mlr2cPsYv4nmIlvU)!|gcOc)2(j&LN= zEDqyrc1=Gf`p+0oR`G?o8svjl5x5y(H@$73zD(m#rm~bU7K5(I4}ER26|>sAON->MN%y7@<_$O!I-a#(BZPt1^i$>83u99_wIC z52sMd=NxEg+d8M#XF4BQnkTGzQam?B>XqPVOLTt-#InzPd@xRR%2dP%7wm}Y3Jh8s zVqcL1+k2VmtsxUYw(^6mjk=-9&`zNr3RtU~Z5^lb^=|(0YjXy7+|r9RwQ1hN@^5Hn z;&%|5!(X-^f~$y*0THH9RvQO-nvdc4!SFBD5livF93sOVdT@Hw%(~PD22Ck)S6y6l zY3bk3GOsh9b{Dw_6*m5QJy(EmvaKn#FFn7yqZQ2Z3NuRMS0mCM)lryY|18?YW$1k6 zeRL|@C)~sEg|u53H~{f<%G2$&;r9mU z{nixBcL~nZYBco--k6MG5N{dOcVI4dLWlI^^D@{FI4yF{Ok3dY(H)Sp;?>-|kNxZ7 zs5#hv;xEqh{YnOs6GYSOkh(mn?>{web*HJLoc~=qZ^2dv6I`S-{1?W5H@dD^RpQyj z`@OK_JuiZch~VnDmRkm7jgIodDU~(He9D?dEC%WIr%S}_K_f+DeIi=3Rr{iYuzrpO zMS!1&K(ZfyS0IFf`~FyX=ZybcOsmekO{dAv<;z=#ROo}blhTka`Kl$EeYY6mGm={1wW`gRS5I@063yN`C3iLl> zga$eawWA0L3N||GQe6HE^7!IMcW}5#p*Y{a8F^=OAX~e1BoZfCyfz^m! zej_kYZq`!Vaf?(sM0XdZd4Sx{q~XzT`5$>fMANQSyRR!YoyE8Wa~037Waj{J76eif zpP1zKp66OtCX+b}z%|6m#jq}&-2G0VC;rUt{h5V3Hc$AM>=7mH;%f1_tc(*+vD_oi zpDYlxD=7QhSlI+e8a3Ra1`oRGUJRox;jmg|zn&13`G-`x+q3URiTD)w*#(FJ*@tzl zjI72;B5X*ZwoxIHJCjF(NA)%HcbbIr-=-dA@5EJu+)7CbBMT9=qQ#+_>M)PoR5>c) zC=!3JYR^adtA%@MT%4tyfwL*~-f2a$ws6P7^6+f9%NQp+RDJLPZ4{lS`{45ErDHOi zXoICS-VY&iW`h6w(MSfra-e7ktP#fiYjs`m-Oh2&P+N^CYikq{@K%sfcs>ykU1at{|5?3i(a5Ndh~bGw)pxkfAaFzK!gji_ZQov- zzGZ5JY4F>oaQWs@-AXeuKM3=mwlE&lrISU0d3R;SHlJT|X^F8eff;c>(i8iVv!(KP zEB(QqzI1gdn!aIeSJKh~b)GjX6wS{wnCHM>FZ|R4JD4^NPtTIu&T+bx=;MT5UIYsLsnvs5dHh=; zhh!sAASuc6UDZA)B8vQG!3y}^pJNEMJbI;{nD#`t8xm(OjP!k4`Iu{Adlg4opn=YD zpy=IDMQO-`KS{PF5V1m+KP(ByH->Lj)#m^?Q4eZDY@Xrhb)WJ@DM=I_*u`;p{Tt@v2i}4h@VYWbgh>>bqJV-FX`Tf!#u^ZM^p>G>+}^F+ILVW zs8qQ$X`p%7Czw=5)n16TdCwZ<+<&r_xrL2A%@fFqIJJx+6A=9i3LOgaWyv5(fbFqV zt1}lZZX5T{%qM3cU*btz!U?E3-l8f8QP-#30zp!LmeJ+?Pu~Xg5HZqmZoS+mu<{@z zJ!rl2$3+5En=T1>?WhZylttQfbQsN9RLZ`GFvvOcR~_n+qg6~Me%6a0USIVm%we_o z`dN$P%>(wTX|MVYLt4O3Zt3ypcU%NTOW|)+q8@GSN4jh~G{;v^fOo`R>~?JAbGXF^ z`Ysb-3b7@|YpccOQ&%Wd7f6V)nhbg3BR{)IDWYpjfbLOi$AOeGZ6LiZsK4e^A|-`m zGAx4k;Cs@?^@jW>;V4vw`ZB)??*^Npdy*wB$HW*3`p&xSEcA^5GryC( zumUH&#SE0$I1Yp9&m0)(+MGA~aPv>yeX@XMAAtrO@2e)P!|^ZPZ+`PvC|+1^OCROA zme{k^oUKWPAm&_9W;qFhJlVFpotxu4uKa{`edvB=2`?YdV&6+H{CQ)%f1<6{T!if7 zRlfof!)_l%)mV^tLlhDO>o*AOJXD}OMK*z)WZBpFVs(!0NA@9=h8@_j75myERSsB+ zSA&^B6%=6@c+OF&9fO#g>MrD1p9+ujr&2~pl|zin(uD=~^i$!OlrMy-%}~8sjvZw0 znaLcA-nc&snE*pC9;Q&vzD4r4<*(Lt2s(gzo{w?BA4l>8xRQEP@K`x`IvdJ3aiC`a zq$85BVY{*wJ}ouY@|p3G#|F?|f-14%?wVWZfDs~xKJL5FyAP@SnFsE44qFOLt=nrv zV0#;Q9Vl1GRU%Rf`5Qb6v2*_kEsm(dH=9j!P7Gi=gcif61#srM|Fr zeBirDWP|i9N{gymquw|q*0Tf{FmrYO?BE{#TKHzYx4RMBi9=?3R)6I#5OImlV?^fT zKa6o3gmeJY=;Qny2`zBZJWf;R8-YEI3)}bv^T*=kHeBHAZ1zX^=%x^ z9N{M)axGp}Y#YPy1zBYcxp%pBjoB5qO6)wxCHw%wIIlW8vK8CiUHLH8eQ2}eo8;Z3lZd6QbpgK!nA>4MyN z2wEM8)_MvkK=uRXQK?8ItJJ?XA98erOWv+pfh@JyZ|ETMp;QcCD`4a+Wq|EG?buKp z`}$@3GO^clz=6(j=K=B8S1q+D*t?kaQH70gNU!mj$#qTUjc8|!<;1R2Oxsl0Z%I4+ga+j*82urCr4;YhUPqRu!1zzfr9() zqI$mrcTcY9lRl{&;{#XD!UFa-y~Wm^aM?ffI;UN(0jQLBI<9K4gAQd~#wcWhj$FzB z9Y~hyCi$ zI-`<}!+q80XBo9;X!az)c2Dz`KfZHZyaJ#dfF9(M_r9yj+C0Z#3Mi!mCJR>Xqww+x zcdvZ_*>(}@;PFVF7m@ln9d7dItFgs9&57vQ-0MA_jT#*i9_H?htWLFQJ8UYo;JkQw z3aH`2$+)5=of`_~>!?-)upOiy11}K1<7~BBr&lo!`GZv_)STfR0&b!#l&Z|mI9;BK z8eQsJOf>}U*K?_;RPK$Oi6aYn3iVuX*!&8c@n=WQWvg_cDGcnF#N_{%75}YhlCux; zZ;3!ZIX#dYkV~$yQ&v=E7AqtgQkeqG-vx<(P}zCGL2>N7oJ0=D%M08Ca+EDf1!;H9 ziHoYRrzL&I3A%1%o9^^=&YDn^3Y*(yx8}e)8HBS1KKr%aEYT||#9VRRd$9Wv&0cciRXeY>5dQ{4+_W8J%MBJ z!_=E;hEGjLwwWQ~d-&Z>GG%{$!ALC;wJe&wv0}yqy~JD704{!kw2F2YaRvLNG}%;D zJE^&Q1PWg5$Erxk4>NegeU%-C#(X0v)p)RTU-oaRCY85tGFBKIKJC)mL9R>AFh~+8$?PUkdbqug^T;bS*Z7uMA7@F|_0ycMBr)3~c#I>+y=ctpr!3*OGXfg*K4_ zTrMdw9v?2jBE{4-++y;V(vH@_sqeAefudNV^8GbD<4HnPx&jmdrzJj)nq1=ob! zk_s||T^3w}KrnUAD3op9uqG)HsVcKjUnZh@Gaj3^iOA*29K>$wPdcO0$PPvz8#Pdt zE=bO3JV|e8+TVHQM`Kn`(;M>+=4O>j22R2C^m9r#liuDv=NkQCCq5&P%!WtXlT_bG z!!M{rd-mXFkWSn_A#Jj$NCTwYQ25O4eW{TkPE!SqrLrSF!(#Q}O1mTYH6kY$0~uw# zQ0!p9L6!`U0q>w5u^{T!R?+s8;amWy+i_${dkibbxc9a2%XI0#Yr#zBb0ew3i#mRM z$=DJfS;Bg;#QY-CzKVYF1L-gEKIV{6G`aN=aQ-d&#Mz!SHu}<%7T_BHce-q&BX0>q zbo|g;8h)9bA?vTraQg#+)D{-|yFq7; zs+Szb_;293E}A)mzV8aF!E-wS>dGI@039U@2|;N8VUTOc88X=uA51hC3!Mlm&i~Xz zw@~X+Ij+;GfWMHL?WP$a$hFJaPtiBuR%k&Q5c~c~{WTORWKQs_n zNL~Avo`+x4Pu6-PI!Lv7R@c&7oK`wQ)!m-ra&psk+mN(lB*8En3Kas=7@HX%WmNCa zG?|5S_kVPezNer=t4)ZhCUr{kKvE*-J~DM=6t*r*-O=*%NqQ_dUY$doN_ubo0oogr z_XJ^?6TvD!8vZ*c<<>yg+j%BMN)piJzlPd_d&Ye_RwQ@biwb<5!{e8zrAuSlBFpu; zE>kjZomffr3QB(X3`(*j(h>Mr=7D&bj7dM_Zl){QzUef)5Dba;uavL*HE_J%m@4B} zwq#tP;Z2a|XA;xEzQc3g%bzD?4^hiki)6p&!X{iP&|~P%b5j{sT^Ca z#mH4`dG>^{FSwO(7tabj{K!z*8`{yzp#_qW%_dfQS=XLf*Oj#~M zN*snjZQ-@H*FTwJ?iBEO&E)_TWk3UC`sxlg^+Kt#$$%B6G%f#a${i%>qasWE*hZxA zn~!B8y&3^LlYvNlRk(qk=I8;65{;1vJ~(dn@C@KG*~8{O`O-2=Yt3QHC;u5=6FG76 ze)j7oBT&60RE50P)p>xFvFL8wlHnLn)|XC&=DZVa{rpj$v{aTpRlGga<0i>-sLn%e zG{GcVswUGN3urXB^xhoEYr$j-X&4c`KHOHJ`k$f8@9)K*J6W(>wLn41JuxoNlawHg z8=TuBG>o7?hLzuw6W>A3lz40J(<7T5GQxb>rK$GV%=Gt5$h45)9JkB?C5tF&%^usR zaN-G~PEf!*>}j+oL*MOlAe>?EuyH>K8F%r>`pM$wOX1rzDxjmVI#5YhJ11T9pAra% zs69a6qn+E(@a^1O5q0#r4343OdV4>~Jjk53~8E7KB(McM!I@G$mA45+NR<29pZh=dQ^Q2OhV;Xa6mTO$%x?)7|Cl5DW*I63@;=!jC+d``QKkJHxnVK@cwq-%tK< zoA)Esx=r!BnbD1UK=EF=68Q`H^DDRqVqY|Q;rLx!38icRPtx$YDIap0;WZCUOkZSs zh0GpQA80A+ts)EhdR9Y*eU56LZujwU(>3#RdWaE>&<4`hf;#=Sn>QfaBp1D+!iaCC ztsU(N{kGtS;`(eU<_|1-;c-WOtW(2AD~Gmv$A+9h>mF>Q@zKt z`rd}h>l=AX$}(YEq9PYFuVZU@W}-;bSrGPe96{xPkmCfleY9IFs*-1kETrsL`sEgC1Dzl!!j8&_W-OqcLDpQ8Mq2;E&@V)QfNi^0y9Tz~3X#cI!7sIs8jM45ITR@TR?D&Ye+;2n zHdF%!$4#D<(<=fhHY@)v69P*X6)=JR&ZO9-;~R2-%DyEbLSu}mCwHQ(wOCAtTB);M zd>v&}*Bcde1!x(RffPN^2jE^O%|?u@d5As5lS(B|``85Qx$H7Z4@`9Kx$a<&?Q415 zE#1~;FGyv^h?L^-m6)evlI-%tM(ug|z5Wj0U#DbHVO|!cP9Vw!;?2UD@QlTpO|xUb z&hCmFVysrU+*~tbGm z*UnaMCbTs8PW~oV!%3g$7A<;7 zH!DKu8%AzI3ekEcrNMO*`Qd3f5QoV|Shp%FBtYVO)@nuAmURf${w>3}V7r@`m)f$U zJ6kVyObOj+5bMCoFQ~pp0|z$WDdL|GLk>C7LaPZSd zk<&SZ7EPbrQV3XH0NG8@qrxm|rZ4Y{rBVXqxwa_84*buFo8kYCyK^z@-6f%EA)&3U zRQQbMU$Q)XQ{9A4#1F0ly(@6{WY0R@=>Kv{@Tilez)Gm}1KX6uOa^0uAc9RYlN!9i z(_7l-MU>jbbUyG{p=QJ??gwYu^y&oHdbv+A_3$3Fw&bLMG3exs6i&GvQ=@1FyM6SF zo8fOedV_B!BsbRBV+!s-Dl{TrZp})ajgd2mZ)LU!sKh)Z_O9m&q#g?p?TRv_9Oe(9 zL%(N52|yuf#rGMrWq@ku0YMNtNeuaR?Hk03OdFg%>B0sZ2@6%{hEGJbWumGBh+CQ;*Q30H1~Og<)-B44^P-yLp_Lh8x*yB;Pi#4d@vu{j7fJb z=!sm%vAwFymO3l=|Ml;ub~ZKUTH5)MSr>5QjEBy1r8VJe`W2}Z0f|Vu?XUKhqll>m z$$h5mENQBIiN9*F>1cYsxB%Q9;q%~=p@(zwtSOO}1ORM0Rr|DHuY&U~_tT(z~3 zmxH0RgIKaLTbiqof1(9$t){=szz1DgcjwfndM`l;DQLs`A0Ph&Q`o> zG?H}sPFoiS6FXt!`AaQQr;)c(CyPWBM-@CL^doBAOn_&-2x-hBU%wyz%a8>u-=ct6 zaB>qeuO#)3pr>-urjE8fCYRBfnoN%Q7pDDf`W52uQppGYY95AV72lQNuway8_Pl?! zY~t*YzN>H#|3ESpl;mSg>m_`-~aJfgF*Mw!)X+=hl0Y&y8^4#F!60U5Xksy|&_qTcaMZrUOX8JhK zGlAg@cFn(FL_Ep4D3x99vrV0>`9|_vSYdgapb^*bhtDiGM+mt%4;Zo(L@NNftqP`r zF{$_POXr>^Uc0cdlzJ;#3=BtnQdNu}Xjs~^&`Xpc%LI0pC!(fpN6TM##$XD)8HTR* zeSmJ*uQlsiAq-$yH&c*ChrERhJ7jiQ0ix7qKKE-)Um}u~l~MpC-pw zQJ;IJhSSr_3Eo(#APOqvD%o(XUzn{sDF--&jX*Gy-L-t6^LW+91+w};P-PjegSfuX-{|{pTWO$~_tdLv{iQN60sSA}H3yf-(k3~W0(`~Rb3iOE zV!jlI+eo+9{0f7dcPN!f!16k#B44hRG6pSA8Bfw2Rg(7f2eo<`dCgM=z0A{0z&LEbx5*vc&C1Ye=cbt6YL>DcmGz;|80$N+ z(^=U7=`UPa*R9CWh&FR#F0TqJ#8{y(`=Wl{u;G8b9@!&e>d9@y;W06Qf7(k|BM7J+ zEoAn&l37C@?rqG~dozq7m;4Ie$D(PGwmX=jl+fS4WKAfdO@^#1CbHIv;N>gHXpf<5 zS!K}xakk@iBZnK zycl>P3%f2=?R`E8jJd`KIM2;q=#lECcJqfM+|q2v;ZvsBFY-=qP3Eq@ib%a?xSTIJQG(jy!3m+{5wLf!8IYv zDAp@7ls9#B*8AAoPw_k_=@eUFc0U*;MD8j{fpx<|H#2_8R|f}BU&=A^6goNYqsrYm zATksdWQo6=%jSSGv_@se3jSU@X!{|9AVNJ*J*=1oHLZ9BcE1_)?-Q7U`8nuGXQ`-T z2T^61Q@#T>ziQnC39clyfm2e^o{+B8a{iwOC?_UGRP`$~v@}~pm-w%!h*6-6*3y1_fH0X&k7!)03}<3{u_1FseX>c z!*pd|VZ9_e8&6Mc#P-IPf`KW5IvlwuT-Dca7neZ!VCIF9@Mq)~!9uF7vFS)k z7xAbx3YBnU!*cRZ$BOzkC@la6l$3$hJtCn-MDhvy0w@?*@Eg%7k*1T8*U)BErfoqG zVn6^K<6hj}mr^XAQ|hiNQmmEMJuB;o%m+LSzZAZd$D7V6O5rloCw#>d&O{>Le{rTA zjV^U90m(PHeVkqBPjRW++k`9C26ZT@Iq8+-tCS!G68?Js%nS%29L-?aDOm;0hytz* zK4V>>0gY0+hDB#=K2BhBC%^u&!%a~FU{RI1c1_x^KEWkT)4TqHLKl`n>8_w0`{vm* zv<0RAJ|#u zj}A)R5<1UjRwJNm@7Q>w-c24)eUGYWI@6)k+B;Di@1rX2znx~0Xt50R|2s8m@O7~H z3BJNwQyvvhX}vHD;d5lPHJMNgvWL=1P#qZB*>%SAidQziDWmXLA(`913kQ#GZ-tdC z38yl``S(#J-8PmR(ImJpy|$Rjut1BK{&B2#=tYJLkuD4RVNPE;ak>4pjbNpuipNXI zmn#0tJeuQ(GlL+qFMpabJvuf2+cN+6uMs%^hMujBzbt>KAeogwdD^cj+ICq@ebIP2 zpeDFZoT3%vS3N=Mr-|D%R69Oiork7*fR5*sb6}wnMW^)H-Oc&Dw5yiKR!ThYF}>X# zcEQD)145Wim1P7FjrVXJ?W6ep%yok=@o%4OVdBV5OG)iQjpthK}>HxgsVGxa#al9-x!k-9ASK2o7wx8=e!73YZEyI~^N`CJt$sz&I9 zX$PQL&#>EqZ>Krk6(jHcokswrZMt3LS2~z_L13G4i^~ zX73B@1eKbbw9oV?dR>&oMO3Eiy>=jWhrl+rAx@OE}=uv;^y@l zO`&DpYI_JL>+b13;pXuJ%|()aAt^WU|HoC zj}NYjWr;yR%!HA)E~`583`yd7Lmi_b{-Ydk!4YX9s0n{;E}Sl|f~n~a*VXarN|95y zDQDn0&sH5<`0V+0KaO+qQWkrLp5&B~^6<&{`v~KDXb%E8aIG}v9yVQTbXG|fN$+hV zIKcgGT&!D5Y$y-oY)R+hMXec{*Zg;X74`U%wkyQH6igjyo!@?gv~E_ET7im$8@P)3 z4_?)MURXl=CU1~RRI?_0)y|=ialw7xSP~0C!RCw?vs6HQBDZmU6$mgwL0Eosir?H< zSar=%G@iF$bSxN%Po5$SEq(5X^lT*kCI#N|Z`#6ajj6-6lG-9_p|Z-BqheF#;%+^#R|%TKY#7`a_7GC|~9`1SP;S;^7C<|R3>@1Hw6I^c3M(Mg{Siu^j5s(T{1 zgg`L!Ien#?(=!?)K{GO%vaHZH2<(3DxhUNAAIQXe=Ig&q1^**|E7+Yq_p0KV|@^pVGJghD~I5AaR$$#25b? zl&J5B%ER%~Rpvx4J4(Jf%QTt;a@fkTyOY} znD4NSC_!Go$*zI#sm2=Q{o>xeACmVzKw(*O}(_M2}6eKfV5`!{IWdfoX}=CZToCDCW4Uq zp%}nQB`b0RkampTGL-JHv1GW?tSX(Q=GS33v!dnPsFN1~*7 z9B}cr3WYR#2(K?%*lymd2mJ==!0Qq;&D}ERkzelA)KDtVZQ#oNQUVsovb}xqMV^M; z&{+OPjYN_xUQn|*f1cv&m8@dA=_MA8%-7j!)bB3?)iiv~mJ0VBhJE#}hOujomjOA? z+yi-vsD2z>FZ^{3kQ_9T#9@m5szFrXY%s~T+)p|7^#&1NpY+<5whX7!SgNM~k4tDG z$l4S*cH5_4telJ=&y|n8aDuTf*jI(2LXWvAtQDH}p8c6(l7(9BfdgCHk7oqAdFY77 z+%n?ya8Z*|istqa6}VjVmYhkgSOOfzvi2G?Gc$~r<#>W1tojIaBZA3o;EAeAMB=Y< z-LgOq)x=43hH7e?N3S0{+cXB{aWwb!+>jC9uZ`&XFW(t9O#Dw)BF+A82w>}k$w}zT z-spM&#peJLEwh~U=Du$Ql+E3AV=fC{H1o`Rn1pMjs1Ev2d$c!)s&cudnZ&{S^2CpE zu{fV?yg0OXB#0)QLj8eTdVwRsJ|D|ESqd@;UkN&L7SOx{v&c3?o1$wd5!ckk$dC&l zNcE8FCA#nBm^6-Pb3I z{EwO1r1`^Nld4aXt}&nprc-9G_`bI!S%8#lfEf>TGevkTcw|DlK$x>MUfiZbJ^JqD zu^%W~z!yqRW(kLo2=E+*sMo$#?f7l%*3L+fKhhW%W+tF({$^TJl7$a%U9in>?1BeS zQ==&GG5Z{UbOwj=G*s%3R;hB}x9-p0`7KB6`1{WC1zX@|OpxxZZ*%{1D541L)!R<~ zMZII419j`!w`}Y6Gn-#uWYC(m$AmqHld+JuWefgD70P15xZ`=*Oo9R2r1C-{3W8sL z3;6u0@|q>UTi3t};u%*7V5`|(*dy5yHg-CF2>q_wWNuvcRx)V-<0qovW3R}yn@H~2 z+pn6zoJq`W5x2^Kz(NxQ?a!sdtj=pf4A|Pc*Xnd(n-V5 zU6<1GKg&K<1=dL6<4l)_Ej~qwEKbU=U5*lfp5c&dMpoLgM?}42dnMl9KV6h_JJcoj ztyej6KbSObuunEFF$k%fbDAXKOE2w82A??^>C&TFUCL4O>Pxm}-!QbKXveRZD+ffj zKc{p#C}t?DfKxFh_O9$E#{-A!524Wi2pYB;P^YwmS0VasU$|imA!anvjQf2z9JZof zd-IR|WUsv{&i6XBSar1r&bVtQskzsjE}Nl5=*&*rGVF5hXIu)c0CE zw_weK@R4eMJ6Wx>r=7T*Etz^Qi@~wNuGDa&aR-IwD@wdo_Xn7Cp{KoZtTmX%w^kQ~ zGns+%%PUN?I@!N>HNVk3knONMnc~HNuc%HiG`>MKUR(Ev9#=RBd_Pt|tGt7umoLKP z3Kh-PW==eX8^#Ixcu&Y3btog_Qxd3tv;$s5bT)gmRLZ=eO~ z-j)LY%*exD1rotGng1PJp5>&bUQ5(>;zpYTU&_KZ^~R+k!NcL!USe>NMdEgpJw z>!A+1`AmM@`jnFeSP7f_I08o1szBWqUZNJ$b)sNA+HW3QX07ztvB(vbHlf<06fEiB zBT=l$)AZ?0*G#m0F0gEnGH~0EYbNeOwBOa<;2e&Nsnp;{M6|XS`cQ64xxcTX*^dSw ztQa;iRyefdaget)(db, (DBT *)&key, (DBT *)&retval, 0); + dbtkey.data = key.dptr; + dbtkey.size = key.dsize; + status = (db->get)(db, &dbtkey, &dbtretdata, 0); if (status) { - retval.dptr = NULL; - retval.dsize = 0; + dbtretdata.data = NULL; + dbtretdata.size = 0; } - return (retval); + retdata.dptr = dbtretdata.data; + retdata.dsize = dbtretdata.size; + return (retdata); } /* @@ -126,11 +131,14 @@ dbm_firstkey(db) DBM *db; { int status; - datum retdata, retkey; + datum retkey; + DBT dbtretkey, dbtretdata; - status = (db->seq)(db, (DBT *)&retkey, (DBT *)&retdata, R_FIRST); + status = (db->seq)(db, &dbtretkey, &dbtretdata, R_FIRST); if (status) - retkey.dptr = NULL; + dbtretkey.data = NULL; + retkey.dptr = dbtretkey.data; + retkey.dsize = dbtretkey.size; return (retkey); } @@ -144,11 +152,14 @@ dbm_nextkey(db) DBM *db; { int status; - datum retdata, retkey; + datum retkey; + DBT dbtretkey, dbtretdata; - status = (db->seq)(db, (DBT *)&retkey, (DBT *)&retdata, R_NEXT); + status = (db->seq)(db, &dbtretkey, &dbtretdata, R_NEXT); if (status) - retkey.dptr = NULL; + dbtretkey.data = NULL; + retkey.dptr = dbtretkey.data; + retkey.dsize = dbtretkey.size; return (retkey); } /* @@ -162,8 +173,11 @@ dbm_delete(db, key) datum key; { int status; + DBT dbtkey; - status = (db->del)(db, (DBT *)&key, 0); + dbtkey.data = key.dptr; + dbtkey.size = key.dsize; + status = (db->del)(db, &dbtkey, 0); if (status) return (-1); else @@ -177,12 +191,18 @@ dbm_delete(db, key) * 1 if DBM_INSERT and entry exists */ extern int -dbm_store(db, key, content, flags) +dbm_store(db, key, data, flags) DBM *db; - datum key, content; + datum key, data; int flags; { - return ((db->put)(db, (DBT *)&key, (DBT *)&content, + DBT dbtkey, dbtdata; + + dbtkey.data = key.dptr; + dbtkey.size = key.dsize; + dbtdata.data = data.dptr; + dbtdata.size = data.dsize; + return ((db->put)(db, &dbtkey, &dbtdata, (flags == DBM_INSERT) ? R_NOOVERWRITE : 0)); } diff --git a/doc/README.iax b/doc/README.iax index 10f6a80897..1a35d6b156 100755 --- a/doc/README.iax +++ b/doc/README.iax @@ -133,10 +133,12 @@ The first line of the "general" section is always: Following the first line are a number of other possibilities: -> port = +> bindport = -This sets the port that IAX will bind to. The default IAX port number is -5036. It is recommended that this value not be altered in general. +This sets the port that IAX will bind to. The default IAX version 1 +port number is 5036. For IAX version 2, that is now the default in +Asterisk, the default port is 4569. +It is recommended that this value not be altered in general. > bindaddr = @@ -170,12 +172,15 @@ disallow the LPC10 codec just because it doesn't sound very good. These parameters control the operation of the jitter buffer. The jitterbuffer should always be enabled unless you expect all your -connections to be over a LAN. The drop count is the maximum number of -voice packets to allow to drop (out of 100). Useful values are 3-10. The -maxjitterbuffer is the maximum amount of jitter buffer to permit to be -used. The "maxexcessbuffer" is the maximum amount of excess jitter buffer -that is permitted before the jitter buffer is slowly shrunk to eliminate -latency. +connections to be over a LAN. +* drop count is the maximum number of voice packets to allow to drop + (out of 100). Useful values are 3-10. +* maxjitterbuffer is the maximum amount of jitter buffer to permit to be + used. +* maxexcessbuffer is the maximum amount of excess jitter buffer + that is permitted before the jitter buffer is slowly shrunk to eliminate + latency. +* minexcessbuffer is the minimum amout of excess jitter buffer > accountcode = > amaflags = [default|omit|billing|documentation] @@ -208,13 +213,20 @@ The name is a required field, and is the remote peer name that we wish to identify ourselves as. A secret may be provided as well. The secret is generally a shared password between the local server and the remote server. However, if the secret is in square brackets ([]'s) then it is -interpreted as the name of a key to use. In that case, the local Asterisk +interpreted as the name of a RSA key to use. In that case, the local Asterisk server must have the *private* key (/var/lib/asterisk/keys/.key) and the remote server will have to have the corresponding public key. The "host" is a required field and is the hostname or IP address of the remote Asterisk server. The port specification is optional and is by -default 5036 if not specified. +default 4569 for iax2 if not specified. + +> notransfer = yes | no + +If an IAX phone calls another IAX phone by using a Asterisk server, +Asterisk will transfer the call to go peer to peer. If you do not +want this, turn on notransfer with a "yes". This is also settable +for peers and users. ------------- @@ -232,7 +244,7 @@ should be an alphanumeric string. > type = [user|peer|friend] This line tells Asterisk how to interpret this entity. Users are things -that connect to us, while peers are people we connect to, and a friend is +that connect to us, while peers are phones we connect to, and a friend is shorthand for creating a user and a peer with identical information ---------------- @@ -262,8 +274,8 @@ the final result being the decision. For example: would deny anyone in 192.168.0.0 with a netmask of 24 bits (class C), whereas: -> deny = 192.168.0.0/255.255.255.0 -> permit = 0.0.0.0/0.0.0.0 +> deny = 192.168.0.0/24 +> permit = 0.0.0.0/0 would not deny anyone since the final rule would permit anyone, thus overriding the denial. @@ -281,8 +293,8 @@ perspective of your server. You may select which authentication methods are permitted to be used by the user to authenticate to us. Multiple methods may be specified, -separated by commas. If md5 or plaintext authentication is selected, a -secret must be provided. If RSA authentication is specified, then one or +separated by commas. If md5 or plaintext authentication is selected, a +secret must be provided. If RSA authentication is specified, then one or more key names must be specified with "inkeys" If no secret is specified and no authentication method is specified, then @@ -328,3 +340,30 @@ or has timed out. Specifies the context name to be passed to the peer for it to use when routing the call through its dial plan. This entry will be used only if a context is not included in the IAX2 channel name passed to the Dial command. + +> qualify = [yes | no | ] + +Qualify turns on checking of availability of the remote peer. If the +peer becomes unavailable, no calls are placed to the peer until +it is reachable again. This is also helpful in certain NAT situations. + +> jitterbuffer = [yes | no] + +Turns on or off the jitterbuffer for this peer + +> mailbox = [@mailboxcontext] + +Specifies a mailbox to check for voicemail notification. + +> permit = / +> deny = / + +Permit and deny rules may be applied to users, allowing them to connect +from certain IP addresses and not others. The permit and deny rules are +interpreted in sequence and all are evaluated on a given IP address, with +the final result being the decision. See the user section above +for examples. + +---------------------------------------------------------------------- +For more examples of a configuration, please see the iax.conf.sample in +your the /configs directory of you source code distribution diff --git a/doc/README.variables b/doc/README.variables index dfb6de3d10..b703b54ae4 100755 --- a/doc/README.variables +++ b/doc/README.variables @@ -79,8 +79,8 @@ The dial() application sets the following variables: ${DIALEDPEERNAME} Dialed peer name ${DIALEDPEERNUMBER} Dialed peer number -${DIALEDTIME} Time for the call (seconds) -${ANSWEREDTIME} Time from dial to answer (seconds) +${DIALEDTIME} Total time for the call in seconds (Network time). +${ANSWEREDTIME} Time from answer to end of call in seconds (Billable time). ${DIALSTATUS} Status of the call, one of: CHANUNAVAIL | CONGESTION | BUSY | NOANSWER | ANSWER | CANCEL diff --git a/doc/cdr.txt b/doc/cdr.txt index 580619737e..dac7df30a4 100755 --- a/doc/cdr.txt +++ b/doc/cdr.txt @@ -88,7 +88,7 @@ MSSQL: Asterisk can currently store CDRs into an MSSQL database in [lastdata] [varchar] (80) NOT NULL , [duration] [int] NOT NULL , [billsec] [int] NOT NULL , - [disposition] [varchar] (45) NOT NULL , + [disposition] [int] NOT NULL , [amaflags] [int] NOT NULL , [accountcode] [varchar] (20) NOT NULL , [uniqueid] [varchar] (32) NOT NULL , diff --git a/dsp.c b/dsp.c index e637aa65bd..a7566f0da6 100755 --- a/dsp.c +++ b/dsp.c @@ -1124,7 +1124,7 @@ static int __ast_dsp_call_progress(struct ast_dsp *dsp, short *s, int len) if (pass > dsp->gsamp_size - dsp->gsamps) pass = dsp->gsamp_size - dsp->gsamps; for (x=0;xfreqcount;y++) + for (y=0;y<=dsp->freqcount;y++) goertzel_sample(&dsp->freqs[y], s[x]); dsp->genergy += s[x] * s[x]; } @@ -1136,8 +1136,9 @@ static int __ast_dsp_call_progress(struct ast_dsp *dsp, short *s, int len) for (y=0;y<7;y++) hz[y] = goertzel_result(&dsp->freqs[y]); #if 0 - printf("Got whole dsp state: 350: %e, 440: %e, 480: %e, 620: %e, 950: %e, 1400: %e, 1800: %e, Energy: %e\n", - hz_350, hz_440, hz_480, hz_620, hz_950, hz_1400, hz_1800, dsp->genergy); + printf("\n350: 425: 440: 480: 620: 950: 1400: 1800: Energy: \n"); + printf("%.2e %.2e %.2e %.2e %.2e %.2e %.2e %.2e %.2e\n", + hz[HZ_350], hz[HZ_425], hz[HZ_440], hz[HZ_480], hz[HZ_620], hz[HZ_950], hz[HZ_1400], hz[HZ_1800], dsp->genergy); #endif switch(dsp->progmode) { case PROG_MODE_NA: @@ -1308,10 +1309,10 @@ int ast_dsp_busydetect(struct ast_dsp *dsp) } #endif if (avgtone > dsp->historicnoise[x]) { - if (avgtone - (avgtone / BUSY_PERCENT) <= dsp->historicsilence[x]) + if (avgtone - (avgtone / BUSY_PERCENT) <= dsp->historicnoise[x]) hittone++; } else { - if (avgtone + (avgtone / BUSY_PERCENT) >= dsp->historicsilence[x]) + if (avgtone + (avgtone / BUSY_PERCENT) >= dsp->historicnoise[x]) hittone++; } } @@ -1465,7 +1466,7 @@ struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp, shortdata[x] = AST_ALAW(odata[x]); break; default: - ast_log(LOG_WARNING, "Inband DTMF is not supported on codec %s. Use RFC2833\n", ast_codec2str(af->subclass)); + ast_log(LOG_WARNING, "Inband DTMF is not supported on codec %s. Use RFC2833\n", ast_getformatname(af->subclass)); return af; } silence = __ast_dsp_silence(dsp, shortdata, len, NULL); diff --git a/formats/format_wav_gsm.c b/formats/format_wav_gsm.c index 9fcc3c9ceb..5b4d31f6a5 100755 --- a/formats/format_wav_gsm.c +++ b/formats/format_wav_gsm.c @@ -219,7 +219,7 @@ static int update_header(int fd) end = lseek(fd, 0, SEEK_END); /* in a gsm WAV, data starts 60 bytes in */ bytes = end - 60; - datalen = htoll(bytes); + datalen = htoll((bytes + 1) & ~0x1); filelen = htoll(52 + ((bytes + 1) & ~0x1)); if (cur < 0) { ast_log(LOG_WARNING, "Unable to find our position\n"); diff --git a/frame.c b/frame.c index 72197d5c15..019ed8fdff 100755 --- a/frame.c +++ b/frame.c @@ -3,9 +3,9 @@ * * Frame manipulation routines * - * Copyright (C) 1999, Mark Spencer + * Copyright (C) 1999-2004, Mark Spencer * - * Mark Spencer + * Mark Spencer * * This program is free software, distributed under the terms of * the GNU General Public License @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,13 @@ AST_MUTEX_DEFINE_STATIC(framelock); #define SMOOTHER_SIZE 8000 +struct ast_format_list { + int visible; /* Can we see this entry */ + int bits; /* bitmask value */ + char *name; /* short name */ + char *desc; /* Description */ +}; + struct ast_smoother { int size; int format; @@ -391,141 +399,137 @@ int ast_fr_fdhangup(int fd) return ast_fr_fdwrite(fd, &hangup); } -char* ast_getformatname(int format) -{ - if (format == AST_FORMAT_G723_1) - return "G723"; - else if (format == AST_FORMAT_GSM) - return "GSM"; - else if (format == AST_FORMAT_ULAW) - return "ULAW"; - else if (format == AST_FORMAT_ALAW) - return "ALAW"; - else if (format == AST_FORMAT_G726) - return "G726"; - else if (format == AST_FORMAT_SLINEAR) - return "SLINR"; - else if (format == AST_FORMAT_LPC10) - return "LPC10"; - else if (format == AST_FORMAT_ADPCM) - return "ADPCM"; - else if (format == AST_FORMAT_G729A) - return "G729A"; - else if (format == AST_FORMAT_SPEEX) - return "SPEEX"; - else if (format == AST_FORMAT_ILBC) - return "ILBC"; - else if (format == AST_FORMAT_JPEG) - return "JPEG"; - else if (format == AST_FORMAT_PNG) - return "PNG"; - else if (format == AST_FORMAT_H261) - return "H261"; - else if (format == AST_FORMAT_H263) - return "H263"; - return "UNKN"; +static struct ast_format_list AST_FORMAT_LIST[] = { + { 1, AST_FORMAT_G723_1 , "g723" , "G.723.1"}, + { 1, AST_FORMAT_GSM, "gsm" , "GSM"}, + { 1, AST_FORMAT_ULAW, "ulaw", "G.711 u-law" }, + { 1, AST_FORMAT_ALAW, "alaw", "G.711 A-law" }, + { 1, AST_FORMAT_G726, "g726", "G.726" }, + { 1, AST_FORMAT_ADPCM, "adpcm" , "ADPCM"}, + { 1, AST_FORMAT_SLINEAR, "slin", "16 bit Signed Linear PCM"}, + { 1, AST_FORMAT_LPC10, "lpc10", "LPC10" }, + { 1, AST_FORMAT_G729A, "g729", "G.729A" }, + { 1, AST_FORMAT_SPEEX, "speex", "SpeeX" }, + { 1, AST_FORMAT_ILBC, "ilbc", "iLBC"}, + { 0, 0, "nothing", "undefined" }, + { 0, 0, "nothing", "undefined" }, + { 0, 0, "nothing", "undefined" }, + { 0, 0, "nothing", "undefined" }, + { 0, AST_FORMAT_MAX_AUDIO, "maxaudio", "Maximum audio format" }, + { 1, AST_FORMAT_JPEG, "jpeg", "JPEG image"}, + { 1, AST_FORMAT_PNG, "png", "PNG image"}, + { 1, AST_FORMAT_H261, "h261", "H.261 Video" }, + { 1, AST_FORMAT_H263, "h263", "H.263 Video" }, + { 0, 0, "nothing", "undefined" }, + { 0, 0, "nothing", "undefined" }, + { 0, 0, "nothing", "undefined" }, + { 0, 0, "nothing", "undefined" }, + { 0, AST_FORMAT_MAX_VIDEO, "maxvideo", "Maximum video format" }, +}; + +struct ast_format_list *ast_get_format_list_index(int index) { + return &AST_FORMAT_LIST[index]; } -char* ast_getformatname_multiple(char *buf, unsigned n, int format) { - unsigned u=1; - unsigned len; - char *b = buf; - char *start = buf; - if (!n) return buf; - snprintf(b,n,"0x%x(",format); - len = strlen(b); - b += len; - n -= len; - start = b; - while (u) { - if (u&format) { - snprintf(b,n,"%s|",ast_getformatname(u)); - len = strlen(b); - b += len; - n -= len; +struct ast_format_list *ast_get_format_list(size_t *size) { + *size = (sizeof(AST_FORMAT_LIST) / sizeof(struct ast_format_list)); + return AST_FORMAT_LIST; +} + +char* ast_getformatname(int format) +{ + int x = 0; + char *ret = "unknown"; + for (x = 0 ; x < sizeof(AST_FORMAT_LIST) / sizeof(struct ast_format_list) ; x++) { + if(AST_FORMAT_LIST[x].visible && AST_FORMAT_LIST[x].bits == format) { + ret = AST_FORMAT_LIST[x].name; + break; } - u *= 2; } - if (start==b) - snprintf(start,n,"EMPTY)"); - else if (n>1) - b[-1]=')'; + return ret; +} + +char *ast_getformatname_multiple(char *buf, size_t size, int format) { + + int x = 0; + unsigned len; + char *end = buf; + char *start = buf; + if (!size) return buf; + snprintf(end, size, "0x%x (", format); + len = strlen(end); + end += len; + size -= len; + start = end; + for (x = 0 ; x < sizeof(AST_FORMAT_LIST) / sizeof(struct ast_format_list) ; x++) { + if (AST_FORMAT_LIST[x].visible && (AST_FORMAT_LIST[x].bits & format)) { + snprintf(end, size,"%s|",AST_FORMAT_LIST[x].name); + len = strlen(end); + end += len; + size -= len; + } + } + if (start == end) + snprintf(start, size, "nothing)"); + else if (size > 1) + *(end -1) = ')'; return buf; } +static struct ast_codec_alias_table { + char *alias; + char *realname; + +} ast_codec_alias_table[] = { + {"slinear","slin"}, + {"g723.1","g723"}, +}; + +static char *ast_expand_codec_alias(char *in) { + int x = 0; + + for (x = 0; x < sizeof(ast_codec_alias_table) / sizeof(struct ast_codec_alias_table) ; x++) { + if(!strcmp(in,ast_codec_alias_table[x].alias)) + return ast_codec_alias_table[x].realname; + } + return in; +} + int ast_getformatbyname(char *name) { - if (!strcasecmp(name, "g723.1")) - return AST_FORMAT_G723_1; - else if (!strcasecmp(name, "gsm")) - return AST_FORMAT_GSM; - else if (!strcasecmp(name, "ulaw")) - return AST_FORMAT_ULAW; - else if (!strcasecmp(name, "alaw")) - return AST_FORMAT_ALAW; - else if (!strcasecmp(name, "g726")) - return AST_FORMAT_G726; - else if (!strcasecmp(name, "slinear")) - return AST_FORMAT_SLINEAR; - else if (!strcasecmp(name, "lpc10")) - return AST_FORMAT_LPC10; - else if (!strcasecmp(name, "adpcm")) - return AST_FORMAT_ADPCM; - else if (!strcasecmp(name, "g729")) - return AST_FORMAT_G729A; - else if (!strcasecmp(name, "speex")) - return AST_FORMAT_SPEEX; - else if (!strcasecmp(name, "ilbc")) - return AST_FORMAT_ILBC; - else if (!strcasecmp(name, "h261")) - return AST_FORMAT_H261; - else if (!strcasecmp(name, "h263")) - return AST_FORMAT_H263; - else if (!strcasecmp(name, "all")) - return 0x7FFFFFFF; - return 0; + int x = 0, all = 0, format = 0; + + all = strcasecmp(name, "all") ? 0 : 1; + for (x = 0 ; x < sizeof(AST_FORMAT_LIST) / sizeof(struct ast_format_list) ; x++) { + if(AST_FORMAT_LIST[x].visible && (all || + !strcasecmp(AST_FORMAT_LIST[x].name,name) || + !strcasecmp(AST_FORMAT_LIST[x].name,ast_expand_codec_alias(name)))) { + format |= AST_FORMAT_LIST[x].bits; + if(!all) + break; + } + } + + return format; } char *ast_codec2str(int codec) { - static char codecs[25][30] = { - /* Audio formats */ - "G.723.1", /* 0 */ - "GSM", /* 1 */ - "G.711 u-law", /* 2 */ - "G.711 A-law", /* 3 */ - "G.726", /* 4 */ - "ADPCM", /* 5 */ - "16 bit Signed Linear PCM", /* 6 */ - "LPC10", /* 7 */ - "G.729A audio", /* 8 */ - "SpeeX", /* 9 */ - "iLBC", /* 10 */ - "undefined", /* 11 */ - "undefined", /* 12 */ - "undefined", /* 13 */ - "undefined", /* 14 */ - "Maximum audio format", /* 15 */ - /* Image formats */ - "JPEG image", /* 16 */ - "PNG image", /* 17 */ - "H.261 Video", /* 18 */ - "H.263 Video", /* 19 */ - "undefined", /* 20 */ - "undefined", /* 21 */ - "undefined", /* 22 */ - "undefined", /* 23 */ - "Maximum video format", /* 24 */ - }; - if ((codec >= 0) && (codec <= 24)) - return codecs[codec]; - else - return "unknown"; + int x = 0; + char *ret = "unknown"; + for (x = 0 ; x < sizeof(AST_FORMAT_LIST) / sizeof(struct ast_format_list) ; x++) { + if(AST_FORMAT_LIST[x].visible && AST_FORMAT_LIST[x].bits == codec) { + ret = AST_FORMAT_LIST[x].desc; + break; + } + } + return ret; } static int show_codecs(int fd, int argc, char *argv[]) { int i, found=0; - + char hex[25]; + if ((argc < 2) || (argc > 3)) return RESULT_SHOWUSAGE; @@ -533,22 +537,30 @@ static int show_codecs(int fd, int argc, char *argv[]) ast_cli(fd, "Disclaimer: this command is for informational purposes only.\n" "\tIt does not indicate anything about your configuration.\n"); + ast_cli(fd, "%11s %9s %10s TYPE %5s %s\n","INT","BINARY","HEX","NAME","DESC"); + ast_cli(fd, "--------------------------------------------------------------------------------\n"); if ((argc == 2) || (!strcasecmp(argv[1],"audio"))) { found = 1; - for (i=0;i<11;i++) - ast_cli(fd, "%11u (1 << %2d) %s\n",1 << i,i,ast_codec2str(i)); + for (i=0;i<11;i++) { + snprintf(hex,25,"(0x%x)",1<subclass); } + break; case AST_FRAME_NULL: strcpy(ftype, "Null Frame"); strcpy(subclass, "N/A"); @@ -793,3 +809,186 @@ int init_framer(void) ast_cli_register(&cli_show_codec_n); return 0; } + +void ast_codec_pref_shift(struct ast_codec_pref *pref, char *buf, size_t size, int right) +{ + int x = 0, differential = 65, mem = 0; + char *from = NULL, *to = NULL; + + if(right) { + from = pref->order; + to = buf; + mem = size; + } else { + to = pref->order; + from = buf; + mem = 32; + } + + memset(to, 0, mem); + for (x = 0; x < 32 ; x++) { + if(!from[x]) + break; + to[x] = right ? (from[x] + differential) : (from[x] - differential); + } +} + +int ast_codec_pref_string(struct ast_codec_pref *pref, char *buf, size_t size) +{ + int x = 0, codec = 0; + size_t total_len = 0, slen = 0; + char *formatname = 0; + + memset(buf,0,size); + total_len = size; + buf[0] = '('; + total_len--; + for(x = 0; x < 32 ; x++) { + if(total_len <= 0) + break; + if(!(codec = ast_codec_pref_index(pref,x))) + break; + if((formatname = ast_getformatname(codec))) { + slen = strlen(formatname); + if(slen > total_len) + break; + strncat(buf,formatname,total_len); + total_len -= slen; + } + if(total_len && x < 31 && ast_codec_pref_index(pref , x + 1)) { + strncat(buf,"|",total_len); + total_len--; + } + } + if(total_len) { + strncat(buf,")",total_len); + total_len--; + } + + return size - total_len; +} + +int ast_codec_pref_index(struct ast_codec_pref *pref, int index) +{ + int slot = 0; + + + if((index >= 0) && (index < sizeof(pref->order))) { + slot = pref->order[index]; + } + + return slot ? AST_FORMAT_LIST[slot-1].bits : 0; +} + +/*--- ast_codec_pref_remove: Remove codec from pref list ---*/ +void ast_codec_pref_remove(struct ast_codec_pref *pref, int format) +{ + struct ast_codec_pref oldorder; + int x=0, y=0; + size_t size = 0; + int slot = 0; + + if(!pref->order[0]) + return; + + size = sizeof(AST_FORMAT_LIST) / sizeof(struct ast_format_list); + + memcpy(&oldorder,pref,sizeof(struct ast_codec_pref)); + memset(pref,0,sizeof(struct ast_codec_pref)); + + for (x = 0; x < size; x++) { + slot = oldorder.order[x]; + if(! slot) + break; + if(AST_FORMAT_LIST[slot-1].bits != format) + pref->order[y++] = slot; + } + +} + +/*--- ast_codec_pref_append: Append codec to list ---*/ +int ast_codec_pref_append(struct ast_codec_pref *pref, int format) +{ + size_t size = 0; + int x = 0, newindex = -1; + + ast_codec_pref_remove(pref, format); + size = sizeof(AST_FORMAT_LIST) / sizeof(struct ast_format_list); + + for (x = 0; x < size; x++) { + if(AST_FORMAT_LIST[x].bits == format) { + newindex = x + 1; + break; + } + } + + if(newindex) { + for (x = 0; x < size; x++) { + if(!pref->order[x]) { + pref->order[x] = newindex; + break; + } + } + } + + return x; +} + + +/*--- sip_codec_choose: Pick a codec ---*/ +int ast_codec_choose(struct ast_codec_pref *pref, int formats, int find_best) +{ + size_t size = 0; + int x = 0, ret = 0, slot = 0; + + size = sizeof(AST_FORMAT_LIST) / sizeof(struct ast_format_list); + for (x = 0; x < size; x++) { + slot = pref->order[x]; + + if(!slot) + break; + if ( formats & AST_FORMAT_LIST[slot-1].bits ) { + ret = AST_FORMAT_LIST[slot-1].bits; + break; + } + } + if(ret) + return ret; + + return find_best ? ast_best_codec(formats) : 0; +} + +void ast_parse_allow_disallow(struct ast_codec_pref *pref, int *mask, char *list, int allowing) +{ + int format_i = 0; + char *next_format = NULL, *last_format = NULL; + + last_format = ast_strdupa(list); + while(last_format) { + if((next_format = strchr(last_format, ','))) { + *next_format = '\0'; + next_format++; + } + if ((format_i = ast_getformatbyname(last_format)) > 0) { + if (mask) { + if (allowing) + (*mask) |= format_i; + else + (*mask) &= ~format_i; + } + /* can't consider 'all' a prefered codec*/ + if(pref && strcasecmp(last_format, "all")) { + if(allowing) + ast_codec_pref_append(pref, format_i); + else + ast_codec_pref_remove(pref, format_i); + } else if(!allowing) /* disallow all must clear your prefs or it makes no sense */ + memset(pref, 0, sizeof(struct ast_codec_pref)); + } else + ast_log(LOG_WARNING, "Cannot %s unknown format '%s'\n", allowing ? "allow" : "disallow", last_format); + + last_format = next_format; + } +} + + diff --git a/include/asterisk/cdr.h b/include/asterisk/cdr.h index 90d9433e3e..c274d8a815 100755 --- a/include/asterisk/cdr.h +++ b/include/asterisk/cdr.h @@ -254,6 +254,6 @@ extern char ast_default_accountcode[20]; #define ast_cdr_add_flag(cdr, flag) ((cdr)->flags |= (flag)) #define ast_cdr_del_flag(cdr, flag) ((cdr)->flags &= ~(flag)) -extern void ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr); +extern struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr); #endif /* _CDR_H */ diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 75627cda74..516e3a9a8a 100755 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -19,7 +19,11 @@ #include #include #include +#if defined(__APPLE__) +#include +#else #include +#endif #if defined(__cplusplus) || defined(c_plusplus) extern "C" { diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index c80ca72f5a..c97973226a 100755 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -58,6 +58,11 @@ extern "C" { #error Need to know endianess #endif /* __BYTE_ORDER */ +struct ast_codec_pref { + char order[32]; +}; + + //! Data structure associated with a single frame of data /* A frame of data read used to communicate between between channels and applications */ @@ -337,11 +342,10 @@ extern char* ast_getformatname(int format); * \param n size of buf (bytes) * \param format the format (combined IDs of codecs) * Prints a list of readable codec names corresponding to "format". - * ex: for format=AST_FORMAT_GSM|AST_FORMAT_SPEEX|AST_FORMAT_ILBC it will return "0x602(GSM|SPEEX|ILBC)" + * ex: for format=AST_FORMAT_GSM|AST_FORMAT_SPEEX|AST_FORMAT_ILBC it will return "0x602 (GSM|SPEEX|ILBC)" * \return The return value is buf. */ -extern char* ast_getformatname_multiple(char *buf, unsigned n, int format); - +extern char* ast_getformatname_multiple(char *buf, size_t size, int format); /*! * \param name string of format @@ -364,6 +368,8 @@ extern int ast_best_codec(int fmts); struct ast_smoother; +extern struct ast_format_list *ast_get_format_list_index(int index); +extern struct ast_format_list *ast_get_format_list(size_t *size); extern struct ast_smoother *ast_smoother_new(int bytes); extern void ast_smoother_set_flags(struct ast_smoother *smoother, int flags); extern int ast_smoother_get_flags(struct ast_smoother *smoother); @@ -374,6 +380,32 @@ extern struct ast_frame *ast_smoother_read(struct ast_smoother *s); extern void ast_frame_dump(char *name, struct ast_frame *f, char *prefix); +/* Initialize a codec preference to "no preference" */ +extern void ast_codec_pref_init(struct ast_codec_pref *pref); + +/* Codec located at a particular place in the preference index */ +extern int ast_codec_pref_index(struct ast_codec_pref *pref, int index); + +/* Remove a codec from a preference list */ +extern void ast_codec_pref_remove(struct ast_codec_pref *pref, int format); + +/* Append a codec to a preference list, removing it first if it was already there */ +extern int ast_codec_pref_append(struct ast_codec_pref *pref, int format); + +/* Select the best format according to preference list from supplied options. + If "find_best" is non-zero then if nothing is found, the "Best" format of + the format list is selected, otherwise 0 is returned. */ +extern int ast_codec_choose(struct ast_codec_pref *pref, int formats, int find_best); + +/* Parse an "allow" or "deny" line and update the mask and pref if provided */ +extern void ast_parse_allow_disallow(struct ast_codec_pref *pref, int *mask, char *list, int allowing); + +/* Dump codec preference list into a string */ +extern int ast_codec_pref_string(struct ast_codec_pref *pref, char *buf, size_t size); + +/* Shift a codec preference list up or down 65 bytes so that it becomes an ASCII string */ +extern void ast_codec_pref_shift(struct ast_codec_pref *pref, char *buf, size_t size, int right); + #if defined(__cplusplus) || defined(c_plusplus) } #endif diff --git a/loader.c b/loader.c index 2a539f30c5..0720d0670c 100755 --- a/loader.c +++ b/loader.c @@ -67,7 +67,7 @@ static int printdigest(unsigned char *d) strcat(buf, buf2); } strcat(buf, "\n"); - ast_log(LOG_DEBUG, buf); + ast_log(LOG_DEBUG, "%s", buf); return 0; } @@ -103,6 +103,7 @@ AST_MUTEX_DEFINE_STATIC(modlock); AST_MUTEX_DEFINE_STATIC(reloadlock); static struct module *module_list=NULL; +static int modlistver = 0; int ast_unload_resource(char *resource_name, int force) { @@ -142,6 +143,7 @@ int ast_unload_resource(char *resource_name, int force) ml = m; m = m->next; } + modlistver = rand(); ast_mutex_unlock(&modlock); ast_update_use_count(); return res; @@ -150,6 +152,8 @@ int ast_unload_resource(char *resource_name, int force) void ast_module_reload(const char *name) { struct module *m; + int oldversion; + int (*reload)(void); /* We'll do the logger and manager the favor of calling its reload here first */ @@ -168,14 +172,20 @@ void ast_module_reload(const char *name) time(&ast_lastreloadtime); ast_mutex_lock(&modlock); + oldversion = modlistver; m = module_list; while(m) { if (!name || !strcasecmp(name, m->resource)) { - if (m->reload) { + reload = m->reload; + ast_mutex_unlock(&modlock); + if (reload) { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Reloading module '%s' (%s)\n", m->resource, m->description()); - m->reload(); + reload(); } + ast_mutex_lock(&modlock); + if (oldversion != modlistver) + break; } m = m->next; } @@ -328,7 +338,8 @@ int ast_load_resource(char *resource_name) ; i->next = m; } - + + modlistver = rand(); ast_mutex_unlock(&modlock); if ((res = m->load_module())) { ast_log(LOG_WARNING, "%s: load_module failed, returning %d\n", m->resource, res); diff --git a/logger.c b/logger.c index 25bf5d3d0e..cafe4d70db 100755 --- a/logger.c +++ b/logger.c @@ -48,6 +48,13 @@ static int syslog_level_map[] = { #define MAX_MSG_QUEUE 200 +#if defined(__linux__) && defined (__NR_gettid) +#include +#define GETTID() syscall(__NR_gettid) +#else +#define GETTID() getpid() +#endif + static char dateformat[256] = "%b %e %T"; /* Original Asterisk Format */ AST_MUTEX_DEFINE_STATIC(msglist_lock); AST_MUTEX_DEFINE_STATIC(loglock); @@ -455,8 +462,8 @@ void close_logger(void) { struct msglist *m, *tmp; - m = list; ast_mutex_lock(&msglist_lock); + m = list; while(m) { if (m->msg) { free(m->msg); @@ -481,11 +488,11 @@ static void ast_log_vsyslog(int level, const char *file, int line, const char *f return; } if (level == __LOG_VERBOSE) { - snprintf(buf, sizeof(buf), "VERBOSE[%ld]: ", (long)pthread_self()); + snprintf(buf, sizeof(buf), "VERBOSE[%ld]: ", (long)GETTID()); level = __LOG_DEBUG; } else { snprintf(buf, sizeof(buf), "%s[%ld]: %s:%d in %s: ", - levels[level], (long)pthread_self(), file, line, function); + levels[level], (long)GETTID(), file, line, function); } vsnprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), fmt, args); syslog(syslog_level_map[level], "%s", buf); @@ -543,7 +550,7 @@ void ast_log(int level, const char *file, int line, const char *function, const snprintf(buf, sizeof(buf), "%s %s[%ld]: %s:%s %s: ", date, term_color(tmp1, levels[level], colors[level], 0, sizeof(tmp1)), - (long)pthread_self(), + (long)GETTID(), term_color(tmp2, file, COLOR_BRWHITE, 0, sizeof(tmp2)), term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)), term_color(tmp4, function, COLOR_BRWHITE, 0, sizeof(tmp4))); @@ -556,7 +563,7 @@ void ast_log(int level, const char *file, int line, const char *function, const } } else if ((chan->logmask & (1 << level)) && (chan->fileptr)) { snprintf(buf, sizeof(buf), "%s %s[%ld]: ", date, - levels[level], (long)pthread_self()); + levels[level], (long)GETTID()); fprintf(chan->fileptr, buf); va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); @@ -642,7 +649,7 @@ extern void ast_verbose(const char *fmt, ...) } /* else fprintf(stdout, stuff + opos); */ - ast_log(LOG_VERBOSE, stuff); + ast_log(LOG_VERBOSE, "%s", stuff); if (fmt[strlen(fmt)-1] != '\n') replacelast = 1; @@ -656,8 +663,8 @@ extern void ast_verbose(const char *fmt, ...) int ast_verbose_dmesg(void (*v)(const char *string, int opos, int replacelast, int complete)) { struct msglist *m; - m = list; ast_mutex_lock(&msglist_lock); + m = list; while(m) { /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */ v(m->msg, 0, 0, 1); diff --git a/pbx.c b/pbx.c index 16888bdbfd..09f173e1ea 100755 --- a/pbx.c +++ b/pbx.c @@ -481,7 +481,7 @@ int pbx_exec(struct ast_channel *c, /* Channel */ /* Go no deeper than this through includes (not counting loops) */ -#define AST_PBX_MAX_STACK 64 +#define AST_PBX_MAX_STACK 128 #define HELPER_EXISTS 0 #define HELPER_SPAWN 1 @@ -577,6 +577,7 @@ static void pbx_destroy(struct ast_pbx *p) match=1;\ pattern++;\ while(match && *data && *pattern && (*pattern != '/')) {\ + while (*data == '-' && (*(data+1) != '\0')) data++;\ switch(toupper(*pattern)) {\ case '[': \ {\ @@ -1258,7 +1259,7 @@ static int pbx_extension_helper(struct ast_channel *c, char *context, char *exte pbx_substitute_variables(passdata, sizeof(passdata), c, e); if (option_debug) ast_log(LOG_DEBUG, "Launching '%s'\n", app->name); - else if (option_verbose > 2) + if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_3 "Executing %s(\"%s\", \"%s\") %s\n", term_color(tmp, app->name, COLOR_BRCYAN, 0, sizeof(tmp)), term_color(tmp2, c->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)), @@ -2760,6 +2761,7 @@ static int handle_show_dialplan(int fd, int argc, char *argv[]) e = ast_walk_context_extensions(c, NULL); while (e) { struct ast_exten *p; + int prio; /* looking for extension? is this our extension? */ if (exten && @@ -2786,11 +2788,18 @@ static int handle_show_dialplan(int fd, int argc, char *argv[]) snprintf(buf, sizeof(buf), "'%s' =>", ast_get_extension_name(e)); - snprintf(buf2, sizeof(buf2), - "%d. %s(%s)", - ast_get_extension_priority(e), - ast_get_extension_app(e), - (char *)ast_get_extension_app_data(e)); + prio = ast_get_extension_priority(e); + if (prio == PRIORITY_HINT) { + snprintf(buf2, sizeof(buf2), + "hint: %s", + ast_get_extension_app(e)); + } else { + snprintf(buf2, sizeof(buf2), + "%d. %s(%s)", + prio, + ast_get_extension_app(e), + (char *)ast_get_extension_app_data(e)); + } ast_cli(fd, " %-17s %-45s [%s]\n", buf, buf2, ast_get_extension_registrar(e)); @@ -2800,11 +2809,18 @@ static int handle_show_dialplan(int fd, int argc, char *argv[]) while (p) { bzero((void *)buf2, sizeof(buf2)); - snprintf(buf2, sizeof(buf2), - "%d. %s(%s)", - ast_get_extension_priority(p), - ast_get_extension_app(p), - (char *)ast_get_extension_app_data(p)); + prio = ast_get_extension_priority(p); + if (prio == PRIORITY_HINT) { + snprintf(buf2, sizeof(buf2), + "hint: %s", + ast_get_extension_app(p)); + } else { + snprintf(buf2, sizeof(buf2), + "%d. %s(%s)", + prio, + ast_get_extension_app(p), + (char *)ast_get_extension_app_data(p)); + } ast_cli(fd," %-17s %-45s [%s]\n", "", buf2, @@ -4381,7 +4397,8 @@ static int pbx_builtin_answer(struct ast_channel *chan, void *data) static int pbx_builtin_setlanguage(struct ast_channel *chan, void *data) { /* Copy the language as specified */ - strncpy(chan->language, (char *)data, sizeof(chan->language)-1); + if (data) + strncpy(chan->language, (char *)data, sizeof(chan->language)-1); return 0; } @@ -4567,7 +4584,7 @@ static int pbx_builtin_background(struct ast_channel *chan, void *data) res = ast_waitstream(chan, AST_DIGIT_ANY); ast_stopstream(chan); } else { - ast_log(LOG_WARNING, "ast_streamfile failed on %s fro %s\n", chan->name, (char*)data); + ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char*)data); res = 0; } } diff --git a/res/res_agi.c b/res/res_agi.c index 04cf9eec5e..8f96c8ca29 100755 --- a/res/res_agi.c +++ b/res/res_agi.c @@ -11,12 +11,12 @@ * the GNU General Public License */ +#include #include #include #include #include #include -#include #include #include #include @@ -119,7 +119,7 @@ static int launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int struct pollfd pfds[1]; char *host; char *c; int port = AGI_PORT; - char *script; + char *script = ""; struct sockaddr_in sin; struct hostent *hp; struct ast_hostent ahp; @@ -136,7 +136,7 @@ static int launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int if ((c = strchr(host, ':'))) { *c = '\0'; c++; - port = atoi(c + 1); + port = atoi(c); } if (efd) { ast_log(LOG_WARNING, "AGI URI's don't support Enhanced AGI yet\n"); @@ -185,6 +185,9 @@ static int launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int return -1; } ast_log(LOG_DEBUG, "Wow, connected!\n"); + /* Send the script parameter */ + if (!ast_strlen_zero(script)) + fdprintf(s, "agi_network_script: %s\n", script); fds[0] = s; fds[1] = s; *opid = -1; @@ -432,8 +435,7 @@ static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, char fs = ast_openstream(chan, argv[2], chan->language); if(!fs){ fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset); - ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]); - return RESULT_FAILURE; + return RESULT_SUCCESS; } ast_seekstream(fs, 0, SEEK_END); max_length = ast_tellstream(fs); diff --git a/res/res_config_odbc.c b/res/res_config_odbc.c index 3f8490530c..82e9e681b8 100755 --- a/res/res_config_odbc.c +++ b/res/res_config_odbc.c @@ -44,7 +44,7 @@ static struct ast_config *config_odbc (char *file, struct ast_config *new_config odbc_obj *obj; SQLINTEGER err=0, commented=0, cat_metric=0, var_metric=0, last_cat_metric=0; SQLBIGINT id; - char sql[255] = "", filename[128], category[128], var_name[128], var_val[128]; + char sql[255]="", filename[128]="", category[128]="", var_name[128]="", var_val[512]=""; SQLSMALLINT rowcount=0; SQLHSTMT stmt; char last[80] = ""; diff --git a/res/res_crypto.c b/res/res_crypto.c index 912d7c8c09..16c879de1c 100755 --- a/res/res_crypto.c +++ b/res/res_crypto.c @@ -174,6 +174,7 @@ static struct ast_key *try_load_key (char *dir, char *fname, int ifd, int ofd, i while(!feof(f)) { /* Calculate a "whatever" quality md5sum of the key */ char buf[256]; + memset(buf, 0, 256); fgets(buf, sizeof(buf), f); if (!feof(f)) { MD5Update(&md5, buf, strlen(buf)); @@ -441,7 +442,7 @@ int ast_check_signature(struct ast_key *key, char *msg, char *sig) static void crypto_load(int ifd, int ofd) { struct ast_key *key, *nkey, *last; - DIR *dir; + DIR *dir = NULL; struct dirent *ent; int note = 0; /* Mark all keys for deletion */ diff --git a/res/res_features.c b/res/res_features.c index b2fd1a866d..5cd2da58cd 100755 --- a/res/res_features.c +++ b/res/res_features.c @@ -152,6 +152,7 @@ int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeou struct ast_context *con; pu = malloc(sizeof(struct parkeduser)); if (pu) { + memset(pu,0,sizeof(struct parkeduser)); ast_mutex_lock(&parking_lock); for (x=parking_start;x<=parking_stop;x++) { cur = parkinglot; @@ -302,6 +303,12 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast int allowdisconnect_in,allowdisconnect_out,allowredirect_in,allowredirect_out; char *monitor_exec; + if (chan && peer) { + pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name); + pbx_builtin_setvar_helper(peer, "BRIDGEPEER", chan->name); + } else if (chan) + pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", NULL); + if (monitor_ok) { if (!monitor_app) { if (!(monitor_app = pbx_findapp("Monitor"))) @@ -480,6 +487,8 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast } /* XXX Maybe we should have another message here instead of invalid extension XXX */ } else if (ast_exists_extension(transferee, transferer_real_context, newext, 1, transferer->callerid)) { + pbx_builtin_setvar_helper(peer, "BLINDTRANSFER", chan->name); + pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", peer->name); ast_moh_stop(transferee); res=ast_autoservice_stop(transferee); if (!transferee->pbx) { diff --git a/res/res_indications.c b/res/res_indications.c index b289612d5d..e998be582a 100755 --- a/res/res_indications.c +++ b/res/res_indications.c @@ -53,6 +53,14 @@ static char help_show_indications[] = " Show either a condensed for of all country/indications, or the\n" " indications for the specified countries.\n"; +char *playtones_desc= +"PlayTone(arg): Plays a tone list. Execution will continue with the next step immediately,\n" +"while the tones continue to play.\n" +"Arg is either the tone name defined in the indications.conf configuration file, or a directly\n" +"specified list of frequencies and durations.\n" +"See indications.conf for a description of the specification of a tonelist.\n\n" +"Use the StopPlaytones application to stop the tones playing. \n"; + /* * Implementation of functions provided by this module */ @@ -367,11 +375,12 @@ int unload_module(void) ast_cli_unregister(&add_indication_cli); ast_cli_unregister(&remove_indication_cli); ast_cli_unregister(&show_indications_cli); - ast_unregister_application("Playlist"); - ast_unregister_application("StopPlaylist"); + ast_unregister_application("Playtones"); + ast_unregister_application("StopPlaytones"); return 0; } + int load_module(void) { if (ind_load_module()) return -1; @@ -379,7 +388,7 @@ int load_module(void) ast_cli_register(&add_indication_cli); ast_cli_register(&remove_indication_cli); ast_cli_register(&show_indications_cli); - ast_register_application("Playtones", handle_playtones, "Play a tone list","Play a tone list, either registered (through indications.conf) or a direct list of tones and durations."); + ast_register_application("Playtones", handle_playtones, "Play a tone list", playtones_desc); ast_register_application("StopPlaytones", handle_stopplaytones, "Stop playing a tone list","Stop playing a tone list"); return 0; diff --git a/res/res_monitor.c b/res/res_monitor.c index c36460c5a7..7f92a848d1 100755 --- a/res/res_monitor.c +++ b/res/res_monitor.c @@ -33,22 +33,24 @@ static char *monitor_descrip = "Monitor([file_format|[fname_base]|[options]]):\n "Used to start monitoring a channel. The channel's input and output\n" "voice packets are logged to files until the channel hangs up or\n" "monitoring is stopped by the StopMonitor application.\n" -" file_format -- optional, if not set, defaults to \"wav\"\n" -" fname_base -- if set, changes the filename used to the one specified.\n" -" options:\n" -" 'm' - when the recording ends mix the two leg files into one and\n" -" delete the two leg files. If MONITOR_EXEC is set, the\n" -" application refernced in it will be executed instead of\n" -" soxmix and the raw leg files will NOT be deleted automatically.\n" -" soxmix or MONITOR_EXEC is handed 3 arguments, the two leg files\n" -" and a target mixed file name which is the same as the leg file names\n" -" only without the in/out designator.\n" -" If MONITOR_EXEC_ARGS is set, the contents will be passed on as\n" -" additional arguements to MONITOR_EXEC\n" -" Both MONITOR_EXEC and the Mix flag can be set from the\n" -" administrator interface\n\n" +" file_format optional, if not set, defaults to \"wav\"\n" +" fname_base if set, changes the filename used to the one specified.\n" +" options:\n" +" m - when the recording ends mix the two leg files into one and\n" +" delete the two leg files. If the variable MONITOR_EXEC is set, the\n" +" application referenced in it will be executed instead of\n" +" soxmix and the raw leg files will NOT be deleted automatically.\n" +" soxmix or MONITOR_EXEC is handed 3 arguments, the two leg files\n" +" and a target mixed file name which is the same as the leg file names\n" +" only without the in/out designator.\n" +" If MONITOR_EXEC_ARGS is set, the contents will be passed on as\n" +" additional arguements to MONITOR_EXEC\n" +" Both MONITOR_EXEC and the Mix flag can be set from the\n" +" administrator interface\n" "\n" -" 'b' - Don't begin recording unless a call is bridged to another channel\n" +" b - Don't begin recording unless a call is bridged to another channel\n" +"\nReturns -1 if monitor files can't be opened or if the channel is already\n" +"monitored, otherwise 0.\n" ; static char *stopmonitor_synopsis = "Stop monitoring a channel"; @@ -58,10 +60,9 @@ static char *stopmonitor_descrip = "StopMonitor\n" static char *changemonitor_synopsis = "Change monitoring filename of a channel"; -static char *changemonitor_descrip = "ChangeMonitor\n" +static char *changemonitor_descrip = "ChangeMonitor(filename_base)\n" "Changes monitoring filename of a channel. Has no effect if the channel is not monitored\n" - "The option string may contain the following:\n" - " filename_base -- if set, changes the filename used to the one specified.\n"; + "The argument is the new filename base to use for monitoring this channel.\n"; /* Start monitoring a channel */ int ast_monitor_start( struct ast_channel *chan, const char *format_spec, @@ -90,6 +91,11 @@ int ast_monitor_start( struct ast_channel *chan, const char *format_spec, } monitor = malloc(sizeof(struct ast_channel_monitor)); + if (!monitor) { + if (need_lock) + ast_mutex_unlock(&chan->lock); + return -1; + } memset(monitor, 0, sizeof(struct ast_channel_monitor)); /* Determine file names */ @@ -98,9 +104,9 @@ int ast_monitor_start( struct ast_channel *chan, const char *format_spec, /* try creating the directory just in case it doesn't exist */ if (directory) { char *name = strdup(fname_base); - snprintf(tmp, sizeof(tmp), "mkdir -p %s",dirname(name)); + snprintf(tmp, sizeof(tmp), "mkdir -p \"%s\"",dirname(name)); free(name); - system(tmp); + ast_safe_system(tmp); } snprintf(monitor->read_filename, FILENAME_MAX, "%s/%s-in", directory ? "" : AST_MONITOR_DIR, fname_base); @@ -238,9 +244,9 @@ int ast_monitor_stop(struct ast_channel *chan, int need_lock) execute_args = ""; } - snprintf(tmp, sizeof(tmp), "%s %s/%s-in.%s %s/%s-out.%s %s/%s.%s %s &", execute, dir, name, format, dir, name, format, dir, name, format,execute_args); + snprintf(tmp, sizeof(tmp), "%s \"%s/%s-in.%s\" \"%s/%s-out.%s\" \"%s/%s.%s\" %s &", execute, dir, name, format, dir, name, format, dir, name, format,execute_args); if (delfiles) { - snprintf(tmp2,sizeof(tmp2), "( %s& rm -f %s/%s-* ) &",tmp, dir ,name); /* remove legs when done mixing */ + snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s\"/%s-* ) &",tmp, dir ,name); /* remove legs when done mixing */ strncpy(tmp, tmp2, sizeof(tmp) - 1); } ast_verbose("monitor executing %s\n",tmp); @@ -281,7 +287,7 @@ int ast_monitor_change_fname(struct ast_channel *chan, const char *fname_base, i char *name = strdup(fname_base); snprintf(tmp, sizeof(tmp), "mkdir -p %s",dirname(name)); free(name); - system(tmp); + ast_safe_system(tmp); } snprintf(chan->monitor->filename_base, FILENAME_MAX, "%s/%s", directory ? "" : AST_MONITOR_DIR, fname_base); @@ -391,6 +397,11 @@ static int start_monitor_action(struct mansession *s, struct message *m) if ((!fname) || (ast_strlen_zero(fname))) { // No filename base specified, default to channel name as per CLI fname = malloc (FILENAME_MAX); + if (!fname) { + astman_send_error(s, m, "Could not start monitoring channel"); + ast_mutex_unlock(&c->lock); + return 0; + } memset(fname, 0, FILENAME_MAX); strncpy(fname, c->name, FILENAME_MAX-1); // Channels have the format technology/channel_name - have to replace that / @@ -502,6 +513,10 @@ int unload_module(void) { ast_unregister_application("Monitor"); ast_unregister_application("StopMonitor"); + ast_unregister_application("ChangeMonitor"); + ast_manager_unregister("Monitor"); + ast_manager_unregister("StopMonitor"); + ast_manager_unregister("ChangeMonitor"); return 0; } diff --git a/res/res_musiconhold.c b/res/res_musiconhold.c index 2dc39a5c65..6cf30db4ea 100755 --- a/res/res_musiconhold.c +++ b/res/res_musiconhold.c @@ -224,8 +224,11 @@ static int spawn_mp3(struct mohclass *class) /* Stdout goes to pipe */ dup2(fds[1], STDOUT_FILENO); /* Close unused file descriptors */ - for (x=3;x<8192;x++) - close(x); + for (x=3;x<8192;x++) { + if (-1 != fcntl(x, F_GETFL)) { + close(x); + } + } /* Child */ chdir(class->dir); if(class->custom) { @@ -352,6 +355,7 @@ static int moh0_exec(struct ast_channel *chan, void *data) return -1; } while(!ast_safe_sleep(chan, 10000)); + ast_moh_stop(chan); return -1; } @@ -363,7 +367,7 @@ static int moh1_exec(struct ast_channel *chan, void *data) return -1; } if (ast_moh_start(chan, NULL)) { - ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name); + ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi((char *)data), chan->name); return -1; } res = ast_safe_sleep(chan, atoi(data) * 1000); diff --git a/res/res_odbc.c b/res/res_odbc.c index c8efb06201..90caa655d2 100755 --- a/res/res_odbc.c +++ b/res/res_odbc.c @@ -99,10 +99,12 @@ static int load_odbc_config(void) if (!strcmp(cat, "ENV")) { for (v = ast_variable_browse(config, cat); v; v = v->next) { env_var = malloc(strlen(v->name) + strlen(v->value) + 2); - sprintf(env_var, "%s=%s", v->name, v->value); - ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value); - putenv(env_var); - free(env_var); + if (env_var) { + sprintf(env_var, "%s=%s", v->name, v->value); + ast_log(LOG_NOTICE, "Adding ENV var: %s=%s\n", v->name, v->value); + putenv(env_var); + free(env_var); + } } cat = ast_category_browse(config, cat); @@ -145,39 +147,27 @@ static int load_odbc_config(void) int odbc_dump_fd(int fd, odbc_obj * obj) { - ast_cli(fd, "\n\nName: %s\nDSN: %s\nConnected: %s\n\n", obj->name, obj->dsn, obj->up ? "yes" : "no"); + ast_cli(fd, "Name: %s\nDSN: %s\nConnected: %s\n", obj->name, obj->dsn, obj->up ? "yes" : "no"); return 0; } -static int odbc_usage(int fd) +static int odbc_connect_usage(int fd) { - ast_cli(fd, "\n\nusage odbc .. \n\n"); + ast_cli(fd, "usage odbc connect \n"); return 0; } -static int odbc_command(int fd, int argc, char **argv) +static int odbc_disconnect_usage(int fd) +{ + ast_cli(fd, "usage odbc disconnect \n"); + return 0; +} + +static int odbc_show_command(int fd, int argc, char **argv) { odbc_obj *obj; int x = 0; - if (!argv[1]) - return odbc_usage(fd); - - ast_cli(fd, "\n\n"); - - if (!strcmp(argv[1], "connect") || !strcmp(argv[1], "disconnect")) { - if (!argv[2]) - return odbc_usage(fd); - - obj = odbc_read(ODBC_REGISTRY, argv[2]); - if (obj) { - if (!strcmp(argv[1], "connect")) - odbc_obj_connect(obj); - - if (!strcmp(argv[1], "disconnect")) - odbc_obj_disconnect(obj); - } - - } else if (!strcmp(argv[1], "show")) { + if (!strcmp(argv[1], "show")) { if (!argv[2] || (argv[2] && !strcmp(argv[2], "all"))) { for (x = 0; x < MAX_ODBC_HANDLES; x++) { if (!ODBC_REGISTRY[x].used) @@ -190,18 +180,64 @@ static int odbc_command(int fd, int argc, char **argv) if (obj) odbc_dump_fd(fd, obj); } - - } else { - return odbc_usage(fd); } - ast_cli(fd, "\n"); return 0; } -static struct ast_cli_entry odbc_command_struct = { - {"odbc", NULL}, odbc_command, - "Execute ODBC Command", "obdc .. ", NULL -}; +static int odbc_disconnect_command(int fd, int argc, char **argv){ + odbc_obj *obj; + int x = 0; + if (!strcmp(argv[1], "disconnect")) { + if (!argv[2]) + return odbc_disconnect_usage(fd); + + obj = odbc_read(ODBC_REGISTRY, argv[2]); + if (obj) { + odbc_obj_disconnect(obj); + } + } + return 0; +} + +static int odbc_connect_command(int fd, int argc, char **argv){ + odbc_obj *obj; + int x = 0; + if (!argv[1]) + return odbc_connect_usage(fd); + + if (!strcmp(argv[1], "connect") || !strcmp(argv[1], "disconnect")) { + if (!argv[2]) + return odbc_connect_usage(fd); + + obj = odbc_read(ODBC_REGISTRY, argv[2]); + if (obj) { + odbc_obj_connect(obj); + } + } + return 0; +} + +static char connect_usage[] = +"Usage: odbc connect \n" +" Connect to ODBC DSN\n"; + +static char disconnect_usage[] = +"Usage: odbc connect \n" +" Disconnect from ODBC DSN\n"; + +static char show_usage[] = +"Usage: odbc show {DSN}\n" +" Show ODBC {DSN}\n" +" Specifying DSN will show that DSN else, all DSNs are shown\n"; + +static struct ast_cli_entry odbc_connect_struct = + { { "odbc", "connect", NULL }, odbc_connect_command, "Connect to ODBC DSN", connect_usage }; + +static struct ast_cli_entry odbc_disconnect_struct = + { { "odbc", "disconnect", NULL }, odbc_disconnect_command, "Disconnect from ODBC DSN", disconnect_usage }; + +static struct ast_cli_entry odbc_show_struct = + { { "odbc", "show", NULL }, odbc_show_command, "Show ODBC DSN(s)", show_usage }; /* api calls */ @@ -222,6 +258,8 @@ odbc_obj *new_odbc_obj(char *name, char *dsn, char *username, char *password) static odbc_obj *new; new = malloc(sizeof(odbc_obj)); + if (!new) + return NULL; memset(new, 0, sizeof(odbc_obj)); new->env = SQL_NULL_HANDLE; @@ -362,7 +400,9 @@ int unload_module(void) { STANDARD_HANGUP_LOCALUSERS; odbc_destroy(); - ast_cli_unregister(&odbc_command_struct); + ast_cli_unregister(&odbc_disconnect_struct); + ast_cli_unregister(&odbc_connect_struct); + ast_cli_unregister(&odbc_show_struct); ast_log(LOG_NOTICE, "res_odbc unloaded.\n"); return 0; } @@ -371,7 +411,9 @@ int load_module(void) { odbc_init(); load_odbc_config(); - ast_cli_register(&odbc_command_struct); + ast_cli_register(&odbc_disconnect_struct); + ast_cli_register(&odbc_connect_struct); + ast_cli_register(&odbc_show_struct); ast_log(LOG_NOTICE, "res_odbc loaded.\n"); return 0; } diff --git a/rtp.c b/rtp.c index fff3deed0c..9b40b7980c 100755 --- a/rtp.c +++ b/rtp.c @@ -188,8 +188,13 @@ static struct ast_frame *send_dtmf(struct ast_rtp *rtp) return &null_frame; } ast_log(LOG_DEBUG, "Sending dtmf: %d (%c), at %s\n", rtp->resp, rtp->resp, ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->them.sin_addr)); - rtp->f.frametype = AST_FRAME_DTMF; - rtp->f.subclass = rtp->resp; + if (rtp->resp == 'X') { + rtp->f.frametype = AST_FRAME_CONTROL; + rtp->f.subclass = AST_CONTROL_FLASH; + } else { + rtp->f.frametype = AST_FRAME_DTMF; + rtp->f.subclass = rtp->resp; + } rtp->f.datalen = 0; rtp->f.samples = 0; rtp->f.mallocd = 0; @@ -218,6 +223,8 @@ static struct ast_frame *process_cisco_dtmf(struct ast_rtp *rtp, unsigned char * resp = '#'; } else if (event < 16) { resp = 'A' + (event - 12); + } else if (event < 17) { + resp = 'X'; } if (rtp->resp && (rtp->resp != resp)) { f = send_dtmf(rtp); @@ -252,6 +259,8 @@ static struct ast_frame *process_rfc2833(struct ast_rtp *rtp, unsigned char *dat resp = '#'; } else if (event < 16) { resp = 'A' + (event - 12); + } else if (event < 17) { + resp = 'X'; } if (rtp->resp && (rtp->resp != resp)) { f = send_dtmf(rtp); @@ -397,6 +406,7 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) struct sockaddr_in sin; int len; unsigned int seqno; + int version; int payloadtype; int hdrlen = 12; int mark; @@ -445,6 +455,12 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) /* Get fields */ seqno = ntohl(rtpheader[0]); + + /* Check RTP version */ + version = (seqno & 0xC0000000) >> 30; + if (version != 2) + return &null_frame; + payloadtype = (seqno & 0x7f0000) >> 16; mark = seqno & (1 << 23); ext = seqno & (1 << 28); @@ -469,17 +485,17 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) /* This is special in-band data that's not one of our codecs */ if (rtpPT.code == AST_RTP_DTMF) { /* It's special -- rfc2833 process it */ - if (rtp->lasteventseqn <= seqno) { + if (rtp->lasteventseqn <= seqno || rtp->resp == 0 || (rtp->lasteventseqn >= 65530 && seqno <= 6)) { f = process_rfc2833(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen); rtp->lasteventseqn = seqno; - } + } else f = NULL; if (f) return f; else return &null_frame; } else if (rtpPT.code == AST_RTP_CISCO_DTMF) { /* It's really special -- process it the Cisco way */ - if (rtp->lasteventseqn <= seqno) { + if (rtp->lasteventseqn <= seqno || rtp->resp == 0 || (rtp->lasteventseqn >= 65530 && seqno <= 6)) { f = process_cisco_dtmf(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen); rtp->lasteventseqn = seqno; - } + } else f = NULL; if (f) return f; else return &null_frame; } else if (rtpPT.code == AST_RTP_CN) { /* Comfort Noise */ @@ -978,7 +994,6 @@ int ast_rtp_senddigit(struct ast_rtp *rtp, char digit) unsigned int *rtpheader; int hdrlen = 12; int res; - int ms; int x; int payload; char data[256]; @@ -1010,10 +1025,6 @@ int ast_rtp_senddigit(struct ast_rtp *rtp, char digit) rtp->dtmfmute.tv_usec -= 1000000; rtp->dtmfmute.tv_sec += 1; } - - ms = calc_txstamp(rtp, NULL); - /* Default prediction */ - rtp->lastts = rtp->lastts + ms * 8; /* Get a pointer to the header */ rtpheader = (unsigned int *)data; @@ -1021,7 +1032,7 @@ int ast_rtp_senddigit(struct ast_rtp *rtp, char digit) rtpheader[1] = htonl(rtp->lastts); rtpheader[2] = htonl(rtp->ssrc); rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (0)); - for (x=0;x<4;x++) { + for (x=0;x<6;x++) { if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) { res = sendto(rtp->s, (void *)rtpheader, hdrlen + 4, 0, (struct sockaddr *)&rtp->them, sizeof(rtp->them)); if (res <0) @@ -1030,13 +1041,15 @@ int ast_rtp_senddigit(struct ast_rtp *rtp, char digit) printf("Sent %d bytes of RTP data to %s:%d\n", res, ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->them.sin_addr), ntohs(rtp->them.sin_port)); #endif } - if (x ==0) { + if (x == 2) { /* Clear marker bit and increment seqno */ rtpheader[0] = htonl((2 << 30) | (payload << 16) | (rtp->seqno++)); /* Make duration 800 (100ms) */ rtpheader[3] |= htonl((800)); /* Set the End bit for the last 3 */ rtpheader[3] |= htonl((1 << 23)); + } else if ( x < 5) { + rtpheader[0] = htonl((2 << 30) | (payload << 16) | (rtp->seqno++)); } } return 0; diff --git a/say.c b/say.c index 879c56da8a..480b08e8ed 100755 --- a/say.c +++ b/say.c @@ -579,7 +579,7 @@ static int ast_say_number_full_en(struct ast_channel *chan, int num, char *ints, } if (!res) { if(!ast_streamfile(chan, fn, language)) { - if (audiofd && ctrlfd) + if ((audiofd > -1) && (ctrlfd > -1)) res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); else res = ast_waitstream(chan, ints); @@ -682,7 +682,7 @@ static int ast_say_number_full_da(struct ast_channel *chan, int num, char *ints, } if (!res) { if(!ast_streamfile(chan, fn, language)) { - if (audiofd && ctrlfd) + if ((audiofd > -1) && (ctrlfd > -1)) res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); else res = ast_waitstream(chan, ints); @@ -791,14 +791,14 @@ static int ast_say_number_full_de(struct ast_channel *chan, int num, char *ints, } if (!res) { if(!ast_streamfile(chan, fn, language)) { - if (audiofd && ctrlfd) + if ((audiofd > -1) && (ctrlfd > -1)) res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); else res = ast_waitstream(chan, ints); } ast_stopstream(chan); if(!ast_streamfile(chan, fna, language)) { - if (audiofd && ctrlfd) + if ((audiofd > -1) && (ctrlfd > -1)) res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); else res = ast_waitstream(chan, ints); @@ -881,7 +881,7 @@ static int ast_say_number_full_es(struct ast_channel *chan, int num, char *ints, if (!res) { if(!ast_streamfile(chan, fn, language)) { - if (audiofd && ctrlfd) + if ((audiofd > -1) && (ctrlfd > -1)) res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); else res = ast_waitstream(chan, ints); @@ -967,7 +967,7 @@ static int ast_say_number_full_fr(struct ast_channel *chan, int num, char *ints, } if (!res) { if(!ast_streamfile(chan, fn, language)) { - if (audiofd && ctrlfd) + if ((audiofd > -1) && (ctrlfd > -1)) res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); else res = ast_waitstream(chan, ints); @@ -1113,7 +1113,7 @@ static int ast_say_number_full_it(struct ast_channel *chan, int num, char *ints, } if (!res) { if(!ast_streamfile(chan, fn, language)) { - if (audiofd && ctrlfd) + if ((audiofd > -1) && (ctrlfd > -1)) res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); else res = ast_waitstream(chan, ints); @@ -1183,7 +1183,7 @@ static int ast_say_number_full_nl(struct ast_channel *chan, int num, char *ints, if (!res) { if(!ast_streamfile(chan, fn, language)) { - if (audiofd && ctrlfd) + if ((audiofd > -1) && (ctrlfd > -1)) res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); else res = ast_waitstream(chan, ints); @@ -1238,7 +1238,7 @@ static void pl_odtworz_plik(struct ast_channel *chan, char *language, int audiof strcat(file_name, fn); ast_log(LOG_DEBUG, "Trying to play: %s\n", file_name); if (!ast_streamfile(chan, file_name, language)) { - if (audiofd && ctrlfd) + if ((audiofd > -1) && (ctrlfd > -1)) ast_waitstream_full(chan, ints, audiofd, ctrlfd); else ast_waitstream(chan, ints); @@ -1566,19 +1566,19 @@ static int ast_say_number_full_pt(struct ast_channel *chan, int num, char *ints, playh = 1; num = num % 1000000; } - if (!res && playh) { - res = wait_file(chan, ints, "digits/pt-e", language); - ast_stopstream(chan); - playh = 0; - } if (!res) { if(!ast_streamfile(chan, fn, language)) { - if (audiofd && ctrlfd) + if ((audiofd > -1) && (ctrlfd > -1)) res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); else res = ast_waitstream(chan, ints); } ast_stopstream(chan); } + if (!res && playh) { + res = wait_file(chan, ints, "digits/pt-e", language); + ast_stopstream(chan); + playh = 0; + } } return res; } @@ -1638,7 +1638,7 @@ static int ast_say_number_full_se(struct ast_channel *chan, int num, char *ints, } if (!res) { if(!ast_streamfile(chan, fn, language)) { - if (audiofd && ctrlfd) + if ((audiofd > -1) && (ctrlfd > -1)) res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); else res = ast_waitstream(chan, ints); @@ -1699,7 +1699,7 @@ static int ast_say_number_full_tw(struct ast_channel *chan, int num, char *ints, } if (!res) { if(!ast_streamfile(chan, fn, language)) { - if (audiofd && ctrlfd) + if ((audiofd > -1) && (ctrlfd > -1)) res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); else res = ast_waitstream(chan, ints); @@ -1808,7 +1808,7 @@ static int ast_say_number_full_cz(struct ast_channel *chan, int num, char *ints, } if (!res) { if(!ast_streamfile(chan, fn, language)) { - if (audiofd && ctrlfd) { + if ((audiofd > -1) && (ctrlfd > -1)) { res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); } else { res = ast_waitstream(chan, ints); diff --git a/sounds.txt b/sounds.txt index 5797920267..19fc612a73 100755 --- a/sounds.txt +++ b/sounds.txt @@ -447,6 +447,8 @@ %vm-advopts.gsm%press 3 for advanced options +%vm-saveoper.gsm%press 1 to accept this recording, or continue to hold + %vm-toreply.gsm%press 1 to send a reply %vm-tocallback.gsm%press 2 to call the person who sent this message diff --git a/sounds/vm-saveoper.gsm b/sounds/vm-saveoper.gsm new file mode 100755 index 0000000000000000000000000000000000000000..1fa92a22a05f15312bc4ba3cd2f91606a3094fc9 GIT binary patch literal 16533 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 ADgXcg literal 0 HcmV?d00001 diff --git a/tdd.c b/tdd.c index 6ff6c0fbd0..e2a6cc839c 100755 --- a/tdd.c +++ b/tdd.c @@ -77,8 +77,8 @@ struct tdd_state *tdd_new(void) { struct tdd_state *tdd; tdd = malloc(sizeof(struct tdd_state)); - memset(tdd, 0, sizeof(struct tdd_state)); if (tdd) { + memset(tdd, 0, sizeof(struct tdd_state)); tdd->fskd.spb = 176; /* 45.5 baud */ tdd->fskd.hdlc = 0; /* Async */ tdd->fskd.nbit = 5; /* 5 bits */ diff --git a/utils/astman.c b/utils/astman.c deleted file mode 100755 index ebeeb67f1c..0000000000 --- a/utils/astman.c +++ /dev/null @@ -1,683 +0,0 @@ -/* - * ASTerisk MANager - * Copyright (C) 2002, Linux Support Services, Inc. - * - * Distributed under the terms of the GNU General Public License - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#undef gethostbyname - -#define MAX_HEADERS 80 -#define MAX_LEN 256 - -static struct ast_mansession { - struct sockaddr_in sin; - int fd; - char inbuf[MAX_LEN]; - int inlen; -} session; - -static struct ast_chan { - char name[80]; - char exten[20]; - char context[20]; - char priority[20]; - char callerid[40]; - char state[10]; - struct ast_chan *next; -} *chans; - -static struct ast_chan *find_chan(char *name) -{ - struct ast_chan *prev = NULL, *chan = chans; - while(chan) { - if (!strcmp(name, chan->name)) - return chan; - prev = chan; - chan = chan->next; - } - chan = malloc(sizeof(struct ast_chan)); - if (chan) { - memset(chan, 0, sizeof(struct ast_chan)); - strncpy(chan->name, name, sizeof(chan->name) - 1); - if (prev) - prev->next = chan; - else - chans = chan; - } - return chan; -} - -static void del_chan(char *name) -{ - struct ast_chan *prev = NULL, *chan = chans; - while(chan) { - if (!strcmp(name, chan->name)) { - if (prev) - prev->next = chan->next; - else - chans = chan->next; - return; - } - prev = chan; - chan = chan->next; - } -} - -static void fdprintf(int fd, char *fmt, ...) -{ - char stuff[4096]; - va_list ap; - va_start(ap, fmt); - vsnprintf(stuff, sizeof(stuff), fmt, ap); - va_end(ap); - write(fd, stuff, strlen(stuff)); -} - -static char *get_header(struct message *m, char *var) -{ - char cmp[80]; - int x; - snprintf(cmp, sizeof(cmp), "%s: ", var); - for (x=0;xhdrcount;x++) - if (!strncasecmp(cmp, m->headers[x], strlen(cmp))) - return m->headers[x] + strlen(cmp); - return ""; -} - -static int event_newstate(struct ast_mansession *s, struct message *m) -{ - struct ast_chan *chan; - chan = find_chan(get_header(m, "Channel")); - strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1); - return 0; -} - -static int event_newexten(struct ast_mansession *s, struct message *m) -{ - struct ast_chan *chan; - chan = find_chan(get_header(m, "Channel")); - strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1); - strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1); - strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1); - return 0; -} - -static int event_newchannel(struct ast_mansession *s, struct message *m) -{ - struct ast_chan *chan; - chan = find_chan(get_header(m, "Channel")); - strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1); - strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1); - return 0; -} - -static int event_status(struct ast_mansession *s, struct message *m) -{ - struct ast_chan *chan; - chan = find_chan(get_header(m, "Channel")); - strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1); - strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1); - strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1); - strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1); - strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1); - return 0; -} - -static int event_hangup(struct ast_mansession *s, struct message *m) -{ - del_chan(get_header(m, "Channel")); - return 0; -} - -static int event_ignore(struct ast_mansession *s, struct message *m) -{ - return 0; -} - -static int event_rename(struct ast_mansession *s, struct message *m) -{ - struct ast_chan *chan; - chan = find_chan(get_header(m, "Oldname")); - strncpy(chan->name, get_header(m, "Newname"), sizeof(chan->name) - 1); - return 0; -} -static struct event { - char *event; - int (*func)(struct ast_mansession *s, struct message *m); -} events[] = { - { "Newstate", event_newstate }, - { "Newchannel", event_newchannel }, - { "Newexten", event_newexten }, - { "Hangup", event_hangup }, - { "Rename", event_rename }, - { "Status", event_status }, - { "Link", event_ignore }, - { "Unlink", event_ignore }, - { "StatusComplete", event_ignore } -}; - -static int process_message(struct ast_mansession *s, struct message *m) -{ - int x; - char event[80] = ""; - strncpy(event, get_header(m, "Event"), sizeof(event) - 1); - if (!strlen(event)) { - fprintf(stderr, "Missing event in request"); - return 0; - } - for (x=0;x= sizeof(events) / sizeof(events[0])) - fprintf(stderr, "Ignoring unknown event '%s'", event); -#if 0 - for (x=0;xhdrcount;x++) { - printf("Header: %s\n", m->headers[x]); - } -#endif - return 0; -} - -static void rebuild_channels(newtComponent c) -{ - void *prev = NULL; - struct ast_chan *chan; - char tmpn[42]; - char tmp[256]; - int x=0; - prev = newtListboxGetCurrent(c); - newtListboxClear(c); - chan = chans; - while(chan) { - snprintf(tmpn, sizeof(tmpn), "%s (%s)", chan->name, chan->callerid); - if (strlen(chan->exten)) - snprintf(tmp, sizeof(tmp), "%-30s %8s -> %s@%s:%s", - tmpn, chan->state, - chan->exten, chan->context, chan->priority); - else - snprintf(tmp, sizeof(tmp), "%-30s %8s", - tmpn, chan->state); - newtListboxAppendEntry(c, tmp, chan); - x++; - chan = chan->next; - } - if (!x) - newtListboxAppendEntry(c, " << No Active Channels >> ", NULL); - newtListboxSetCurrentByKey(c, prev); -} - -static int has_input(struct ast_mansession *s) -{ - int x; - for (x=1;xinlen;x++) - if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) - return 1; - return 0; -} - -static int get_input(struct ast_mansession *s, char *output) -{ - /* output must have at least sizeof(s->inbuf) space */ - int res; - int x; - struct timeval tv = {0, 0}; - fd_set fds; - for (x=1;xinlen;x++) { - if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) { - /* Copy output data up to and including \r\n */ - memcpy(output, s->inbuf, x + 1); - /* Add trailing \0 */ - output[x+1] = '\0'; - /* Move remaining data back to the front */ - memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x); - s->inlen -= (x + 1); - return 1; - } - } - if (s->inlen >= sizeof(s->inbuf) - 1) { - fprintf(stderr, "Dumping long line with no return from %s: %s\n", inet_ntoa(s->sin.sin_addr), s->inbuf); - s->inlen = 0; - } - FD_ZERO(&fds); - FD_SET(s->fd, &fds); - res = select(s->fd + 1, &fds, NULL, NULL, &tv); - if (res < 0) { - fprintf(stderr, "Select returned error: %s\n", strerror(errno)); - } else if (res > 0) { - res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen); - if (res < 1) - return -1; - s->inlen += res; - s->inbuf[s->inlen] = '\0'; - } else { - return 2; - } - return 0; -} - -static int input_check(struct ast_mansession *s, struct message **mout) -{ - static struct message m; - int res; - - if (mout) - *mout = NULL; - - for(;;) { - res = get_input(s, m.headers[m.hdrcount]); - if (res == 1) { -#if 0 - fprintf(stderr, "Got header: %s", m.headers[m.hdrcount]); - fgetc(stdin); -#endif - /* Strip trailing \r\n */ - if (strlen(m.headers[m.hdrcount]) < 2) - continue; - m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0'; - if (!strlen(m.headers[m.hdrcount])) { - if (mout && strlen(get_header(&m, "Response"))) { - *mout = &m; - return 0; - } - if (process_message(s, &m)) - break; - memset(&m, 0, sizeof(&m)); - } else if (m.hdrcount < MAX_HEADERS - 1) - m.hdrcount++; - } else if (res < 0) { - return -1; - } else if (res == 2) - return 0; - } - return -1; -} - -static struct message *wait_for_response(int timeout) -{ - struct message *m; - struct timeval tv; - int res; - fd_set fds; - for (;;) { - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - FD_SET(session.fd, &fds); - res = select(session.fd + 1, &fds, NULL, NULL, &tv); - if (res < 1) - break; - if (input_check(&session, &m) < 0) { - return NULL; - } - if (m) - return m; - } - return NULL; -} - -static int manager_action(char *action, char *fmt, ...) -{ - struct ast_mansession *s; - char tmp[4096]; - va_list ap; - - s = &session; - fdprintf(s->fd, "Action: %s\r\n", action); - va_start(ap, fmt); - vsnprintf(tmp, sizeof(tmp), fmt, ap); - va_end(ap); - write(s->fd, tmp, strlen(tmp)); - fdprintf(s->fd, "\r\n"); - return 0; -} - -static int show_message(char *title, char *msg) -{ - newtComponent form; - newtComponent label; - newtComponent ok; - struct newtExitStruct es; - - newtCenteredWindow(60,7, title); - - label = newtLabel(4,1,msg); - ok = newtButton(27, 3, "OK"); - form = newtForm(NULL, NULL, 0); - newtFormAddComponents(form, label, ok, NULL); - newtFormRun(form, &es); - newtPopWindow(); - newtFormDestroy(form); - return 0; -} - -static newtComponent showform; -static int show_doing(char *title, char *tmp) -{ - struct newtExitStruct es; - newtComponent label; - showform = newtForm(NULL, NULL, 0); - newtCenteredWindow(70,4, title); - label = newtLabel(3,1,tmp); - newtFormAddComponents(showform,label, NULL); - newtFormSetTimer(showform, 200); - newtFormRun(showform, &es); - return 0; -} - -static int hide_doing(void) -{ - newtPopWindow(); - newtFormDestroy(showform); - return 0; -} - -static void try_status(void) -{ - struct message *m; - manager_action("Status", ""); - m = wait_for_response(10000); - if (!m) { - show_message("Status Failed", "Timeout waiting for response"); - } else if (strcasecmp(get_header(m, "Response"), "Success")) { - show_message("Status Failed Failed", get_header(m, "Message")); - } -} - - -static void try_hangup(newtComponent c) -{ - struct ast_chan *chan; - struct message *m; - - chan = newtListboxGetCurrent(c); - if (chan) { - manager_action("Hangup", "Channel: %s\r\n", chan->name); - m = wait_for_response(10000); - if (!m) { - show_message("Hangup Failed", "Timeout waiting for response"); - } else if (strcasecmp(get_header(m, "Response"), "Success")) { - show_message("Hangup Failed", get_header(m, "Message")); - } - } - -} - -static int get_user_input(char *msg, char *buf, int buflen) -{ - newtComponent form; - newtComponent ok; - newtComponent cancel; - newtComponent inpfield; - const char *input; - int res = -1; - struct newtExitStruct es; - - newtCenteredWindow(60,7, msg); - - inpfield = newtEntry(5, 2, "", 50, &input, 0); - ok = newtButton(22, 3, "OK"); - cancel = newtButton(32, 3, "Cancel"); - form = newtForm(NULL, NULL, 0); - newtFormAddComponents(form, inpfield, ok, cancel, NULL); - newtFormRun(form, &es); - strncpy(buf, input, buflen - 1); - if (es.u.co == ok) - res = 0; - else - res = -1; - newtPopWindow(); - newtFormDestroy(form); - return res; -} - -static void try_redirect(newtComponent c) -{ - struct ast_chan *chan; - char dest[256]; - struct message *m; - char channame[256]; - char tmp[80]; - char *context; - - chan = newtListboxGetCurrent(c); - if (chan) { - strncpy(channame, chan->name, sizeof(channame) - 1); - snprintf(tmp, sizeof(tmp), "Enter new extension for %s", channame); - if (get_user_input(tmp, dest, sizeof(dest))) - return; - if ((context = strchr(dest, '@'))) { - *context = '\0'; - context++; - manager_action("Redirect", "Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name,context,dest); - } else { - manager_action("Redirect", "Channel: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name, dest); - } - m = wait_for_response(10000); - if (!m) { - show_message("Hangup Failed", "Timeout waiting for response"); - } else if (strcasecmp(get_header(m, "Response"), "Success")) { - show_message("Hangup Failed", get_header(m, "Message")); - } - } - -} - -static int manage_calls(char *host) -{ - newtComponent form; - newtComponent quit; - newtComponent hangup; - newtComponent redirect; - newtComponent channels; - struct newtExitStruct es; - char tmp[80]; - - /* If there's one thing you learn from this code, it is this... - Never, ever fly Air France. Their customer service is absolutely - the worst. I've never heard the words "That's not my problem" as - many times as I have from their staff -- It should, without doubt - be their corporate motto if it isn't already. Don't bother giving - them business because you're just a pain in their side and they - will be sure to let you know the first time you speak to them. - - If you ever want to make me happy just tell me that you, too, will - never fly Air France again either (in spite of their excellent - cuisine). */ - snprintf(tmp, sizeof(tmp), "Asterisk Manager at %s", host); - newtCenteredWindow(74, 20, tmp); - form = newtForm(NULL, NULL, 0); - newtFormWatchFd(form, session.fd, NEWT_FD_READ); - newtFormSetTimer(form, 100); - quit = newtButton(62, 16, "Quit"); - redirect = newtButton(35, 16, "Redirect"); - hangup = newtButton(50, 16, "Hangup"); - channels = newtListbox(1,1,14, NEWT_FLAG_SCROLL); - newtFormAddComponents(form, channels, redirect, hangup, quit, NULL); - newtListboxSetWidth(channels, 72); - - show_doing("Getting Status", "Retrieving system status..."); - try_status(); - hide_doing(); - - for(;;) { - newtFormRun(form, &es); - if (has_input(&session) || (es.reason == NEWT_EXIT_FDREADY)) { - if (input_check(&session, NULL)) { - show_message("Disconnected", "Disconnected from remote host"); - break; - } - } else if (es.reason == NEWT_EXIT_COMPONENT) { - if (es.u.co == quit) - break; - if (es.u.co == hangup) { - try_hangup(channels); - } else if (es.u.co == redirect) { - try_redirect(channels); - } - } - rebuild_channels(channels); - } - newtFormDestroy(form); - return 0; -} - -static int login(char *hostname) -{ - newtComponent form; - newtComponent cancel; - newtComponent login; - newtComponent username; - newtComponent password; - newtComponent label; - newtComponent ulabel; - newtComponent plabel; - const char *user; - const char *pass; - struct message *m; - struct newtExitStruct es; - char tmp[55]; - struct hostent *hp; - int res = -1; - - session.fd = socket(AF_INET, SOCK_STREAM, 0); - if (session.fd < 0) { - snprintf(tmp, sizeof(tmp), "socket() failed: %s\n", strerror(errno)); - show_message("Socket failed", tmp); - return -1; - } - - snprintf(tmp, sizeof(tmp), "Looking up %s\n", hostname); - show_doing("Connecting....", tmp); - - - hp = gethostbyname(hostname); - if (!hp) { - snprintf(tmp, sizeof(tmp), "No such address: %s\n", hostname); - show_message("Host lookup failed", tmp); - return -1; - } - hide_doing(); - snprintf(tmp, sizeof(tmp), "Connecting to %s", hostname); - show_doing("Connecting...", tmp); - - session.sin.sin_family = AF_INET; - session.sin.sin_port = htons(DEFAULT_MANAGER_PORT); - memcpy(&session.sin.sin_addr, hp->h_addr, sizeof(session.sin.sin_addr)); - - if (connect(session.fd,(struct sockaddr*)&session.sin, sizeof(session.sin))) { - snprintf(tmp, sizeof(tmp), "%s failed: %s\n", hostname, strerror(errno)); - show_message("Connect Failed", tmp); - return -1; - } - - hide_doing(); - - login = newtButton(5, 6, "Login"); - cancel = newtButton(25, 6, "Cancel"); - newtCenteredWindow(40, 10, "Asterisk Manager Login"); - snprintf(tmp, sizeof(tmp), "Host: %s", hostname); - label = newtLabel(4,1, tmp); - - ulabel = newtLabel(4,2,"Username:"); - plabel = newtLabel(4,3,"Password:"); - - username = newtEntry(14, 2, "", 20, &user, 0); - password = newtEntry(14, 3, "", 20, &pass, NEWT_FLAG_HIDDEN); - - form = newtForm(NULL, NULL, 0); - newtFormAddComponents(form, username, password, login, cancel, label, ulabel, plabel,NULL); - newtFormRun(form, &es); - if (es.reason == NEWT_EXIT_COMPONENT) { - if (es.u.co == login) { - snprintf(tmp, sizeof(tmp), "Logging in '%s'...", user); - show_doing("Logging in", tmp); - /* Check to see if the remote host supports MD5 Authentication */ - manager_action("Challenge", "AuthType: MD5\r\n"); - m = wait_for_response(10000); - if (m && !strcasecmp(get_header(m, "Response"), "Success")) { - char *challenge = get_header(m, "Challenge"); - int x; - int len = 0; - char md5key[256] = ""; - struct MD5Context md5; - unsigned char digest[16]; - MD5Init(&md5); - MD5Update(&md5, challenge, strlen(challenge)); - MD5Update(&md5, pass, strlen(pass)); - MD5Final(digest, &md5); - for (x=0; x<16; x++) - len += sprintf(md5key + len, "%2.2x", digest[x]); - manager_action("Login", - "AuthType: MD5\r\n" - "Username: %s\r\n" - "Key: %s\r\n", - user, md5key); - m = wait_for_response(10000); - hide_doing(); - if (!strcasecmp(get_header(m, "Response"), "Success")) { - res = 0; - } else { - show_message("Login Failed", get_header(m, "Message")); - } - } else { - memset(m, 0, sizeof(m)); - manager_action("Login", - "Username: %s\r\n" - "Secret: %s\r\n", - user, pass); - m = wait_for_response(10000); - hide_doing(); - if (m) { - if (!strcasecmp(get_header(m, "Response"), "Success")) { - res = 0; - } else { - show_message("Login Failed", get_header(m, "Message")); - } - } - } - } - } - newtFormDestroy(form); - return res; -} - -int main(int argc, char *argv[]) -{ - if (argc < 2) { - fprintf(stderr, "Usage: astman \n"); - exit(1); - } - newtInit(); - newtCls(); - newtDrawRootText(0, 0, "Asterisk Manager (C)2002, Linux Support Services, Inc."); - newtPushHelpLine("Welcome to the Asterisk Manager!"); - if (login(argv[1])) { - newtFinished(); - exit(1); - } - manage_calls(argv[1]); - newtFinished(); - return 0; -}