summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES22
-rw-r--r--UPGRADE.txt8
-rw-r--r--apps/app_dial.c5
-rw-r--r--apps/app_followme.c93
-rw-r--r--apps/app_queue.c4
-rw-r--r--apps/app_voicemail.c226
-rw-r--r--cdr/cdr_custom.c2
-rw-r--r--cdr/cdr_syslog.c2
-rw-r--r--cel/cel_custom.c2
-rw-r--r--channels/chan_rtp.c17
-rw-r--r--configs/samples/followme.conf.sample8
-rw-r--r--contrib/ast-db-manage/config/versions/6d8c104e6184_res_pjsip_add_contact_via_addr_and_.py25
-rw-r--r--contrib/ast-db-manage/config/versions/81b01a191a46_pjsip_add_contact_reg_server.py2
-rw-r--r--include/asterisk/multicast_rtp.h58
-rw-r--r--include/asterisk/res_pjsip.h8
-rw-r--r--include/asterisk/stasis_app.h29
-rw-r--r--main/bridge_channel.c29
-rw-r--r--main/cdr.c9
-rw-r--r--main/dial.c3
-rw-r--r--main/logger.c1
-rw-r--r--main/manager_channels.c36
-rw-r--r--main/stasis_channels.c2
-rw-r--r--main/stasis_endpoints.c6
-rw-r--r--res/ari/resource_channels.c139
-rw-r--r--res/res_pjsip.c28
-rw-r--r--res/res_pjsip/location.c17
-rw-r--r--res/res_pjsip/pjsip_configuration.c10
-rw-r--r--res/res_pjsip/pjsip_distributor.c9
-rw-r--r--res/res_pjsip/pjsip_options.c10
-rw-r--r--res/res_pjsip_header_funcs.c9
-rw-r--r--res/res_pjsip_mwi_body_generator.c4
-rw-r--r--res/res_pjsip_outbound_publish.c6
-rw-r--r--res/res_pjsip_registrar.c32
-rw-r--r--res/res_rtp_multicast.c189
-rw-r--r--res/res_rtp_multicast.exports.in6
-rw-r--r--res/res_stasis.c22
-rw-r--r--res/stasis/control.c376
-rw-r--r--third-party/pjproject/patches/0001-2.4.5-fix-for-tls-async-ops.patch224
-rw-r--r--third-party/pjproject/patches/0001-Bump-tcp-tls-and-transaction-log-levels-from-1-to-3.patch70
-rw-r--r--third-party/pjproject/patches/0001-ioqueue-Enable-epoll-in-aconfigure.ac.patch80
-rw-r--r--third-party/pjproject/patches/0001-pjsip-apps-src-python-setup.py-Take-make-from-the-en.patch51
-rw-r--r--third-party/pjproject/patches/0001-sip_parser.c-Fix-pjsip_VIA_PARAM_SPEC_ESC.patch30
-rw-r--r--third-party/pjproject/patches/0001-sip_parser.c-Remove-wholesale-strip-from-parse_param.patch55
-rw-r--r--third-party/pjproject/patches/0001-sip_transport-Search-for-transport-even-if-listener-.patch114
-rw-r--r--third-party/pjproject/patches/0001-sip_transport_tcp-tls-Set-factory-on-transports-crea.patch48
-rw-r--r--third-party/pjproject/patches/0002-aconfigure.ac-Fix-autoconf-issue-with-opencore-amrnb.patch48
-rw-r--r--third-party/versions.mak2
47 files changed, 1281 insertions, 895 deletions
diff --git a/CHANGES b/CHANGES
index 281cff393..6ffefbf35 100644
--- a/CHANGES
+++ b/CHANGES
@@ -12,6 +12,13 @@
--- Functionality changes from Asterisk 13 to Asterisk 14 --------------------
------------------------------------------------------------------------------
+AMI
+-----------------
+ * A new event, "DialState" has been added. This is similar to "DialBegin" and
+ "DialEnd" in that it tracks the state of a dialed call. The difference is that
+ this indicates some intermediate state change in the dial attempt, such as
+ "RINGING", "PROGRESS", or "PROCEEDING".
+
ARI
-----------------
* A new ARI method has been added to the channels resource. "create" allows for
@@ -37,6 +44,10 @@ ARI
transmit the raw media file to the requester as binary.
+ * "Dial" events have been modified to not only be sent when dialing begins and ends.
+ They now are also sent for intermediate states, such as "RINGING", "PROGRESS", and
+ "PROCEEDING".
+
Applications
------------------
@@ -68,6 +79,11 @@ ControlPlayback
* Remote files can now be retrieved and played back. See the Playback
dialplan application for more details.
+FollowMe
+------------------
+ * It is now possible to disable the prompt from a callee by setting
+ 'enable_callee_prompt = no' in followme.conf.
+
Playback
------------------
* Remote files can now be retrieved and played back via the Playback and other
@@ -328,6 +344,12 @@ res_fax
res_pjsip
------------------
+ * Added "via_addr", "via_port", "call_id" to contacts.
+ As res_pjsip_nat rewrites contact's address, only the last Via header
+ can contain the source address of registered endpoint.
+ Also Call-Id header may contain the source address of registered endpoint.
+ Added new fields ViaAddress,CallID to AMI event ContactStatus
+
* Endpoint IP Access Controls
Added new configuration Endpoint options:
"acl" - list of IP ACL section names in acl.conf
diff --git a/UPGRADE.txt b/UPGRADE.txt
index 131ce6caa..f8fa7906b 100644
--- a/UPGRADE.txt
+++ b/UPGRADE.txt
@@ -22,6 +22,14 @@
=== UPGRADE-13.txt -- Upgrade info for 12 to 13
===========================================================
+ARI:
+ - The policy for when to send "Dial" events has changed. Previously, "Dial"
+ events were sent on the calling channel's topic. However, starting in Asterisk
+ 14, if there is no calling channel on which to send the event, the event is
+ instead sent on the called channel's topic. Note that for the ARI channels
+ resource's dial operation, this means that the "Dial" events will always be
+ sent on the called channel's topic.
+
Channel Drivers:
chan_dahdi:
diff --git a/apps/app_dial.c b/apps/app_dial.c
index 11591bdfb..5e034d3eb 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -872,6 +872,8 @@ static void do_forward(struct chanlist *o, struct cause_args *num,
/* If we have been told to ignore forwards, just set this channel to null and continue processing extensions normally */
if (ast_test_flag64(peerflags, OPT_IGNORE_FORWARDING)) {
ast_verb(3, "Forwarding %s to '%s/%s' prevented.\n", ast_channel_name(in), tech, stuff);
+ ast_channel_publish_dial_forward(in, original, NULL, NULL, "CANCEL",
+ ast_channel_call_forward(original));
c = o->chan = NULL;
cause = AST_CAUSE_BUSY;
} else {
@@ -1385,6 +1387,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
pa->sentringing++;
}
}
+ ast_channel_publish_dial(in, c, NULL, "RINGING");
break;
case AST_CONTROL_PROGRESS:
ast_verb(3, "%s is making progress passing it to %s\n", ast_channel_name(c), ast_channel_name(in));
@@ -1404,6 +1407,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
dtmf_progress);
ast_dtmf_stream(c, in, dtmf_progress, 250, 0);
}
+ ast_channel_publish_dial(in, c, NULL, "PROGRESS");
break;
case AST_CONTROL_VIDUPDATE:
case AST_CONTROL_SRCUPDATE:
@@ -1476,6 +1480,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
}
if (!ast_test_flag64(outgoing, OPT_RINGBACK))
ast_indicate(in, AST_CONTROL_PROCEEDING);
+ ast_channel_publish_dial(in, c, NULL, "PROCEEDING");
break;
case AST_CONTROL_HOLD:
/* XXX this should be saved like AST_CONTROL_CONNECTED_LINE for !single || caller_entertained */
diff --git a/apps/app_followme.c b/apps/app_followme.c
index 106902043..a955843bc 100644
--- a/apps/app_followme.c
+++ b/apps/app_followme.c
@@ -168,6 +168,8 @@ struct call_followme {
char context[AST_MAX_CONTEXT]; /*!< Context to dial from */
unsigned int active; /*!< Profile is active (1), or disabled (0). */
int realtime; /*!< Cached from realtime */
+ /*! Allow callees to accept/reject the forwarded call */
+ unsigned int enable_callee_prompt:1;
char takecall[MAX_YN_STRING]; /*!< Digit mapping to take a call */
char nextindp[MAX_YN_STRING]; /*!< Digit mapping to decline a call */
char callfromprompt[PATH_MAX]; /*!< Sound prompt name and path */
@@ -198,6 +200,8 @@ struct fm_args {
unsigned int pending_out_connected_update:1;
/*! TRUE if caller has a pending hold request for the winning call. */
unsigned int pending_hold:1;
+ /*! TRUE if callees will be prompted to answer */
+ unsigned int enable_callee_prompt:1;
/*! Music On Hold Class suggested by caller hold for winning call. */
char suggested_moh[MAX_MUSICCLASS];
char context[AST_MAX_CONTEXT];
@@ -268,6 +272,7 @@ static const char *defaultmoh = "default"; /*!< Default Music-On-Hold Class
static char takecall[MAX_YN_STRING] = "1";
static char nextindp[MAX_YN_STRING] = "2";
+static int enable_callee_prompt = 1;
static char callfromprompt[PATH_MAX] = "followme/call-from";
static char norecordingprompt[PATH_MAX] = "followme/no-recording";
static char optionsprompt[PATH_MAX] = "followme/options";
@@ -313,6 +318,7 @@ static struct call_followme *alloc_profile(const char *fmname)
ast_copy_string(f->name, fmname, sizeof(f->name));
f->moh[0] = '\0';
f->context[0] = '\0';
+ f->enable_callee_prompt = enable_callee_prompt;
ast_copy_string(f->takecall, takecall, sizeof(f->takecall));
ast_copy_string(f->nextindp, nextindp, sizeof(f->nextindp));
ast_copy_string(f->callfromprompt, callfromprompt, sizeof(f->callfromprompt));
@@ -343,6 +349,8 @@ static void profile_set_param(struct call_followme *f, const char *param, const
ast_copy_string(f->moh, val, sizeof(f->moh));
else if (!strcasecmp(param, "context"))
ast_copy_string(f->context, val, sizeof(f->context));
+ else if (!strcasecmp(param, "enable_callee_prompt"))
+ f->enable_callee_prompt = ast_true(val);
else if (!strcasecmp(param, "takecall"))
ast_copy_string(f->takecall, val, sizeof(f->takecall));
else if (!strcasecmp(param, "declinecall"))
@@ -398,6 +406,7 @@ static int reload_followme(int reload)
char *numberstr;
int timeout;
int numorder;
+ const char* enable_callee_prompt_str;
const char *takecallstr;
const char *declinecallstr;
const char *tmpstr;
@@ -430,6 +439,12 @@ static int reload_followme(int reload)
featuredigittimeout = 5000;
}
+ if ((enable_callee_prompt_str = ast_variable_retrieve(cfg, "general",
+ "enable_callee_prompt")) &&
+ !ast_strlen_zero(enable_callee_prompt_str)) {
+ enable_callee_prompt = ast_true(enable_callee_prompt_str);
+ }
+
if ((takecallstr = ast_variable_retrieve(cfg, "general", "takecall")) && !ast_strlen_zero(takecallstr)) {
ast_copy_string(takecall, takecallstr, sizeof(takecall));
}
@@ -651,26 +666,30 @@ static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_us
if (tmpuser->digts && (tmpuser->digts > featuredigittimeout)) {
ast_verb(3, "<%s> We've been waiting for digits longer than we should have.\n",
ast_channel_name(tmpuser->ochan));
- if (!ast_strlen_zero(tpargs->namerecloc)) {
- tmpuser->state = 1;
- tmpuser->digts = 0;
- if (!ast_streamfile(tmpuser->ochan, callfromname, ast_channel_language(tmpuser->ochan))) {
- ast_sched_runq(ast_channel_sched(tmpuser->ochan));
+ if (tpargs->enable_callee_prompt) {
+ if (!ast_strlen_zero(tpargs->namerecloc)) {
+ tmpuser->state = 1;
+ tmpuser->digts = 0;
+ if (!ast_streamfile(tmpuser->ochan, callfromname, ast_channel_language(tmpuser->ochan))) {
+ ast_sched_runq(ast_channel_sched(tmpuser->ochan));
+ } else {
+ ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
+ clear_caller(tmpuser);
+ continue;
+ }
} else {
- ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
- clear_caller(tmpuser);
- continue;
+ tmpuser->state = 2;
+ tmpuser->digts = 0;
+ if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, ast_channel_language(tmpuser->ochan)))
+ ast_sched_runq(ast_channel_sched(tmpuser->ochan));
+ else {
+ ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
+ clear_caller(tmpuser);
+ continue;
+ }
}
} else {
- tmpuser->state = 2;
- tmpuser->digts = 0;
- if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, ast_channel_language(tmpuser->ochan)))
- ast_sched_runq(ast_channel_sched(tmpuser->ochan));
- else {
- ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
- clear_caller(tmpuser);
- continue;
- }
+ tmpuser->state = 3;
}
}
if (ast_channel_stream(tmpuser->ochan)) {
@@ -787,23 +806,28 @@ static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_us
/* If call has been answered, then the eventual hangup is likely to be normal hangup */
ast_channel_hangupcause_set(winner, AST_CAUSE_NORMAL_CLEARING);
ast_channel_hangupcause_set(caller, AST_CAUSE_NORMAL_CLEARING);
- ast_verb(3, "Starting playback of %s\n", callfromname);
- if (!ast_strlen_zero(tpargs->namerecloc)) {
- if (!ast_streamfile(winner, callfromname, ast_channel_language(winner))) {
- ast_sched_runq(ast_channel_sched(winner));
- tmpuser->state = 1;
+ if (tpargs->enable_callee_prompt) {
+ ast_verb(3, "Starting playback of %s\n", callfromname);
+ if (!ast_strlen_zero(tpargs->namerecloc)) {
+ if (!ast_streamfile(winner, callfromname, ast_channel_language(winner))) {
+ ast_sched_runq(ast_channel_sched(winner));
+ tmpuser->state = 1;
+ } else {
+ ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
+ clear_caller(tmpuser);
+ }
} else {
- ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
- clear_caller(tmpuser);
+ tmpuser->state = 2;
+ if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, ast_channel_language(tmpuser->ochan)))
+ ast_sched_runq(ast_channel_sched(tmpuser->ochan));
+ else {
+ ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
+ clear_caller(tmpuser);
+ }
}
} else {
+ ast_verb(3, "Skip playback of caller name / norecording\n");
tmpuser->state = 2;
- if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, ast_channel_language(tmpuser->ochan)))
- ast_sched_runq(ast_channel_sched(tmpuser->ochan));
- else {
- ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
- clear_caller(tmpuser);
- }
}
break;
case AST_CONTROL_BUSY:
@@ -824,9 +848,11 @@ static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_us
break;
case AST_CONTROL_RINGING:
ast_verb(3, "%s is ringing\n", ast_channel_name(winner));
+ ast_channel_publish_dial(caller, winner, NULL, "RINGING");
break;
case AST_CONTROL_PROGRESS:
ast_verb(3, "%s is making progress\n", ast_channel_name(winner));
+ ast_channel_publish_dial(caller, winner, NULL, "PROGRESS");
break;
case AST_CONTROL_VIDUPDATE:
ast_verb(3, "%s requested a video update\n", ast_channel_name(winner));
@@ -836,6 +862,7 @@ static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_us
break;
case AST_CONTROL_PROCEEDING:
ast_verb(3, "%s is proceeding\n", ast_channel_name(winner));
+ ast_channel_publish_dial(caller, winner, NULL, "PROCEEDING");
break;
case AST_CONTROL_HOLD:
ast_verb(3, "%s placed call on hold\n", ast_channel_name(winner));
@@ -927,6 +954,11 @@ static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_us
break;
}
}
+ if (!tpargs->enable_callee_prompt && tmpuser) {
+ ast_debug(1, "Taking call with no prompt\n");
+ ast_frfree(f);
+ return tmpuser->ochan;
+ }
if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) {
int cmp_len;
@@ -1365,6 +1397,7 @@ static int app_exec(struct ast_channel *chan, const char *data)
/* Lock the profile lock and copy out everything we need to run with before unlocking it again */
ast_mutex_lock(&f->lock);
+ targs->enable_callee_prompt = f->enable_callee_prompt;
targs->mohclass = ast_strdupa(f->moh);
ast_copy_string(targs->context, f->context, sizeof(targs->context));
ast_copy_string(targs->takecall, f->takecall, sizeof(targs->takecall));
diff --git a/apps/app_queue.c b/apps/app_queue.c
index 8171d11cb..165924e0c 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -4846,6 +4846,8 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
/* Before processing channel, go ahead and check for forwarding */
if (!ast_strlen_zero(ast_channel_call_forward(o->chan)) && !forwardsallowed) {
ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, ast_channel_call_forward(o->chan));
+ ast_channel_publish_dial_forward(qe->chan, o->chan, NULL, NULL,
+ "CANCEL", ast_channel_call_forward(o->chan));
numnochan++;
do_hang(o);
winner = NULL;
@@ -5052,6 +5054,8 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
case AST_CONTROL_RINGING:
ast_verb(3, "%s is ringing\n", ochan_name);
+ ast_channel_publish_dial(qe->chan, o->chan, on, "RINGING");
+
/* Start ring indication when the channel is ringing, if specified */
if (qe->ring_when_ringing) {
ast_moh_stop(qe->chan);
diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c
index e7de8a2b2..1d5b2dcb3 100644
--- a/apps/app_voicemail.c
+++ b/apps/app_voicemail.c
@@ -1088,7 +1088,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, int *sound_duration, const char *unlockdir,
- signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id);
+ signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro);
static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag);
@@ -2174,6 +2174,7 @@ static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_
{
struct vm_state *vms_p;
char *file, *filename;
+ char dest[PATH_MAX];
char *attachment;
int i;
BODY *body;
@@ -2237,6 +2238,7 @@ static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_
if (!strcmp(filename, file)) {
ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
vms_p->msgArray[vms_p->curmsg] = i + 1;
+ create_dirpath(dest, sizeof(dest), vmu->context, vms_p->username, "");
save_body(body, vms_p, "2", attachment, 0);
ret = 0;
break;
@@ -2369,7 +2371,8 @@ static int imap_retrieve_file(const char *dir, const int msgnum, const char *mai
snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
if (!(text_file_ptr = fopen(text_file, "w"))) {
- ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
+ ast_log(LOG_ERROR, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
+ goto exit;
}
fprintf(text_file_ptr, "%s\n", "[message]");
@@ -2709,8 +2712,8 @@ static int imap_store_file(const char *dir, const char *mailboxuser, const char
}
make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
- S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
- S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
+ chan ? S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL) : NULL,
+ chan ? S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL) : NULL,
fn, introfn, fmt, duration, 1, chan, NULL, 1, flag, msg_id);
/* read mail file to memory */
len = ftell(p);
@@ -3018,6 +3021,10 @@ static int init_mailstream(struct vm_state *vms, int box)
ast_mutex_lock(&vms->lock);
ast_mutex_lock(&mail_open_lock);
vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
+ /* Create the folder if it dosn't exist */
+ if (vms->mailstream && !mail_status(vms->mailstream, tmp, SA_UIDNEXT)) {
+ mail_create(vms->mailstream, tmp);
+ }
ast_mutex_unlock(&mail_open_lock);
ast_mutex_unlock(&vms->lock);
if (vms->mailstream == NIL) {
@@ -3112,7 +3119,15 @@ static void write_file(char *filename, char *buffer, unsigned long len)
{
FILE *output;
- output = fopen (filename, "w");
+ if (!filename || !buffer) {
+ return;
+ }
+
+ if (!(output = fopen(filename, "w"))) {
+ ast_log(LOG_ERROR, "Unable to open/create file %s: %s\n", filename, strerror(errno));
+ return;
+ }
+
if (fwrite(buffer, len, 1, output) != 1) {
if (ferror(output)) {
ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
@@ -3247,18 +3262,30 @@ void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
{
- ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
- if (status->flags & SA_MESSAGES)
- ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
- if (status->flags & SA_RECENT)
- ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
- if (status->flags & SA_UNSEEN)
- ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
- if (status->flags & SA_UIDVALIDITY)
- ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
- if (status->flags & SA_UIDNEXT)
- ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
- ast_log(AST_LOG_NOTICE, "\n");
+ struct ast_str *str;
+ if (!DEBUG_ATLEAST(5) || !(str = ast_str_create(MAX_OBJECT_FIELD))) {
+ return;
+ }
+
+ ast_str_append(&str, 0, " Mailbox %s", mailbox);
+ if (status->flags & SA_MESSAGES) {
+ ast_str_append(&str, 0, ", %lu messages", status->messages);
+ }
+ if (status->flags & SA_RECENT) {
+ ast_str_append(&str, 0, ", %lu recent", status->recent);
+ }
+ if (status->flags & SA_UNSEEN) {
+ ast_str_append(&str, 0, ", %lu unseen", status->unseen);
+ }
+ if (status->flags & SA_UIDVALIDITY) {
+ ast_str_append(&str, 0, ", %lu UID validity", status->uidvalidity);
+ }
+ if (status->flags & SA_UIDNEXT) {
+ ast_str_append(&str, 0, ", %lu next UID", status->uidnext);
+ }
+ ast_log(LOG_DEBUG, "%s\n", ast_str_buffer(str));
+
+ ast_free(str);
}
@@ -3446,8 +3473,9 @@ static struct vm_state *get_vm_state_by_imapuser(const char *user, int interacti
if (interactive) {
struct vm_state *vms;
pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
- vms = pthread_getspecific(ts_vmstate.key);
- return vms;
+ if ((vms = pthread_getspecific(ts_vmstate.key)) && vms->imapuser && !strcmp(vms->imapuser, user)) {
+ return vms;
+ }
}
AST_LIST_LOCK(&vmstates);
@@ -3485,8 +3513,10 @@ static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char
if (interactive) {
struct vm_state *vms;
pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
- vms = pthread_getspecific(ts_vmstate.key);
- return vms;
+ if ((vms = pthread_getspecific(ts_vmstate.key)) && vms->username && vms->context &&
+ !strcmp(vms->username,mailbox) && !strcmp(vms->context, local_context)) {
+ return vms;
+ }
}
AST_LIST_LOCK(&vmstates);
@@ -5133,7 +5163,9 @@ static void make_email_file(FILE *p,
first_line = 0;
}
- if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
+ if (msgnum <= -1) {
+ fprintf(p, "Subject: New greeting '%s' on %s." ENDL, greeting_attachment, date);
+ } else if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
struct ast_channel *ast;
if ((ast = ast_dummy_channel_alloc())) {
@@ -5186,9 +5218,8 @@ static void make_email_file(FILE *p,
fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
#endif
/* flag added for Urgent */
- fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
+ fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, S_OR(flag, ""));
fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan ? ast_channel_priority(chan) : 0);
- fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan ? ast_channel_name(chan) : "");
fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
@@ -5219,7 +5250,11 @@ static void make_email_file(FILE *p,
fprintf(p, "--%s" ENDL, bound);
}
fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
- if (emailbody || vmu->emailbody) {
+ if (msgnum <= -1) {
+ fprintf(p, "This message is to let you know that your greeting '%s' was changed on %s." ENDL
+ "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL,
+ greeting_attachment, date);
+ } else if (emailbody || vmu->emailbody) {
char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
struct ast_channel *ast;
if ((ast = ast_dummy_channel_alloc())) {
@@ -5245,7 +5280,7 @@ static void make_email_file(FILE *p,
} else {
ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
}
- } else if (msgnum > -1) {
+ } else {
if (strcmp(vmu->mailbox, mailbox)) {
/* Forwarded type */
struct ast_config *msg_cfg;
@@ -5290,9 +5325,6 @@ plain_message:
ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
(cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
}
- } else {
- fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
- "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
}
if (imap || attach_user_voicemail) {
@@ -6851,7 +6883,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
goto leave_vm_out;
}
- res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag, msg_id);
+ res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag, msg_id, 0);
if (txt) {
fprintf(txt, "flag=%s\n", flag);
@@ -7055,6 +7087,7 @@ static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg
char sequence[10];
char mailbox[256];
int res;
+ int curr_mbox;
/* get the real IMAP message number for this message */
snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
@@ -7064,26 +7097,35 @@ static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg
/* if save to Old folder, put in INBOX as read */
if (box == OLD_FOLDER) {
mail_setflag(vms->mailstream, sequence, "\\Seen");
- mail_clearflag(vms->mailstream, sequence, "\\Unseen");
} else if (box == NEW_FOLDER) {
- mail_setflag(vms->mailstream, sequence, "\\Unseen");
mail_clearflag(vms->mailstream, sequence, "\\Seen");
}
if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
ast_mutex_unlock(&vms->lock);
return 0;
}
- /* Create the folder if it don't exist */
+
+ /* get the current mailbox so that we can point the mailstream back to it later */
+ curr_mbox = get_folder_by_name(vms->curbox);
+
+ /* Create the folder if it dosn't exist */
imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
- ast_debug(5, "Checking if folder exists: %s\n", mailbox);
- if (mail_create(vms->mailstream, mailbox) == NIL)
- ast_debug(5, "Folder exists.\n");
- else
- ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
- if (move) {
- res = !mail_move(vms->mailstream, sequence, (char *) mbox(vmu, box));
+ if (vms->mailstream && !mail_status(vms->mailstream, mailbox, SA_UIDNEXT)) {
+ if (mail_create(vms->mailstream, mailbox) != NIL) {
+ ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
+ }
+ }
+
+ /* restore previous mbox stream */
+ if (init_mailstream(vms, curr_mbox) || !vms->mailstream) {
+ ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
+ res = -1;
} else {
- res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
+ if (move) {
+ res = !mail_move(vms->mailstream, sequence, (char *) mbox(vmu, box));
+ } else {
+ res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
+ }
}
ast_mutex_unlock(&vms->lock);
return res;
@@ -7814,9 +7856,9 @@ static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu,
}
make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
strncat(vms->introfn, "intro", sizeof(vms->introfn));
- ast_play_and_wait(chan, INTRO);
+ ast_play_and_wait(chan, "vm-record-prepend");
ast_play_and_wait(chan, "beep");
- cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag, msg_id);
+ cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag, msg_id, 1);
if (cmd == -1) {
break;
}
@@ -8085,6 +8127,7 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
char *stringp;
const char *s;
+ const char mailbox_context[256];
int saved_messages = 0;
int valid_extensions = 0;
char *dir;
@@ -8193,20 +8236,44 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
/* start optimistic */
valid_extensions = 1;
while (s) {
+ snprintf((char*)mailbox_context, sizeof(mailbox_context), "%s@%s", s, context ? context : "default");
if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
int oldmsgs;
int newmsgs;
int capacity;
- if (inboxcount(s, &newmsgs, &oldmsgs)) {
- ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
+
+ if (inboxcount(mailbox_context, &newmsgs, &oldmsgs)) {
+ ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", mailbox_context);
/* Shouldn't happen, but allow trying another extension if it does */
res = ast_play_and_wait(chan, "pbx-invalid");
valid_extensions = 0;
break;
}
+#ifdef IMAP_STORAGE
+ if (!(dstvms = get_vm_state_by_mailbox(s, context, 0))) {
+ if (!(dstvms = create_vm_state_from_user(receiver))) {
+ ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
+ /* Shouldn't happen, but allow trying another extension if it does */
+ res = ast_play_and_wait(chan, "pbx-invalid");
+ valid_extensions = 0;
+ break;
+ }
+ }
+ check_quota(dstvms, imapfolder);
+ if (dstvms->quota_limit && dstvms->quota_usage >= dstvms->quota_limit) {
+ ast_log(LOG_NOTICE, "Mailbox '%s' is exceeded quota %u >= %u\n", mailbox_context, dstvms->quota_usage, dstvms->quota_limit);
+ res = ast_play_and_wait(chan, "vm-mailboxfull");
+ valid_extensions = 0;
+ while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
+ inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
+ free_user(vmtmp);
+ }
+ break;
+ }
+#endif
capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
if ((newmsgs + oldmsgs) >= capacity) {
- ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
+ ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", mailbox_context, capacity);
res = ast_play_and_wait(chan, "vm-mailboxfull");
valid_extensions = 0;
while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
@@ -8226,7 +8293,7 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
free_user(receiver);
}
- ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
+ ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", mailbox_context);
/* "I am sorry, that's not a valid extension. Please try again." */
res = ast_play_and_wait(chan, "pbx-invalid");
valid_extensions = 0;
@@ -8306,7 +8373,7 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
if (!dstvms->mailstream) {
ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
} else {
- copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str, msg_id);
+ copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str, msg_id);
run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str);
}
} else {
@@ -8342,10 +8409,6 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
res = ast_play_and_wait(chan, "vm-messages");
if (!res)
res = ast_play_and_wait(chan, "vm-saved"); */
-#ifdef IMAP_STORAGE
- /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
- if (ast_strlen_zero(vmstmp.introfn))
-#endif
res = ast_play_and_wait(chan, "vm-msgforwarded");
}
#ifndef IMAP_STORAGE
@@ -8814,6 +8877,7 @@ static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
char arg[10];
int i;
BODY* body;
+ int curr_mbox;
file = strrchr(ast_strdupa(dir), '/');
if (file) {
@@ -8824,6 +8888,16 @@ static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
}
ast_mutex_lock(&vms->lock);
+
+ /* get the current mailbox so that we can point the mailstream back to it later */
+ curr_mbox = get_folder_by_name(vms->curbox);
+
+ if (init_mailstream(vms, GREETINGS_FOLDER) || !vms->mailstream) {
+ ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
+ ast_mutex_unlock(&vms->lock);
+ return -1;
+ }
+
for (i = 0; i < vms->mailstream->nmsgs; i++) {
mail_fetchstructure(vms->mailstream, i + 1, &body);
/* We have the body, now we extract the file name of the first attachment. */
@@ -8841,6 +8915,14 @@ static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
}
}
mail_expunge(vms->mailstream);
+
+ if (curr_mbox != -1) {
+ /* restore previous mbox stream */
+ if (init_mailstream(vms, curr_mbox) || !vms->mailstream) {
+ ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
+ }
+ }
+
ast_mutex_unlock(&vms->lock);
return 0;
}
@@ -10348,7 +10430,7 @@ static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct
if (ast_test_flag(vmu, VM_FORCENAME)) {
snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
if (ast_fileexists(prefile, NULL, NULL) < 1) {
- cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+ cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
if (cmd < 0 || cmd == 't' || cmd == '#')
return cmd;
}
@@ -10358,14 +10440,14 @@ static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct
if (ast_test_flag(vmu, VM_FORCEGREET)) {
snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
if (ast_fileexists(prefile, NULL, NULL) < 1) {
- cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+ cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
if (cmd < 0 || cmd == 't' || cmd == '#')
return cmd;
}
snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
if (ast_fileexists(prefile, NULL, NULL) < 1) {
- cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+ cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
if (cmd < 0 || cmd == 't' || cmd == '#')
return cmd;
}
@@ -10447,15 +10529,15 @@ static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct
switch (cmd) {
case '1': /* Record your unavailable message */
snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
- cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+ cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
break;
case '2': /* Record your busy message */
snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
- cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+ cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
break;
case '3': /* Record greeting */
snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
- cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+ cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
break;
case '4': /* manage the temporary greeting */
cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
@@ -10589,7 +10671,7 @@ static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, st
retries = 0;
RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
if (ast_fileexists(prefile, NULL, NULL) <= 0) {
- cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+ cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
if (cmd == -1) {
break;
}
@@ -10597,7 +10679,7 @@ static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, st
} else {
switch (cmd) {
case '1':
- cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+ cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
break;
case '2':
DELETE(prefile, -1, prefile, vmu);
@@ -11876,7 +11958,13 @@ static int vm_execmain(struct ast_channel *chan, const char *data)
cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
if (useadsi)
adsi_status(chan, &vms);
- break;
+ /* Reopen play_folder */
+ res = open_mailbox(&vms, vmu, play_folder);
+ if (res < 0) {
+ goto out;
+ }
+ vms.starting = 1;
+ break;
default: /* Nothing */
ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
@@ -15109,7 +15197,7 @@ static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, s
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, int *sound_duration, const char *unlockdir,
- signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id)
+ signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro)
{
/* Record message & let caller review or re-record it, or set options if applicable */
int res = 0;
@@ -15151,7 +15239,9 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
ast_verb(3, "Saving message as is\n");
if (!outsidecaller)
ast_filerename(tempfile, recordfile, NULL);
- ast_stream_and_wait(chan, "vm-msgsaved", "");
+ if (!forwardintro) {
+ ast_stream_and_wait(chan, "vm-msgsaved", "");
+ }
if (!outsidecaller) {
/* Saves to IMAP server only if imapgreeting=yes */
STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag, msg_id);
@@ -15174,7 +15264,11 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
ast_verb(3, "Recording the message\n");
if (recorded && outsidecaller) {
- cmd = ast_play_and_wait(chan, INTRO);
+ if (forwardintro) {
+ cmd = ast_play_and_wait(chan, "vm-record-prepend");
+ } else {
+ cmd = ast_play_and_wait(chan, INTRO);
+ }
cmd = ast_play_and_wait(chan, "beep");
}
recorded = 1;
@@ -15295,10 +15389,10 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
}
return cmd;
default:
- /* If the caller is an ouside caller, and the review option is enabled,
+ /* If the caller is an ouside caller and the review option is enabled or it's forward intro
allow them to review the message, but let the owner of the box review
their OGM's */
- if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
+ if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW) && !forwardintro)
return cmd;
if (msg_exists) {
cmd = ast_play_and_wait(chan, "vm-review");
diff --git a/cdr/cdr_custom.c b/cdr/cdr_custom.c
index 24cb834f6..bb63ea0c6 100644
--- a/cdr/cdr_custom.c
+++ b/cdr/cdr_custom.c
@@ -81,8 +81,10 @@ static AST_RWLIST_HEAD_STATIC(sinks, cdr_custom_config);
static void free_config(void)
{
struct cdr_custom_config *sink;
+
while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
ast_mutex_destroy(&sink->lock);
+ ast_string_field_free_memory(sink);
ast_free(sink);
}
}
diff --git a/cdr/cdr_syslog.c b/cdr/cdr_syslog.c
index 85860ecec..39f115920 100644
--- a/cdr/cdr_syslog.c
+++ b/cdr/cdr_syslog.c
@@ -76,8 +76,10 @@ static AST_RWLIST_HEAD_STATIC(sinks, cdr_syslog_config);
static void free_config(void)
{
struct cdr_syslog_config *sink;
+
while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
ast_mutex_destroy(&sink->lock);
+ ast_string_field_free_memory(sink);
ast_free(sink);
}
}
diff --git a/cel/cel_custom.c b/cel/cel_custom.c
index f6377fcd7..f75efdd4b 100644
--- a/cel/cel_custom.c
+++ b/cel/cel_custom.c
@@ -71,8 +71,10 @@ static AST_RWLIST_HEAD_STATIC(sinks, cel_config);
static void free_config(void)
{
struct cel_config *sink;
+
while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
ast_mutex_destroy(&sink->lock);
+ ast_string_field_free_memory(sink);
ast_free(sink);
}
}
diff --git a/channels/chan_rtp.c b/channels/chan_rtp.c
index 56705b1f0..093602823 100644
--- a/channels/chan_rtp.c
+++ b/channels/chan_rtp.c
@@ -43,6 +43,7 @@ ASTERISK_REGISTER_FILE()
#include "asterisk/rtp_engine.h"
#include "asterisk/causes.h"
#include "asterisk/format_cache.h"
+#include "asterisk/multicast_rtp.h"
/* Forward declarations */
static struct ast_channel *multicast_rtp_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause);
@@ -132,7 +133,9 @@ static struct ast_channel *multicast_rtp_request(const char *type, struct ast_fo
AST_APP_ARG(type);
AST_APP_ARG(destination);
AST_APP_ARG(control);
+ AST_APP_ARG(options);
);
+ struct ast_multicast_rtp_options *mcast_options = NULL;
if (ast_strlen_zero(data)) {
ast_log(LOG_ERROR, "A multicast type and destination must be given to the 'MulticastRTP' channel\n");
@@ -163,7 +166,15 @@ static struct ast_channel *multicast_rtp_request(const char *type, struct ast_fo
goto failure;
}
- fmt = ast_format_cap_get_format(cap, 0);
+ mcast_options = ast_multicast_rtp_create_options(args.type, args.options);
+ if (!mcast_options) {
+ goto failure;
+ }
+
+ fmt = ast_multicast_rtp_options_get_format(mcast_options);
+ if (!fmt) {
+ fmt = ast_format_cap_get_format(cap, 0);
+ }
if (!fmt) {
ast_log(LOG_ERROR, "No format available for sending RTP to '%s'\n",
args.destination);
@@ -175,7 +186,7 @@ static struct ast_channel *multicast_rtp_request(const char *type, struct ast_fo
goto failure;
}
- instance = ast_rtp_instance_new("multicast", NULL, &control_address, args.type);
+ instance = ast_rtp_instance_new("multicast", NULL, &control_address, mcast_options);
if (!instance) {
ast_log(LOG_ERROR,
"Could not create '%s' multicast RTP instance for sending media to '%s'\n",
@@ -207,12 +218,14 @@ static struct ast_channel *multicast_rtp_request(const char *type, struct ast_fo
ao2_ref(fmt, -1);
ao2_ref(caps, -1);
+ ast_multicast_rtp_free_options(mcast_options);
return chan;
failure:
ao2_cleanup(fmt);
ao2_cleanup(caps);
+ ast_multicast_rtp_free_options(mcast_options);
*cause = AST_CAUSE_FAILURE;
return NULL;
}
diff --git a/configs/samples/followme.conf.sample b/configs/samples/followme.conf.sample
index a233948bf..7e1d40ff4 100644
--- a/configs/samples/followme.conf.sample
+++ b/configs/samples/followme.conf.sample
@@ -5,6 +5,11 @@ featuredigittimeout=>5000
; The number of ms to wait for a digit input for the callee on whether to take the call or
; not before we consider them "done" entering digits.
;
+enable_callee_prompt=>true
+; Enable prompting a callee to either accept or reject the forwarded call.
+; If disabled, the optional prompting for caller name (option 'a') is
+; likewise disabled. Enabled by default.
+;
takecall=>1
; The global default keypress for the callee to take taking the current call. This can be
; a single digit or multiple digits. Default is "1".
@@ -54,6 +59,9 @@ number=>01233456,25
; step to make a choice on whether to take the call or not. That being the case,
; you may want to make the timeout on the last step longer to give enough time to
; make the choice to accept or not.
+enable_callee_prompt=>true
+; Enable prompting the callee to accept the forwarded call. The default
+; is the global value.
takecall=>1
; The keypress for the callee to take taking the current call. This can be
; a single digit or multiple digits. Default is the global default.
diff --git a/contrib/ast-db-manage/config/versions/6d8c104e6184_res_pjsip_add_contact_via_addr_and_.py b/contrib/ast-db-manage/config/versions/6d8c104e6184_res_pjsip_add_contact_via_addr_and_.py
new file mode 100644
index 000000000..893d9d82e
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/6d8c104e6184_res_pjsip_add_contact_via_addr_and_.py
@@ -0,0 +1,25 @@
+"""res_pjsip: add contact via_addr and callid
+
+Revision ID: a845e4d8ade8
+Revises: bca7113d796f
+Create Date: 2016-05-19 15:51:33.410852
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'a845e4d8ade8'
+down_revision = 'bca7113d796f'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+ op.add_column('ps_contacts', sa.Column('via_addr', sa.String(40)))
+ op.add_column('ps_contacts', sa.Column('via_port', sa.Integer))
+ op.add_column('ps_contacts', sa.Column('call_id', sa.String(255)))
+
+def downgrade():
+ op.drop_column('ps_contacts', 'via_addr')
+ op.drop_column('ps_contacts', 'via_port')
+ op.drop_column('ps_contacts', 'call_id')
diff --git a/contrib/ast-db-manage/config/versions/81b01a191a46_pjsip_add_contact_reg_server.py b/contrib/ast-db-manage/config/versions/81b01a191a46_pjsip_add_contact_reg_server.py
index c25fc7233..0318b9a4f 100644
--- a/contrib/ast-db-manage/config/versions/81b01a191a46_pjsip_add_contact_reg_server.py
+++ b/contrib/ast-db-manage/config/versions/81b01a191a46_pjsip_add_contact_reg_server.py
@@ -16,7 +16,7 @@ import sqlalchemy as sa
def upgrade():
op.add_column('ps_contacts', sa.Column('reg_server', sa.String(20)))
- op.drop_constraint(UniqueConstraint('id'), 'ps_contacts', type_='unique')
+ op.drop_constraint('id', 'ps_contacts', type_='unique')
op.create_unique_constraint('ps_contacts_uq', 'ps_contacts', ['id','reg_server'])
def downgrade():
diff --git a/include/asterisk/multicast_rtp.h b/include/asterisk/multicast_rtp.h
new file mode 100644
index 000000000..c286c1f96
--- /dev/null
+++ b/include/asterisk/multicast_rtp.h
@@ -0,0 +1,58 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2016, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+#ifndef MULTICAST_RTP_H_
+#define MULTICAST_RTP_H_
+struct ast_multicast_rtp_options;
+
+/*!
+ * \brief Create multicast RTP options.
+ *
+ * These are passed to the multicast RTP engine on its creation.
+ *
+ * \param type The type of multicast RTP, either "basic" or "linksys"
+ * \param options Miscellaneous options
+ * \retval NULL Failure
+ * \retval non-NULL success
+ */
+struct ast_multicast_rtp_options *ast_multicast_rtp_create_options(const char *type,
+ const char *options);
+
+/*!
+ * \brief Free multicast RTP options
+ *
+ * This function is NULL-tolerant
+ *
+ * \param mcast_options Options to free
+ */
+void ast_multicast_rtp_free_options(struct ast_multicast_rtp_options *mcast_options);
+
+/*!
+ * \brief Get format specified in multicast options
+ *
+ * Multicast options allow for a format to be selected.
+ * This function accesses the selected format and creates
+ * an ast_format structure for it.
+ *
+ * \param mcast_options The options where a codec was specified
+ * \retval NULL No format specified in the options
+ * \revval non-NULL The format to use for communication
+ */
+struct ast_format *ast_multicast_rtp_options_get_format(struct ast_multicast_rtp_options *mcast_options);
+
+#endif /* MULTICAST_RTP_H_ */
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index cf8c719d5..50d02d980 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -250,6 +250,12 @@ struct ast_sip_contact {
struct ast_sip_endpoint *endpoint;
/*! Asterisk Server name */
AST_STRING_FIELD_EXTENDED(reg_server);
+ /*! IP-address of the Via header in REGISTER request */
+ AST_STRING_FIELD_EXTENDED(via_addr);
+ /* Port of the Via header in REGISTER request */
+ int via_port;
+ /*! Content of the Call-ID header in REGISTER request */
+ AST_STRING_FIELD_EXTENDED(call_id);
};
#define CONTACT_STATUS "contact_status"
@@ -1093,6 +1099,7 @@ struct ast_sip_contact *ast_sip_location_retrieve_contact(const char *contact_na
*/
int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri,
struct timeval expiration_time, const char *path_info, const char *user_agent,
+ const char *via_addr, int via_port, const char *call_id,
struct ast_sip_endpoint *endpoint);
/*!
@@ -1114,6 +1121,7 @@ int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri,
*/
int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri,
struct timeval expiration_time, const char *path_info, const char *user_agent,
+ const char *via_addr, int via_port, const char *call_id,
struct ast_sip_endpoint *endpoint);
/*!
diff --git a/include/asterisk/stasis_app.h b/include/asterisk/stasis_app.h
index 0863f9f98..a73461547 100644
--- a/include/asterisk/stasis_app.h
+++ b/include/asterisk/stasis_app.h
@@ -673,6 +673,18 @@ int stasis_app_control_queue_control(struct stasis_app_control *control,
struct ast_bridge *stasis_app_bridge_create(const char *type, const char *name, const char *id);
/*!
+ * \brief Create an invisible bridge of the specified type.
+ *
+ * \param type The type of bridge to be created
+ * \param name Optional name to give to the bridge
+ * \param id Optional Unique ID to give to the bridge
+ *
+ * \return New bridge.
+ * \return \c NULL on error.
+ */
+struct ast_bridge *stasis_app_bridge_create_invisible(const char *type, const char *name, const char *id);
+
+/*!
* \brief Returns the bridge with the given id.
* \param bridge_id Uniqueid of the bridge.
*
@@ -855,20 +867,23 @@ int stasis_app_channel_unreal_set_internal(struct ast_channel *chan);
*/
int stasis_app_channel_set_internal(struct ast_channel *chan);
-struct ast_dial;
-
/*!
* \brief Dial a channel
* \param control Control for \c res_stasis.
- * \param dial The ast_dial for the outbound channel
+ * \param dialstring The dialstring to pass to the channel driver
+ * \param timeout Optional timeout in milliseconds
*/
-int stasis_app_control_dial(struct stasis_app_control *control, struct ast_dial *dial);
+int stasis_app_control_dial(struct stasis_app_control *control,
+ const char *dialstring, unsigned int timeout);
/*!
- * \brief Get dial structure on a control
+ * \brief Let Stasis app internals shut down
+ *
+ * This is called when res_stasis is unloaded. It ensures that
+ * the Stasis app internals can free any resources they may have
+ * allocated during the time that res_stasis was loaded.
*/
-struct ast_dial *stasis_app_get_dial(struct stasis_app_control *control);
-
+void stasis_app_control_shutdown(void);
/*! @} */
#endif /* _ASTERISK_STASIS_APP_H */
diff --git a/main/bridge_channel.c b/main/bridge_channel.c
index db4ecfe57..6766dff8e 100644
--- a/main/bridge_channel.c
+++ b/main/bridge_channel.c
@@ -2393,6 +2393,14 @@ static struct ast_frame *bridge_handle_dtmf(struct ast_bridge_channel *bridge_ch
return frame;
}
+static const char *controls[] = {
+ [AST_CONTROL_RINGING] = "RINGING",
+ [AST_CONTROL_PROCEEDING] = "PROCEEDING",
+ [AST_CONTROL_PROGRESS] = "PROGRESS",
+ [AST_CONTROL_BUSY] = "BUSY",
+ [AST_CONTROL_CONGESTION] = "CONGESTION",
+ [AST_CONTROL_ANSWER] = "ANSWER",
+};
/*!
* \internal
@@ -2404,6 +2412,17 @@ static void bridge_handle_trip(struct ast_bridge_channel *bridge_channel)
{
struct ast_frame *frame;
+ if (!ast_strlen_zero(ast_channel_call_forward(bridge_channel->chan))) {
+ /* TODO If early bridging is ever used by anything other than ARI,
+ * it's important that we actually attempt to handle the call forward
+ * attempt, as well as expand features on a bridge channel to allow/disallow
+ * call forwarding. For now, all we do is raise an event, showing that
+ * a call forward is being attempted.
+ */
+ ast_channel_publish_dial_forward(NULL, bridge_channel->chan, NULL, NULL, "CANCEL",
+ ast_channel_call_forward(bridge_channel->chan));
+ }
+
if (bridge_channel->features->mute) {
frame = ast_read_noaudio(bridge_channel->chan);
} else {
@@ -2417,10 +2436,20 @@ static void bridge_handle_trip(struct ast_bridge_channel *bridge_channel)
switch (frame->frametype) {
case AST_FRAME_CONTROL:
switch (frame->subclass.integer) {
+ case AST_CONTROL_CONGESTION:
+ case AST_CONTROL_BUSY:
+ ast_channel_publish_dial(NULL, bridge_channel->chan, NULL, controls[frame->subclass.integer]);
+ break;
case AST_CONTROL_HANGUP:
ast_bridge_channel_kick(bridge_channel, 0);
bridge_frame_free(frame);
return;
+ case AST_CONTROL_RINGING:
+ case AST_CONTROL_PROGRESS:
+ case AST_CONTROL_PROCEEDING:
+ case AST_CONTROL_ANSWER:
+ ast_channel_publish_dial(NULL, bridge_channel->chan, NULL, controls[frame->subclass.integer]);
+ break;
default:
break;
}
diff --git a/main/cdr.c b/main/cdr.c
index b6a0b428c..b43e3610c 100644
--- a/main/cdr.c
+++ b/main/cdr.c
@@ -1902,6 +1902,13 @@ static int filter_channel_cache_message(struct ast_channel_snapshot *old_snapsho
return ret;
}
+static int dial_status_end(const char *dialstatus)
+{
+ return (strcmp(dialstatus, "RINGING") &&
+ strcmp(dialstatus, "PROCEEDING") &&
+ strcmp(dialstatus, "PROGRESS"));
+}
+
/* TOPIC ROUTER CALLBACKS */
/*!
@@ -1970,7 +1977,7 @@ static void handle_dial_message(void *data, struct stasis_subscription *sub, str
res &= it_cdr->fn_table->process_dial_begin(it_cdr,
caller,
peer);
- } else {
+ } else if (dial_status_end(dial_status)) {
if (!it_cdr->fn_table->process_dial_end) {
continue;
}
diff --git a/main/dial.c b/main/dial.c
index fc66af5a7..bac191e06 100644
--- a/main/dial.c
+++ b/main/dial.c
@@ -627,11 +627,13 @@ static void handle_frame(struct ast_dial *dial, struct ast_dial_channel *channel
break;
case AST_CONTROL_RINGING:
ast_verb(3, "%s is ringing\n", ast_channel_name(channel->owner));
+ ast_channel_publish_dial(chan, channel->owner, channel->device, "RINGING");
if (chan && !dial->options[AST_DIAL_OPTION_MUSIC])
ast_indicate(chan, AST_CONTROL_RINGING);
set_state(dial, AST_DIAL_RESULT_RINGING);
break;
case AST_CONTROL_PROGRESS:
+ ast_channel_publish_dial(chan, channel->owner, channel->device, "PROGRESS");
if (chan) {
ast_verb(3, "%s is making progress, passing it to %s\n", ast_channel_name(channel->owner), ast_channel_name(chan));
ast_indicate(chan, AST_CONTROL_PROGRESS);
@@ -675,6 +677,7 @@ static void handle_frame(struct ast_dial *dial, struct ast_dial_channel *channel
}
break;
case AST_CONTROL_PROCEEDING:
+ ast_channel_publish_dial(chan, channel->owner, channel->device, "PROCEEDING");
if (chan) {
ast_verb(3, "%s is proceeding, passing it to %s\n", ast_channel_name(channel->owner), ast_channel_name(chan));
ast_indicate(chan, AST_CONTROL_PROCEEDING);
diff --git a/main/logger.c b/main/logger.c
index ae1e0bd8d..9a16dcf13 100644
--- a/main/logger.c
+++ b/main/logger.c
@@ -174,6 +174,7 @@ struct logmsg {
static void logmsg_free(struct logmsg *msg)
{
+ ast_string_field_free_memory(msg);
ast_free(msg);
}
diff --git a/main/manager_channels.c b/main/manager_channels.c
index c395708cc..ec1f807dc 100644
--- a/main/manager_channels.c
+++ b/main/manager_channels.c
@@ -142,6 +142,33 @@ ASTERISK_REGISTER_FILE()
</see-also>
</managerEventInstance>
</managerEvent>
+ <managerEvent language="en_US" name="DialState">
+ <managerEventInstance class="EVENT_FLAG_CALL">
+ <synopsis>Raised when dial status has changed.</synopsis>
+ <syntax>
+ <channel_snapshot/>
+ <channel_snapshot prefix="Dest"/>
+ <parameter name="DialStatus">
+ <para> The new state of the outbound dial attempt.</para>
+ <enumlist>
+ <enum name="RINGING">
+ <para>The outbound channel is ringing.</para>
+ </enum>
+ <enum name="PROCEEDING">
+ <para>The call to the outbound channel is proceeding.</para>
+ </enum>
+ <enum name="PROGRESS">
+ <para>Progress has been received on the outbound channel.</para>
+ </enum>
+ </enumlist>
+ </parameter>
+ <parameter name="Forward" required="false">
+ <para>If the call was forwarded, where the call was
+ forwarded to.</para>
+ </parameter>
+ </syntax>
+ </managerEventInstance>
+ </managerEvent>
<managerEvent language="en_US" name="DialEnd">
<managerEventInstance class="EVENT_FLAG_CALL">
<synopsis>Raised when a dial action has completed.</synopsis>
@@ -1034,6 +1061,13 @@ static void channel_monitor_stop_cb(void *data, struct stasis_subscription *sub,
publish_basic_channel_event("MonitorStop", EVENT_FLAG_CALL, payload->snapshot);
}
+static int dial_status_end(const char *dialstatus)
+{
+ return (strcmp(dialstatus, "RINGING") &&
+ strcmp(dialstatus, "PROCEEDING") &&
+ strcmp(dialstatus, "PROGRESS"));
+}
+
/*!
* \brief Callback processing messages for channel dialing
*/
@@ -1077,7 +1111,7 @@ static void channel_dial_cb(void *data, struct stasis_subscription *sub,
} else {
int forwarded = !ast_strlen_zero(forward);
- manager_event(EVENT_FLAG_CALL, "DialEnd",
+ manager_event(EVENT_FLAG_CALL, dial_status_end(dialstatus) ? "DialEnd" : "DialState",
"%s"
"%s"
"%s%s%s"
diff --git a/main/stasis_channels.c b/main/stasis_channels.c
index e56d1b928..e1c50c6d0 100644
--- a/main/stasis_channels.c
+++ b/main/stasis_channels.c
@@ -362,7 +362,7 @@ static void ast_channel_publish_dial_internal(struct ast_channel *caller,
return;
}
- publish_message_for_channel_topics(msg, caller);
+ publish_message_for_channel_topics(msg, caller ?: peer);
}
static void remove_dial_masquerade(struct ast_channel *peer);
diff --git a/main/stasis_endpoints.c b/main/stasis_endpoints.c
index 14d3d0ca1..2d114210b 100644
--- a/main/stasis_endpoints.c
+++ b/main/stasis_endpoints.c
@@ -104,6 +104,12 @@ ASTERISK_REGISTER_FILE()
<parameter name="RegExpire">
<para>Absolute time that this contact is no longer valid after</para>
</parameter>
+ <parameter name="ViaAddress">
+ <para>IP address:port of the last Via header in REGISTER request</para>
+ </parameter>
+ <parameter name="CallID">
+ <para>Content of the Call-ID header in REGISTER request</para>
+ </parameter>
</syntax>
</managerEventInstance>
</managerEvent>
diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c
index b42581c84..0f18b2dc1 100644
--- a/res/ari/resource_channels.c
+++ b/res/ari/resource_channels.c
@@ -48,6 +48,7 @@ ASTERISK_REGISTER_FILE()
#include "asterisk/format_cache.h"
#include "asterisk/core_local.h"
#include "asterisk/dial.h"
+#include "asterisk/max_forwards.h"
#include "resource_channels.h"
#include <limits.h>
@@ -1503,6 +1504,67 @@ static void *ari_channel_thread(void *data)
return NULL;
}
+struct ast_datastore_info dialstring_info = {
+ .type = "ARI Dialstring",
+ .destroy = ast_free_ptr,
+};
+
+/*!
+ * \brief Save dialstring onto a channel datastore
+ *
+ * This will later be retrieved when it comes time to actually dial the channel
+ *
+ * \param chan The channel on which to save the dialstring
+ * \param dialstring The dialstring to save
+ * \retval 0 SUCCESS!
+ * \reval -1 Failure :(
+ */
+static int save_dialstring(struct ast_channel *chan, const char *dialstring)
+{
+ struct ast_datastore *datastore;
+
+ datastore = ast_datastore_alloc(&dialstring_info, NULL);
+ if (!datastore) {
+ return -1;
+ }
+
+ datastore->data = ast_strdup(dialstring);
+ if (!datastore->data) {
+ ast_datastore_free(datastore);
+ return -1;
+ }
+
+ ast_channel_lock(chan);
+ if (ast_channel_datastore_add(chan, datastore)) {
+ ast_channel_unlock(chan);
+ ast_datastore_free(datastore);
+ return -1;
+ }
+ ast_channel_unlock(chan);
+
+ return 0;
+}
+
+/*!
+ * \brief Retrieve the dialstring from the channel datastore
+ *
+ * \pre chan is locked
+ * \param chan Channel that was previously created in ARI
+ * \retval NULL Failed to find datastore
+ * \retval non-NULL The dialstring
+ */
+static char *restore_dialstring(struct ast_channel *chan)
+{
+ struct ast_datastore *datastore;
+
+ datastore = ast_channel_datastore_find(chan, &dialstring_info, NULL);
+ if (!datastore) {
+ return NULL;
+ }
+
+ return datastore->data;
+}
+
void ast_ari_channels_create(struct ast_variable *headers,
struct ast_ari_channels_create_args *args,
struct ast_ari_response *response)
@@ -1562,6 +1624,12 @@ void ast_ari_channels_create(struct ast_variable *headers,
return;
}
+ if (save_dialstring(chan_data->chan, stuff)) {
+ ast_ari_response_alloc_failed(response);
+ chan_data_destroy(chan_data);
+ return;
+ }
+
snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan_data->chan));
if (ast_pthread_create_detached(&thread, NULL, ari_channel_thread, chan_data)) {
@@ -1580,8 +1648,8 @@ void ast_ari_channels_dial(struct ast_variable *headers,
{
RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
RAII_VAR(struct ast_channel *, caller, NULL, ast_channel_cleanup);
- struct ast_channel *callee;
- struct ast_dial *dial;
+ RAII_VAR(struct ast_channel *, callee, NULL, ast_channel_cleanup);
+ char *dialstring;
control = find_control(response, args->channel_id);
if (control == NULL) {
@@ -1598,43 +1666,64 @@ void ast_ari_channels_dial(struct ast_variable *headers,
return;
}
- if (ast_channel_state(callee) != AST_STATE_DOWN) {
- ast_channel_unref(callee);
+ if (ast_channel_state(callee) != AST_STATE_DOWN
+ && ast_channel_state(callee) != AST_STATE_RESERVED) {
ast_ari_response_error(response, 409, "Conflict",
"Channel is not in the 'Down' state");
return;
}
- dial = ast_dial_create();
- if (!dial) {
- ast_channel_unref(callee);
- ast_ari_response_alloc_failed(response);
- return;
+ /* XXX This is straight up copied from main/dial.c. It's probably good
+ * to separate this to some common method.
+ */
+ if (caller) {
+ ast_channel_lock_both(caller, callee);
+ } else {
+ ast_channel_lock(callee);
}
- if (ast_dial_append_channel(dial, callee) < 0) {
- ast_channel_unref(callee);
- ast_dial_destroy(dial);
- ast_ari_response_alloc_failed(response);
+ dialstring = restore_dialstring(callee);
+ if (!dialstring) {
+ ast_channel_unlock(callee);
+ if (caller) {
+ ast_channel_unlock(caller);
+ }
+ ast_ari_response_error(response, 409, "Conflict",
+ "Dialing a channel not created by ARI");
return;
}
-
- /* From this point, we don't have to unref the callee channel on
- * failure paths because the dial owns the reference to the called
- * channel and will unref the channel for us
+ /* Make a copy of the dialstring just in case some jerk tries to hang up the
+ * channel before we can actually dial
*/
+ dialstring = ast_strdupa(dialstring);
- if (ast_dial_prerun(dial, caller, NULL)) {
- ast_dial_destroy(dial);
- ast_ari_response_alloc_failed(response);
- return;
+ ast_channel_stage_snapshot(callee);
+ if (caller) {
+ ast_channel_inherit_variables(caller, callee);
+ ast_channel_datastore_inherit(caller, callee);
+ ast_max_forwards_decrement(callee);
+
+ /* Copy over callerid information */
+ ast_party_redirecting_copy(ast_channel_redirecting(callee), ast_channel_redirecting(caller));
+
+ ast_channel_dialed(callee)->transit_network_select = ast_channel_dialed(caller)->transit_network_select;
+
+ ast_connected_line_copy_from_caller(ast_channel_connected(callee), ast_channel_caller(caller));
+
+ ast_channel_language_set(callee, ast_channel_language(caller));
+ ast_channel_req_accountcodes(callee, caller, AST_CHANNEL_REQUESTOR_BRIDGE_PEER);
+ if (ast_strlen_zero(ast_channel_musicclass(callee)))
+ ast_channel_musicclass_set(callee, ast_channel_musicclass(caller));
+
+ ast_channel_adsicpe_set(callee, ast_channel_adsicpe(caller));
+ ast_channel_transfercapability_set(callee, ast_channel_transfercapability(caller));
+ ast_channel_unlock(caller);
}
- ast_dial_set_user_data(dial, control);
- ast_dial_set_global_timeout(dial, args->timeout * 1000);
+ ast_channel_stage_snapshot_done(callee);
+ ast_channel_unlock(callee);
- if (stasis_app_control_dial(control, dial)) {
- ast_dial_destroy(dial);
+ if (stasis_app_control_dial(control, dialstring, args->timeout)) {
ast_ari_response_alloc_failed(response);
return;
}
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index bebe941b5..8fc3c530e 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -1175,6 +1175,28 @@
Asterisk Server name on which SIP endpoint registered.
</para></description>
</configOption>
+ <configOption name="via_addr">
+ <synopsis>IP-address of the last Via header from registration.</synopsis>
+ <description><para>
+ The last Via header should contain the address of UA which sent the request.
+ The IP-address of the last Via header is automatically stored based on data present
+ in incoming SIP REGISTER requests and is not intended to be configured manually.
+ </para></description>
+ </configOption>
+ <configOption name="via_port">
+ <synopsis>IP-port of the last Via header from registration.</synopsis>
+ <description><para>
+ The IP-port of the last Via header is automatically stored based on data present
+ in incoming SIP REGISTER requests and is not intended to be configured manually.
+ </para></description>
+ </configOption>
+ <configOption name="call_id">
+ <synopsis>Call-ID header from registration.</synopsis>
+ <description><para>
+ The Call-ID header is automatically stored based on data present
+ in incoming SIP REGISTER requests and is not intended to be configured manually.
+ </para></description>
+ </configOption>
</configObject>
<configObject name="aor">
<synopsis>The configuration for a location of an endpoint</synopsis>
@@ -1967,6 +1989,12 @@
<parameter name="RegExpire">
<para>Absolute time that this contact is no longer valid after</para>
</parameter>
+ <parameter name="ViaAddress">
+ <para>IP address:port of the last Via header in REGISTER request</para>
+ </parameter>
+ <parameter name="CallID">
+ <para>Content of the Call-ID header in REGISTER request</para>
+ </parameter>
</syntax>
</managerEventInstance>
</managerEvent>
diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c
index fd6db6edc..43e6ea40f 100644
--- a/res/res_pjsip/location.c
+++ b/res/res_pjsip/location.c
@@ -121,6 +121,8 @@ static void *contact_alloc(const char *name)
}
ast_string_field_init_extended(contact, reg_server);
+ ast_string_field_init_extended(contact, via_addr);
+ ast_string_field_init_extended(contact, call_id);
/* Dynamic contacts are delimited with ";@" and static ones with "@@" */
if ((aor_separator = strstr(id, ";@")) || (aor_separator = strstr(id, "@@"))) {
@@ -303,6 +305,7 @@ struct ast_sip_contact *ast_sip_location_retrieve_contact(const char *contact_na
int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri,
struct timeval expiration_time, const char *path_info, const char *user_agent,
+ const char *via_addr, int via_port, const char *call_id,
struct ast_sip_endpoint *endpoint)
{
char name[MAX_OBJECT_FIELD * 2 + 3];
@@ -337,6 +340,15 @@ int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri
ast_string_field_set(contact, reg_server, ast_config_AST_SYSTEM_NAME);
}
+ if (!ast_strlen_zero(via_addr)) {
+ ast_string_field_set(contact, via_addr, via_addr);
+ }
+ contact->via_port = via_port;
+
+ if (!ast_strlen_zero(call_id)) {
+ ast_string_field_set(contact, call_id, call_id);
+ }
+
contact->endpoint = ao2_bump(endpoint);
return ast_sorcery_create(ast_sip_get_sorcery(), contact);
@@ -344,6 +356,7 @@ int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri
int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri,
struct timeval expiration_time, const char *path_info, const char *user_agent,
+ const char *via_addr, int via_port, const char *call_id,
struct ast_sip_endpoint *endpoint)
{
int res;
@@ -356,6 +369,7 @@ int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri,
ao2_wrlock(lock);
res = ast_sip_location_add_contact_nolock(aor, uri, expiration_time, path_info, user_agent,
+ via_addr, via_port, call_id,
endpoint);
ao2_unlock(lock);
ast_named_lock_put(lock);
@@ -1120,6 +1134,9 @@ int ast_sip_initialize_sorcery_location(void)
ast_sorcery_object_field_register(sorcery, "contact", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, outbound_proxy));
ast_sorcery_object_field_register(sorcery, "contact", "user_agent", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, user_agent));
ast_sorcery_object_field_register(sorcery, "contact", "reg_server", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, reg_server));
+ ast_sorcery_object_field_register(sorcery, "contact", "via_addr", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, via_addr));
+ ast_sorcery_object_field_register(sorcery, "contact", "via_port", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_contact, via_port));
+ ast_sorcery_object_field_register(sorcery, "contact", "call_id", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, call_id));
ast_sorcery_object_field_register(sorcery, "aor", "type", "", OPT_NOOP_T, 0, 0);
ast_sorcery_object_field_register(sorcery, "aor", "minimum_expiration", "60", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, minimum_expiration));
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 8b6fe61d8..3c4949573 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -131,7 +131,7 @@ static int persistent_endpoint_update_state(void *obj, void *arg, int flags)
}
}
- ast_verb(1, "Endpoint %s is now Reachable\n", ast_endpoint_get_resource(endpoint));
+ ast_verb(2, "Endpoint %s is now Reachable\n", ast_endpoint_get_resource(endpoint));
} else {
ast_endpoint_set_state(endpoint, AST_ENDPOINT_OFFLINE);
blob = ast_json_pack("{s: s}", "peer_status", "Unreachable");
@@ -144,7 +144,7 @@ static int persistent_endpoint_update_state(void *obj, void *arg, int flags)
}
}
- ast_verb(1, "Endpoint %s is now Unreachable\n", ast_endpoint_get_resource(endpoint));
+ ast_verb(2, "Endpoint %s is now Unreachable\n", ast_endpoint_get_resource(endpoint));
}
ast_free(regcontext);
@@ -173,7 +173,7 @@ static void persistent_endpoint_contact_created_observer(const void *object)
contact_status->status = CREATED;
- ast_verb(1, "Contact %s/%s has been created\n",contact->aor, contact->uri);
+ ast_verb(2, "Contact %s/%s has been created\n",contact->aor, contact->uri);
ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, contact_status);
ao2_cleanup(contact_status);
@@ -192,7 +192,7 @@ static void persistent_endpoint_contact_deleted_observer(const void *object)
return;
}
- ast_verb(1, "Contact %s/%s has been deleted\n", contact->aor, contact->uri);
+ ast_verb(2, "Contact %s/%s has been deleted\n", contact->aor, contact->uri);
ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,
"-1", 1.0, ast_sip_get_contact_status_label(contact_status->status));
ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,
@@ -220,7 +220,7 @@ static void persistent_endpoint_contact_status_observer(const void *object)
}
if (contact_status->status != contact_status->last_status) {
- ast_verb(1, "Contact %s/%s is now %s. RTT: %.3f msec\n", contact_status->aor, contact_status->uri,
+ ast_verb(3, "Contact %s/%s is now %s. RTT: %.3f msec\n", contact_status->aor, contact_status->uri,
ast_sip_get_contact_status_label(contact_status->status),
contact_status->rtt / 1000.0);
diff --git a/res/res_pjsip/pjsip_distributor.c b/res/res_pjsip/pjsip_distributor.c
index 2ab954eb0..3867eaea0 100644
--- a/res/res_pjsip/pjsip_distributor.c
+++ b/res/res_pjsip/pjsip_distributor.c
@@ -257,7 +257,8 @@ static pjsip_dialog *find_dialog(pjsip_rx_data *rdata)
tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE);
if (!tsx) {
- ast_debug(3, "Could not find matching transaction for %s\n", rdata->msg_info.info);
+ ast_debug(3, "Could not find matching transaction for %s\n",
+ pjsip_rx_data_get_info(rdata));
return NULL;
}
@@ -296,13 +297,13 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
if (dlg) {
ast_debug(3, "Searching for serializer on dialog %s for %s\n",
- dlg->obj_name, rdata->msg_info.info);
+ dlg->obj_name, pjsip_rx_data_get_info(rdata));
dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id);
if (dist) {
serializer = ao2_bump(dist->serializer);
if (serializer) {
ast_debug(3, "Found serializer %s on dialog %s\n",
- ast_taskprocessor_name(serializer), dlg->obj_name);
+ ast_taskprocessor_name(serializer), dlg->obj_name);
}
}
pjsip_dlg_dec_lock(dlg);
@@ -312,7 +313,7 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
/* We have a serializer so we know where to send the message. */
} else if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG) {
ast_debug(3, "No dialog serializer for response %s. Using request transaction as basis\n",
- rdata->msg_info.info);
+ pjsip_rx_data_get_info(rdata));
serializer = find_request_serializer(rdata);
} else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method)
|| !pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_bye_method)) {
diff --git a/res/res_pjsip/pjsip_options.c b/res/res_pjsip/pjsip_options.c
index 62640fe4e..1114336bd 100644
--- a/res/res_pjsip/pjsip_options.c
+++ b/res/res_pjsip/pjsip_options.c
@@ -1156,6 +1156,16 @@ static int format_contact_status(void *obj, void *arg, int flags)
ast_str_append(&buf, 0, "URI: %s\r\n", contact->uri);
ast_str_append(&buf, 0, "UserAgent: %s\r\n", contact->user_agent);
ast_str_append(&buf, 0, "RegExpire: %ld\r\n", contact->expiration_time.tv_sec);
+ if (!ast_strlen_zero(contact->via_addr)) {
+ ast_str_append(&buf, 0, "ViaAddress: %s", contact->via_addr);
+ if (contact->via_port) {
+ ast_str_append(&buf, 0, ":%d", contact->via_port);
+ }
+ ast_str_append(&buf, 0, "\r\n");
+ }
+ if (!ast_strlen_zero(contact->call_id)) {
+ ast_str_append(&buf, 0, "CallID: %s\r\n", contact->call_id);
+ }
ast_str_append(&buf, 0, "Status: %s\r\n", ast_sip_get_contact_status_label(status->status));
if (status->status == UNKNOWN) {
ast_str_append(&buf, 0, "RoundtripUsec: N/A\r\n");
diff --git a/res/res_pjsip_header_funcs.c b/res/res_pjsip_header_funcs.c
index 7d164b12a..648f1c860 100644
--- a/res/res_pjsip_header_funcs.c
+++ b/res/res_pjsip_header_funcs.c
@@ -39,7 +39,8 @@
/*** DOCUMENTATION
<function name="PJSIP_HEADER" language="en_US">
<synopsis>
- Gets, adds, updates or removes the specified SIP header from a PJSIP session.
+ Gets headers from an inbound PJSIP channel. Adds, updates or removes the
+ specified SIP header from an outbound PJSIP channel.
</synopsis>
<syntax>
<parameter name="action" required="true">
@@ -75,6 +76,10 @@
</syntax>
<description>
+ <para>PJSIP_HEADER allows you to read specific SIP headers from the inbound
+ PJSIP channel as well as write(add, update, remove) headers on the outbound
+ channel. One exception is that you can read headers that you have already
+ added on the outbound channel.</para>
<para>Examples:</para>
<para>;</para>
<para>; Set 'somevar' to the value of the 'From' header.</para>
@@ -120,7 +125,7 @@
<note><para>If you call PJSIP_HEADER in a normal dialplan context you'll be
operating on the <emphasis>caller's (incoming)</emphasis> channel which
- may not be what you want. To operate on the <emphasis>callee's (outgoing)</emphasis>
+ may not be what you want. To operate on the <emphasis>callee's (outgoing)</emphasis>
channel call PJSIP_HEADER in a pre-dial handler. </para>
<para>Example:</para>
<para>;</para>
diff --git a/res/res_pjsip_mwi_body_generator.c b/res/res_pjsip_mwi_body_generator.c
index f46ce04e3..e8279101c 100644
--- a/res/res_pjsip_mwi_body_generator.c
+++ b/res/res_pjsip_mwi_body_generator.c
@@ -61,11 +61,11 @@ static int mwi_generate_body_content(void *body, void *data)
ast_str_append(mwi, 0, "Messages-Waiting: %s\r\n",
counter->new_msgs ? "yes" : "no");
- ast_str_append(mwi, 0, "Voice-Message: %d/%d (0/0)\r\n",
- counter->new_msgs, counter->old_msgs);
if (!ast_strlen_zero(counter->message_account)) {
ast_str_append(mwi, 0, "Message-Account: %s\r\n", counter->message_account);
}
+ ast_str_append(mwi, 0, "Voice-Message: %d/%d (0/0)\r\n",
+ counter->new_msgs, counter->old_msgs);
return 0;
}
diff --git a/res/res_pjsip_outbound_publish.c b/res/res_pjsip_outbound_publish.c
index 1c3b0c644..53e15a0a4 100644
--- a/res/res_pjsip_outbound_publish.c
+++ b/res/res_pjsip_outbound_publish.c
@@ -1125,6 +1125,8 @@ static int explicit_publish_destroy(void *data)
ao2_ref(publisher, -1);
}
+ ao2_ref(publisher, -1);
+
return 0;
}
@@ -1140,7 +1142,9 @@ static int cancel_and_unpublish(void *obj, void *arg, int flags)
/* If the publisher was never started, there's nothing to unpublish, so just
* destroy the publication and remove its reference to the publisher.
*/
- ast_sip_push_task(NULL, explicit_publish_destroy, publisher);
+ if (ast_sip_push_task(NULL, explicit_publish_destroy, ao2_bump(publisher))) {
+ ao2_ref(publisher, -1);
+ }
return 0;
}
diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c
index cbc33ab80..0e14ab786 100644
--- a/res/res_pjsip_registrar.c
+++ b/res/res_pjsip_registrar.c
@@ -447,6 +447,13 @@ static int rx_task_core(struct rx_task_data *task_data, struct ao2_container *co
char *user_agent = NULL;
pjsip_user_agent_hdr *user_agent_hdr;
pjsip_expires_hdr *expires_hdr;
+ pjsip_via_hdr *via_hdr;
+ pjsip_via_hdr *via_hdr_last;
+ char *via_addr = NULL;
+ int via_port = 0;
+ pjsip_cid_hdr *call_id_hdr;
+ char *call_id = NULL;
+ size_t alloc_size;
/* So we don't count static contacts against max_contacts we prune them out from the container */
ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, registrar_prune_static, NULL);
@@ -484,11 +491,32 @@ static int rx_task_core(struct rx_task_data *task_data, struct ao2_container *co
user_agent_hdr = pjsip_msg_find_hdr_by_name(task_data->rdata->msg_info.msg, &USER_AGENT, NULL);
if (user_agent_hdr) {
- size_t alloc_size = pj_strlen(&user_agent_hdr->hvalue) + 1;
+ alloc_size = pj_strlen(&user_agent_hdr->hvalue) + 1;
user_agent = ast_alloca(alloc_size);
ast_copy_pj_str(user_agent, &user_agent_hdr->hvalue, alloc_size);
}
+ /* Find the first Via header */
+ via_hdr = via_hdr_last = (pjsip_via_hdr*) pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_VIA, NULL);
+ if (via_hdr) {
+ /* Find the last Via header */
+ while ( (via_hdr = (pjsip_via_hdr*) pjsip_msg_find_hdr(task_data->rdata->msg_info.msg,
+ PJSIP_H_VIA, via_hdr->next)) != NULL) {
+ via_hdr_last = via_hdr;
+ }
+ alloc_size = pj_strlen(&via_hdr_last->sent_by.host) + 1;
+ via_addr = ast_alloca(alloc_size);
+ ast_copy_pj_str(via_addr, &via_hdr_last->sent_by.host, alloc_size);
+ via_port=via_hdr_last->sent_by.port;
+ }
+
+ call_id_hdr = (pjsip_cid_hdr*) pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_CALL_ID, NULL);
+ if (call_id_hdr) {
+ alloc_size = pj_strlen(&call_id_hdr->id) + 1;
+ call_id = ast_alloca(alloc_size);
+ ast_copy_pj_str(call_id, &call_id_hdr->id, alloc_size);
+ }
+
/* Iterate each provided Contact header and add, update, or delete */
while ((contact_hdr = pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr ? contact_hdr->next : NULL))) {
int expiration;
@@ -520,7 +548,7 @@ static int rx_task_core(struct rx_task_data *task_data, struct ao2_container *co
if (ast_sip_location_add_contact_nolock(task_data->aor, contact_uri, ast_tvadd(ast_tvnow(),
ast_samp2tv(expiration, 1)), path_str ? ast_str_buffer(path_str) : NULL,
- user_agent, task_data->endpoint)) {
+ user_agent, via_addr, via_port, call_id, task_data->endpoint)) {
ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n",
contact_uri, aor_name);
continue;
diff --git a/res/res_rtp_multicast.c b/res/res_rtp_multicast.c
index 192f3d137..5c419d3e7 100644
--- a/res/res_rtp_multicast.c
+++ b/res/res_rtp_multicast.c
@@ -54,6 +54,8 @@ ASTERISK_REGISTER_FILE()
#include "asterisk/module.h"
#include "asterisk/rtp_engine.h"
#include "asterisk/format_cache.h"
+#include "asterisk/multicast_rtp.h"
+#include "asterisk/app.h"
/*! Command value used for Linksys paging to indicate we are starting */
#define LINKSYS_MCAST_STARTCMD 6
@@ -63,8 +65,10 @@ ASTERISK_REGISTER_FILE()
/*! \brief Type of paging to do */
enum multicast_type {
+ /*! Type has not been set yet */
+ MULTICAST_TYPE_UNSPECIFIED = 0,
/*! Simple multicast enabled client/receiver paging like Snom and Barix uses */
- MULTICAST_TYPE_BASIC = 0,
+ MULTICAST_TYPE_BASIC,
/*! More advanced Linksys type paging which requires a start and stop packet */
MULTICAST_TYPE_LINKSYS,
};
@@ -95,6 +99,91 @@ struct multicast_rtp {
struct timeval txcore;
};
+enum {
+ OPT_CODEC = (1 << 0),
+ OPT_LOOP = (1 << 1),
+ OPT_TTL = (1 << 2),
+ OPT_IF = (1 << 3),
+};
+
+enum {
+ OPT_ARG_CODEC = 0,
+ OPT_ARG_LOOP,
+ OPT_ARG_TTL,
+ OPT_ARG_IF,
+ OPT_ARG_ARRAY_SIZE,
+};
+
+AST_APP_OPTIONS(multicast_rtp_options, BEGIN_OPTIONS
+ /*! Set the codec to be used for multicast RTP */
+ AST_APP_OPTION_ARG('c', OPT_CODEC, OPT_ARG_CODEC),
+ /*! Set whether multicast RTP is looped back to the sender */
+ AST_APP_OPTION_ARG('l', OPT_LOOP, OPT_ARG_LOOP),
+ /*! Set the hop count for multicast RTP */
+ AST_APP_OPTION_ARG('t', OPT_TTL, OPT_ARG_TTL),
+ /*! Set the interface from which multicast RTP is sent */
+ AST_APP_OPTION_ARG('i', OPT_IF, OPT_ARG_IF),
+END_OPTIONS );
+
+struct ast_multicast_rtp_options {
+ char *type;
+ char *options;
+ struct ast_format *fmt;
+ struct ast_flags opts;
+ char *opt_args[OPT_ARG_ARRAY_SIZE];
+ /*! The type and options are stored in this buffer */
+ char buf[0];
+};
+
+struct ast_multicast_rtp_options *ast_multicast_rtp_create_options(const char *type,
+ const char *options)
+{
+ struct ast_multicast_rtp_options *mcast_options;
+ char *pos;
+
+ mcast_options = ast_calloc(1, sizeof(*mcast_options)
+ + strlen(type)
+ + strlen(options) + 2);
+ if (!mcast_options) {
+ return NULL;
+ }
+
+ pos = mcast_options->buf;
+
+ /* Safe */
+ strcpy(pos, type);
+ mcast_options->type = pos;
+ pos += strlen(type) + 1;
+
+ /* Safe */
+ strcpy(pos, options);
+ mcast_options->options = pos;
+
+ if (ast_app_parse_options(multicast_rtp_options, &mcast_options->opts,
+ mcast_options->opt_args, mcast_options->options)) {
+ ast_log(LOG_WARNING, "Error parsing multicast RTP options\n");
+ ast_multicast_rtp_free_options(mcast_options);
+ return NULL;
+ }
+
+ return mcast_options;
+}
+
+void ast_multicast_rtp_free_options(struct ast_multicast_rtp_options *mcast_options)
+{
+ ast_free(mcast_options);
+}
+
+struct ast_format *ast_multicast_rtp_options_get_format(struct ast_multicast_rtp_options *mcast_options)
+{
+ if (ast_test_flag(&mcast_options->opts, OPT_CODEC)
+ && !ast_strlen_zero(mcast_options->opt_args[OPT_ARG_CODEC])) {
+ return ast_format_cache_get(mcast_options->opt_args[OPT_ARG_CODEC]);
+ }
+
+ return NULL;
+}
+
/* Forward Declarations */
static int multicast_rtp_new(struct ast_rtp_instance *instance, struct ast_sched_context *sched, struct ast_sockaddr *addr, void *data);
static int multicast_rtp_activate(struct ast_rtp_instance *instance);
@@ -112,21 +201,93 @@ static struct ast_rtp_engine multicast_rtp_engine = {
.read = multicast_rtp_read,
};
+static int set_type(struct multicast_rtp *multicast, const char *type)
+{
+ if (!strcasecmp(type, "basic")) {
+ multicast->type = MULTICAST_TYPE_BASIC;
+ } else if (!strcasecmp(type, "linksys")) {
+ multicast->type = MULTICAST_TYPE_LINKSYS;
+ } else {
+ ast_log(LOG_WARNING, "Unrecognized multicast type '%s' specified.\n", type);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void set_ttl(int sock, const char *ttl_str)
+{
+ int ttl;
+
+ if (ast_strlen_zero(ttl_str)) {
+ return;
+ }
+
+ ast_debug(3, "Setting multicast TTL to %s\n", ttl_str);
+
+ if (sscanf(ttl_str, "%30d", &ttl) < 1) {
+ ast_log(LOG_WARNING, "Inavlid multicast ttl option '%s'\n", ttl_str);
+ return;
+ }
+
+ if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
+ ast_log(LOG_WARNING, "Could not set multicast ttl to '%s': %s\n",
+ ttl_str, strerror(errno));
+ }
+}
+
+static void set_loop(int sock, const char *loop_str)
+{
+ unsigned char loop;
+
+ if (ast_strlen_zero(loop_str)) {
+ return;
+ }
+
+ ast_debug(3, "Setting multicast loop to %s\n", loop_str);
+
+ if (sscanf(loop_str, "%30hhu", &loop) < 1) {
+ ast_log(LOG_WARNING, "Invalid multicast loop option '%s'\n", loop_str);
+ return;
+ }
+
+ if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) < 0) {
+ ast_log(LOG_WARNING, "Could not set multicast loop to '%s': %s\n",
+ loop_str, strerror(errno));
+ }
+}
+
+static void set_if(int sock, const char *if_str)
+{
+ struct in_addr iface;
+
+ if (ast_strlen_zero(if_str)) {
+ return;
+ }
+
+ ast_debug(3, "Setting multicast if to %s\n", if_str);
+
+ if (!inet_aton(if_str, &iface)) {
+ ast_log(LOG_WARNING, "Cannot parse if option '%s'\n", if_str);
+ }
+
+ if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &iface, sizeof(iface)) < 0) {
+ ast_log(LOG_WARNING, "Could not set multicast if to '%s': %s\n",
+ if_str, strerror(errno));
+ }
+}
+
/*! \brief Function called to create a new multicast instance */
static int multicast_rtp_new(struct ast_rtp_instance *instance, struct ast_sched_context *sched, struct ast_sockaddr *addr, void *data)
{
struct multicast_rtp *multicast;
- const char *type = data;
+ struct ast_multicast_rtp_options *mcast_options = data;
if (!(multicast = ast_calloc(1, sizeof(*multicast)))) {
return -1;
}
- if (!strcasecmp(type, "basic")) {
- multicast->type = MULTICAST_TYPE_BASIC;
- } else if (!strcasecmp(type, "linksys")) {
- multicast->type = MULTICAST_TYPE_LINKSYS;
- } else {
+ if (set_type(multicast, mcast_options->type)) {
ast_free(multicast);
return -1;
}
@@ -136,6 +297,18 @@ static int multicast_rtp_new(struct ast_rtp_instance *instance, struct ast_sched
return -1;
}
+ if (ast_test_flag(&mcast_options->opts, OPT_LOOP)) {
+ set_loop(multicast->socket, mcast_options->opt_args[OPT_ARG_LOOP]);
+ }
+
+ if (ast_test_flag(&mcast_options->opts, OPT_TTL)) {
+ set_ttl(multicast->socket, mcast_options->opt_args[OPT_ARG_TTL]);
+ }
+
+ if (ast_test_flag(&mcast_options->opts, OPT_IF)) {
+ set_if(multicast->socket, mcast_options->opt_args[OPT_ARG_IF]);
+ }
+
multicast->ssrc = ast_random();
ast_rtp_instance_set_data(instance, multicast);
@@ -316,7 +489,7 @@ static int unload_module(void)
return 0;
}
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Multicast RTP Engine",
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Multicast RTP Engine",
.support_level = AST_MODULE_SUPPORT_CORE,
.load = load_module,
.unload = unload_module,
diff --git a/res/res_rtp_multicast.exports.in b/res/res_rtp_multicast.exports.in
new file mode 100644
index 000000000..995a1802e
--- /dev/null
+++ b/res/res_rtp_multicast.exports.in
@@ -0,0 +1,6 @@
+{
+ global:
+ LINKER_SYMBOL_PREFIXast_multicast_rtp*;
+ local:
+ *;
+};
diff --git a/res/res_stasis.c b/res/res_stasis.c
index 346be563c..e7e6bcaa3 100644
--- a/res/res_stasis.c
+++ b/res/res_stasis.c
@@ -749,7 +749,7 @@ static void control_unlink(struct stasis_app_control *control)
ao2_cleanup(control);
}
-struct ast_bridge *stasis_app_bridge_create(const char *type, const char *name, const char *id)
+static struct ast_bridge *bridge_create_common(const char *type, const char *name, const char *id, int invisible)
{
struct ast_bridge *bridge;
char *requested_type, *requested_types = ast_strdupa(S_OR(type, "mixing"));
@@ -758,6 +758,10 @@ struct ast_bridge *stasis_app_bridge_create(const char *type, const char *name,
| AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_SWAP_INHIBIT_TO
| AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY;
+ if (invisible) {
+ flags |= AST_BRIDGE_FLAG_INVISIBLE;
+ }
+
while ((requested_type = strsep(&requested_types, ","))) {
requested_type = ast_strip(requested_type);
@@ -789,6 +793,16 @@ struct ast_bridge *stasis_app_bridge_create(const char *type, const char *name,
return bridge;
}
+struct ast_bridge *stasis_app_bridge_create(const char *type, const char *name, const char *id)
+{
+ return bridge_create_common(type, name, id, 0);
+}
+
+struct ast_bridge *stasis_app_bridge_create_invisible(const char *type, const char *name, const char *id)
+{
+ return bridge_create_common(type, name, id, 1);
+}
+
void stasis_app_bridge_destroy(const char *bridge_id)
{
struct ast_bridge *bridge = stasis_app_bridge_find_by_id(bridge_id);
@@ -1287,7 +1301,6 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
int r;
int command_count;
RAII_VAR(struct ast_bridge *, last_bridge, NULL, ao2_cleanup);
- struct ast_dial *dial;
/* Check to see if a bridge absorbed our hangup frame */
if (ast_check_hangup_locked(chan)) {
@@ -1297,7 +1310,6 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
last_bridge = bridge;
bridge = ao2_bump(stasis_app_get_bridge(control));
- dial = stasis_app_get_dial(control);
if (bridge != last_bridge) {
app_unsubscribe_bridge(app, last_bridge);
@@ -1306,7 +1318,7 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
}
}
- if (bridge || dial) {
+ if (bridge) {
/* Bridge/dial is handling channel frames */
control_wait(control);
control_dispatch_all(control, chan);
@@ -1951,6 +1963,8 @@ static int unload_module(void)
ao2_cleanup(app_bridges_playback);
app_bridges_playback = NULL;
+ stasis_app_control_shutdown();
+
STASIS_MESSAGE_TYPE_CLEANUP(end_message_type);
STASIS_MESSAGE_TYPE_CLEANUP(start_message_type);
diff --git a/res/stasis/control.c b/res/stasis/control.c
index aa6866aee..b255477bf 100644
--- a/res/stasis/control.c
+++ b/res/stasis/control.c
@@ -28,6 +28,7 @@
ASTERISK_REGISTER_FILE()
#include "asterisk/stasis_channels.h"
+#include "asterisk/stasis_app.h"
#include "command.h"
#include "control.h"
@@ -43,6 +44,11 @@ ASTERISK_REGISTER_FILE()
AST_LIST_HEAD(app_control_rules, stasis_app_control_rule);
+/*!
+ * \brief Indicates if the Stasis app internals are being shut down
+ */
+static int shutting_down;
+
struct stasis_app_control {
ast_cond_t wait_cond;
/*! Queue of commands to dispatch on the channel */
@@ -78,10 +84,6 @@ struct stasis_app_control {
*/
struct stasis_app *app;
/*!
- * If channel is being dialed, the dial structure.
- */
- struct ast_dial *dial;
- /*!
* When set, /c app_stasis should exit and continue in the dialplan.
*/
int is_done:1;
@@ -825,6 +827,128 @@ struct ast_bridge *stasis_app_get_bridge(struct stasis_app_control *control)
}
}
+/*!
+ * \brief Singleton dial bridge
+ *
+ * The dial bridge is a holding bridge used to hold all
+ * outbound dialed channels that are not in any "real" ARI-created
+ * bridge. The dial bridge is invisible, meaning that it does not
+ * show up in channel snapshots, AMI or ARI output, and no events
+ * get raised for it.
+ *
+ * This is used to keep dialed channels confined to the bridging system
+ * and unify the threading model used for dialing outbound channels.
+ */
+static struct ast_bridge *dial_bridge;
+AST_MUTEX_DEFINE_STATIC(dial_bridge_lock);
+
+/*!
+ * \brief Retrieve a reference to the dial bridge.
+ *
+ * If the dial bridge has not been created yet, it will
+ * be created, otherwise, a reference to the existing bridge
+ * will be returned.
+ *
+ * The caller will need to unreference the dial bridge once
+ * they are finished with it.
+ *
+ * \retval NULL Unable to find/create the dial bridge
+ * \retval non-NULL A reference to teh dial bridge
+ */
+static struct ast_bridge *get_dial_bridge(void)
+{
+ struct ast_bridge *ret_bridge = NULL;
+
+ ast_mutex_lock(&dial_bridge_lock);
+
+ if (shutting_down) {
+ goto end;
+ }
+
+ if (dial_bridge) {
+ ret_bridge = ao2_bump(dial_bridge);
+ goto end;
+ }
+
+ dial_bridge = stasis_app_bridge_create_invisible("holding", "dial_bridge", NULL);
+ if (!dial_bridge) {
+ goto end;
+ }
+ ret_bridge = ao2_bump(dial_bridge);
+
+end:
+ ast_mutex_unlock(&dial_bridge_lock);
+ return ret_bridge;
+}
+
+/*!
+ * \brief after bridge callback for the dial bridge
+ *
+ * The only purpose of this callback is to ensure that the control structure's
+ * bridge pointer is NULLed
+ */
+static void dial_bridge_after_cb(struct ast_channel *chan, void *data)
+{
+ struct stasis_app_control *control = data;
+
+ control->bridge = NULL;
+}
+
+static void dial_bridge_after_cb_failed(enum ast_bridge_after_cb_reason reason, void *data)
+{
+ struct stasis_app_control *control = data;
+
+ dial_bridge_after_cb(control->channel, data);
+}
+
+/*!
+ * \brief Add a channel to the singleton dial bridge.
+ *
+ * \param control The Stasis control structure
+ * \param chan The channel to add to the bridge
+ * \retval -1 Failed
+ * \retval 0 Success
+ */
+static int add_to_dial_bridge(struct stasis_app_control *control, struct ast_channel *chan)
+{
+ struct ast_bridge *bridge;
+
+ bridge = get_dial_bridge();
+ if (!bridge) {
+ return -1;
+ }
+
+ control->bridge = bridge;
+ ast_bridge_set_after_callback(chan, dial_bridge_after_cb, dial_bridge_after_cb_failed, control);
+ if (ast_bridge_impart(bridge, chan, NULL, NULL, AST_BRIDGE_IMPART_CHAN_DEPARTABLE)) {
+ control->bridge = NULL;
+ ao2_ref(bridge, -1);
+ return -1;
+ }
+
+ ao2_ref(bridge, -1);
+
+ return 0;
+}
+
+/*!
+ * \brief Depart a channel from a bridge, and potentially add it back to the dial bridge
+ *
+ * \param control Take a guess
+ * \param chan Take another guess
+ */
+static int depart_channel(struct stasis_app_control *control, struct ast_channel *chan)
+{
+ ast_bridge_depart(chan);
+
+ if (!ast_check_hangup(chan) && ast_channel_state(chan) != AST_STATE_UP) {
+ /* Channel is still being dialed, so put it back in the dialing bridge */
+ add_to_dial_bridge(control, chan);
+ }
+
+ return 0;
+}
+
static int bridge_channel_depart(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
@@ -843,7 +967,7 @@ static int bridge_channel_depart(struct stasis_app_control *control,
ast_debug(3, "%s: Channel departing bridge\n",
ast_channel_uniqueid(chan));
- ast_bridge_depart(chan);
+ depart_channel(control, chan);
return 0;
}
@@ -903,6 +1027,107 @@ static void bridge_after_cb_failed(enum ast_bridge_after_cb_reason reason,
ast_bridge_after_cb_reason_string(reason));
}
+/*!
+ * \brief Dial timeout datastore
+ *
+ * A datastore is used because a channel may change
+ * bridges during the course of a dial attempt. This
+ * may be because the channel changes from the dial bridge
+ * to a standard bridge, or it may move between standard
+ * bridges. In order to keep the dial timeout, we need
+ * to keep the timeout information local to the channel.
+ * That is what this datastore is for
+ */
+struct ast_datastore_info timeout_datastore = {
+ .type = "ARI dial timeout",
+};
+
+static int hangup_channel(struct stasis_app_control *control,
+ struct ast_channel *chan, void *data)
+{
+ ast_softhangup(chan, AST_SOFTHANGUP_EXPLICIT);
+ return 0;
+}
+
+/*!
+ * \brief Dial timeout
+ *
+ * This is a bridge interval hook callback. The interval hook triggering
+ * means that the dial timeout has been reached. If the channel has not
+ * been answered by the time this callback is called, then the channel
+ * is hung up
+ *
+ * \param bridge_channel Bridge channel on which interval hook has been called
+ * \param ignore Ignored
+ * \return -1 (i.e. remove the interval hook)
+ */
+static int bridge_timeout(struct ast_bridge_channel *bridge_channel, void *ignore)
+{
+ struct ast_datastore *datastore;
+ RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
+
+ control = stasis_app_control_find_by_channel(bridge_channel->chan);
+
+ ast_channel_lock(bridge_channel->chan);
+ if (ast_channel_state(bridge_channel->chan) != AST_STATE_UP) {
+ /* Don't bother removing the datastore because it will happen when the channel is hung up */
+ ast_channel_unlock(bridge_channel->chan);
+ stasis_app_send_command_async(control, hangup_channel, NULL, NULL);
+ return -1;
+ }
+
+ datastore = ast_channel_datastore_find(bridge_channel->chan, &timeout_datastore, NULL);
+ if (!datastore) {
+ ast_channel_unlock(bridge_channel->chan);
+ return -1;
+ }
+ ast_channel_datastore_remove(bridge_channel->chan, datastore);
+ ast_channel_unlock(bridge_channel->chan);
+ ast_datastore_free(datastore);
+
+ return -1;
+}
+
+/*!
+ * \brief Set a dial timeout interval hook on the channel.
+ *
+ * The absolute time that the timeout should occur is stored on
+ * a datastore on the channel. This time is converted into a relative
+ * number of milliseconds in the future. Then an interval hook is set
+ * to trigger in that number of milliseconds.
+ *
+ * \pre chan is locked
+ *
+ * \param chan The channel on which to set the interval hook
+ */
+static void set_interval_hook(struct ast_channel *chan)
+{
+ struct ast_datastore *datastore;
+ struct timeval *hangup_time;
+ int64_t ms;
+ struct ast_bridge_channel *bridge_channel;
+
+ datastore = ast_channel_datastore_find(chan, &timeout_datastore, NULL);
+ if (!datastore) {
+ return;
+ }
+
+ hangup_time = datastore->data;
+
+ ms = ast_tvdiff_ms(*hangup_time, ast_tvnow());
+ bridge_channel = ast_channel_get_bridge_channel(chan);
+ if (!bridge_channel) {
+ return;
+ }
+
+ if (ast_bridge_interval_hook(bridge_channel->features, 0, ms > 0 ? ms : 1,
+ bridge_timeout, NULL, NULL, 0)) {
+ return;
+ }
+
+ ast_queue_frame(bridge_channel->chan, &ast_null_frame);
+}
+
int control_swap_channel_in_bridge(struct stasis_app_control *control, struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap)
{
int res;
@@ -969,6 +1194,10 @@ int control_swap_channel_in_bridge(struct stasis_app_control *control, struct as
ast_assert(stasis_app_get_bridge(control) == NULL);
control->bridge = bridge;
+
+ ast_channel_lock(chan);
+ set_interval_hook(chan);
+ ast_channel_unlock(chan);
}
return 0;
}
@@ -1011,7 +1240,7 @@ static int app_control_remove_channel_from_bridge(
return -1;
}
- ast_bridge_depart(chan);
+ depart_channel(control, chan);
return 0;
}
@@ -1132,83 +1361,110 @@ struct stasis_app *control_app(struct stasis_app_control *control)
return control->app;
}
-static void app_control_dial_destroy(void *data)
+struct control_dial_args {
+ unsigned int timeout;
+ char dialstring[0];
+};
+
+static struct control_dial_args *control_dial_args_alloc(const char *dialstring,
+ unsigned int timeout)
{
- struct ast_dial *dial = data;
+ struct control_dial_args *args;
+
+ args = ast_malloc(sizeof(*args) + strlen(dialstring) + 1);
+ if (!args) {
+ return NULL;
+ }
+
+ args->timeout = timeout;
+ /* Safe */
+ strcpy(args->dialstring, dialstring);
- ast_dial_join(dial);
- ast_dial_destroy(dial);
+ return args;
}
-static int app_control_remove_dial(struct stasis_app_control *control,
- struct ast_channel *chan, void *data)
+static void control_dial_args_destroy(void *data)
{
- if (ast_dial_state(control->dial) != AST_DIAL_RESULT_ANSWERED) {
- ast_softhangup(chan, AST_SOFTHANGUP_EXPLICIT);
- }
- control->dial = NULL;
- return 0;
+ struct control_dial_args *args = data;
+
+ ast_free(args);
}
-static void on_dial_state(struct ast_dial *dial)
+/*!
+ * \brief Set dial timeout on a channel to be dialed.
+ *
+ * \param chan The channel on which to set the dial timeout
+ * \param timeout The timeout in seconds
+ */
+static int set_timeout(struct ast_channel *chan, unsigned int timeout)
{
- enum ast_dial_result state;
- struct stasis_app_control *control;
- struct ast_channel *chan;
+ struct ast_datastore *datastore;
+ struct timeval *hangup_time;
- state = ast_dial_state(dial);
- control = ast_dial_get_user_data(dial);
+ hangup_time = ast_malloc(sizeof(struct timeval));
- switch (state) {
- case AST_DIAL_RESULT_ANSWERED:
- /* Need to steal the reference to the answered channel so that dial doesn't
- * try to hang it up when we destroy the dial structure.
- */
- chan = ast_dial_answered_steal(dial);
- ast_channel_unref(chan);
- /* Fall through intentionally */
- case AST_DIAL_RESULT_INVALID:
- case AST_DIAL_RESULT_FAILED:
- case AST_DIAL_RESULT_TIMEOUT:
- case AST_DIAL_RESULT_HANGUP:
- case AST_DIAL_RESULT_UNANSWERED:
- /* The dial has completed, so we need to break the Stasis loop so
- * that the channel's frames are handled in the proper place now.
- */
- stasis_app_send_command_async(control, app_control_remove_dial, dial, app_control_dial_destroy);
- break;
- case AST_DIAL_RESULT_TRYING:
- case AST_DIAL_RESULT_RINGING:
- case AST_DIAL_RESULT_PROGRESS:
- case AST_DIAL_RESULT_PROCEEDING:
- break;
+ datastore = ast_datastore_alloc(&timeout_datastore, NULL);
+ if (!datastore) {
+ return -1;
+ }
+ *hangup_time = ast_tvadd(ast_tvnow(), ast_samp2tv(timeout, 1));
+ datastore->data = hangup_time;
+
+ ast_channel_lock(chan);
+ ast_channel_datastore_add(chan, datastore);
+
+ if (ast_channel_is_bridged(chan)) {
+ set_interval_hook(chan);
}
+ ast_channel_unlock(chan);
+
+ return 0;
}
static int app_control_dial(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
- struct ast_dial *dial = data;
+ struct control_dial_args *args = data;
+ int bridged;
- ast_dial_set_state_callback(dial, on_dial_state);
- /* The dial API gives the option of providing a caller channel, but for
- * Stasis, we really don't want to do that. The Dial API will take liberties such
- * as passing frames along to the calling channel (think ringing, progress, etc.).
- * This is not desirable in ARI applications since application writers should have
- * control over what does/does not get indicated to the calling channel
- */
- ast_dial_run(dial, NULL, 1);
- control->dial = dial;
+ ast_channel_lock(chan);
+ bridged = ast_channel_is_bridged(chan);
+ ast_channel_unlock(chan);
+
+ if (!bridged && add_to_dial_bridge(control, chan)) {
+ return -1;
+ }
+
+ if (args->timeout && set_timeout(chan, args->timeout)) {
+ return -1;
+ }
+
+ if (ast_call(chan, args->dialstring, 0)) {
+ return -1;
+ }
return 0;
}
-struct ast_dial *stasis_app_get_dial(struct stasis_app_control *control)
+int stasis_app_control_dial(struct stasis_app_control *control,
+ const char *dialstring, unsigned int timeout)
{
- return control->dial;
+ struct control_dial_args *args;
+
+ args = control_dial_args_alloc(dialstring, timeout);
+ if (!args) {
+ return -1;
+ }
+
+ return stasis_app_send_command_async(control, app_control_dial,
+ args, control_dial_args_destroy);
}
-int stasis_app_control_dial(struct stasis_app_control *control, struct ast_dial *dial)
+void stasis_app_control_shutdown(void)
{
- return stasis_app_send_command_async(control, app_control_dial, dial, NULL);
+ ast_mutex_lock(&dial_bridge_lock);
+ shutting_down = 1;
+ ao2_cleanup(dial_bridge);
+ dial_bridge = NULL;
+ ast_mutex_unlock(&dial_bridge_lock);
}
diff --git a/third-party/pjproject/patches/0001-2.4.5-fix-for-tls-async-ops.patch b/third-party/pjproject/patches/0001-2.4.5-fix-for-tls-async-ops.patch
deleted file mode 100644
index 33fc8ea4e..000000000
--- a/third-party/pjproject/patches/0001-2.4.5-fix-for-tls-async-ops.patch
+++ /dev/null
@@ -1,224 +0,0 @@
-diff --git a/pjlib/include/pj/ssl_sock.h b/pjlib/include/pj/ssl_sock.h
-index 1682bda..a69af32 100644
---- a/pjlib/include/pj/ssl_sock.h
-+++ b/pjlib/include/pj/ssl_sock.h
-@@ -864,6 +864,18 @@ PJ_DECL(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param);
-
-
- /**
-+ * Duplicate pj_ssl_sock_param.
-+ *
-+ * @param pool Pool to allocate memory.
-+ * @param dst Destination parameter.
-+ * @param src Source parameter.
-+ */
-+PJ_DECL(void) pj_ssl_sock_param_copy(pj_pool_t *pool,
-+ pj_ssl_sock_param *dst,
-+ const pj_ssl_sock_param *src);
-+
-+
-+/**
- * Create secure socket instance.
- *
- * @param pool The pool for allocating secure socket instance.
-@@ -1115,6 +1127,30 @@ PJ_DECL(pj_status_t) pj_ssl_sock_start_accept(pj_ssl_sock_t *ssock,
-
-
- /**
-+ * Same as #pj_ssl_sock_start_accept(), but application can provide
-+ * a secure socket parameter, which will be used to create a new secure
-+ * socket reported in \a on_accept_complete() callback when there is
-+ * an incoming connection.
-+ *
-+ * @param ssock The secure socket.
-+ * @param pool Pool used to allocate some internal data for the
-+ * operation.
-+ * @param localaddr Local address to bind on.
-+ * @param addr_len Length of buffer containing local address.
-+ * @param newsock_param Secure socket parameter for new accepted sockets.
-+ *
-+ * @return PJ_SUCCESS if the operation has been successful,
-+ * or the appropriate error code on failure.
-+ */
-+PJ_DECL(pj_status_t)
-+pj_ssl_sock_start_accept2(pj_ssl_sock_t *ssock,
-+ pj_pool_t *pool,
-+ const pj_sockaddr_t *local_addr,
-+ int addr_len,
-+ const pj_ssl_sock_param *newsock_param);
-+
-+
-+/**
- * Starts asynchronous socket connect() operation and SSL/TLS handshaking
- * for this socket. Once the connection is done (either successfully or not),
- * the \a on_connect_complete() callback will be called.
-diff --git a/pjlib/src/pj/ssl_sock_common.c b/pjlib/src/pj/ssl_sock_common.c
-index 913efee..717ab1d 100644
---- a/pjlib/src/pj/ssl_sock_common.c
-+++ b/pjlib/src/pj/ssl_sock_common.c
-@@ -19,6 +19,7 @@
- #include <pj/ssl_sock.h>
- #include <pj/assert.h>
- #include <pj/errno.h>
-+#include <pj/pool.h>
- #include <pj/string.h>
-
- /*
-@@ -48,6 +49,31 @@ PJ_DEF(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param)
- }
-
-
-+/*
-+ * Duplicate SSL socket parameter.
-+ */
-+PJ_DEF(void) pj_ssl_sock_param_copy( pj_pool_t *pool,
-+ pj_ssl_sock_param *dst,
-+ const pj_ssl_sock_param *src)
-+{
-+ /* Init secure socket param */
-+ pj_memcpy(dst, src, sizeof(*dst));
-+ if (src->ciphers_num > 0) {
-+ unsigned i;
-+ dst->ciphers = (pj_ssl_cipher*)
-+ pj_pool_calloc(pool, src->ciphers_num,
-+ sizeof(pj_ssl_cipher));
-+ for (i = 0; i < src->ciphers_num; ++i)
-+ dst->ciphers[i] = src->ciphers[i];
-+ }
-+
-+ if (src->server_name.slen) {
-+ /* Server name must be null-terminated */
-+ pj_strdup_with_null(pool, &dst->server_name, &src->server_name);
-+ }
-+}
-+
-+
- PJ_DEF(pj_status_t) pj_ssl_cert_get_verify_status_strings(
- pj_uint32_t verify_status,
- const char *error_strings[],
-diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c
-index 40a5a1e..6a701b7 100644
---- a/pjlib/src/pj/ssl_sock_ossl.c
-+++ b/pjlib/src/pj/ssl_sock_ossl.c
-@@ -141,6 +141,7 @@ struct pj_ssl_sock_t
- pj_pool_t *pool;
- pj_ssl_sock_t *parent;
- pj_ssl_sock_param param;
-+ pj_ssl_sock_param newsock_param;
- pj_ssl_cert_t *cert;
-
- pj_ssl_cert_info local_cert_info;
-@@ -1757,11 +1758,9 @@ static pj_bool_t asock_on_accept_complete (pj_activesock_t *asock,
- unsigned i;
- pj_status_t status;
-
-- PJ_UNUSED_ARG(src_addr_len);
--
- /* Create new SSL socket instance */
-- status = pj_ssl_sock_create(ssock_parent->pool, &ssock_parent->param,
-- &ssock);
-+ status = pj_ssl_sock_create(ssock_parent->pool,
-+ &ssock_parent->newsock_param, &ssock);
- if (status != PJ_SUCCESS)
- goto on_return;
-
-@@ -2183,20 +2182,8 @@ PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool,
- return status;
-
- /* Init secure socket param */
-- ssock->param = *param;
-+ pj_ssl_sock_param_copy(pool, &ssock->param, param);
- ssock->param.read_buffer_size = ((ssock->param.read_buffer_size+7)>>3)<<3;
-- if (param->ciphers_num > 0) {
-- unsigned i;
-- ssock->param.ciphers = (pj_ssl_cipher*)
-- pj_pool_calloc(pool, param->ciphers_num,
-- sizeof(pj_ssl_cipher));
-- for (i = 0; i < param->ciphers_num; ++i)
-- ssock->param.ciphers[i] = param->ciphers[i];
-- }
--
-- /* Server name must be null-terminated */
-- pj_strdup_with_null(pool, &ssock->param.server_name,
-- &param->server_name);
-
- /* Finally */
- *p_ssock = ssock;
-@@ -2617,12 +2604,36 @@ PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock,
- const pj_sockaddr_t *localaddr,
- int addr_len)
- {
-+ return pj_ssl_sock_start_accept2(ssock, pool, localaddr, addr_len,
-+ &ssock->param);
-+}
-+
-+
-+/**
-+ * Same as #pj_ssl_sock_start_accept(), but application provides parameter
-+ * for new accepted secure sockets.
-+ */
-+PJ_DEF(pj_status_t)
-+pj_ssl_sock_start_accept2(pj_ssl_sock_t *ssock,
-+ pj_pool_t *pool,
-+ const pj_sockaddr_t *localaddr,
-+ int addr_len,
-+ const pj_ssl_sock_param *newsock_param)
-+{
- pj_activesock_cb asock_cb;
- pj_activesock_cfg asock_cfg;
- pj_status_t status;
-
- PJ_ASSERT_RETURN(ssock && pool && localaddr && addr_len, PJ_EINVAL);
-
-+ /* Verify new socket parameters */
-+ if (newsock_param->grp_lock != ssock->param.grp_lock ||
-+ newsock_param->sock_af != ssock->param.sock_af ||
-+ newsock_param->sock_type != ssock->param.sock_type)
-+ {
-+ return PJ_EINVAL;
-+ }
-+
- /* Create socket */
- status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0,
- &ssock->sock);
-@@ -2691,6 +2702,7 @@ PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock,
- goto on_error;
-
- /* Start accepting */
-+ pj_ssl_sock_param_copy(pool, &ssock->newsock_param, newsock_param);
- status = pj_activesock_start_accept(ssock->asock, pool);
- if (status != PJ_SUCCESS)
- goto on_error;
-diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c
-index a9e95fb..91d99a7 100644
---- a/pjsip/src/pjsip/sip_transport_tls.c
-+++ b/pjsip/src/pjsip/sip_transport_tls.c
-@@ -314,7 +314,7 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start2( pjsip_endpoint *endpt,
- int af, sip_ssl_method;
- pj_uint32_t sip_ssl_proto;
- struct tls_listener *listener;
-- pj_ssl_sock_param ssock_param;
-+ pj_ssl_sock_param ssock_param, newsock_param;
- pj_sockaddr *listener_addr;
- pj_bool_t has_listener;
- pj_status_t status;
-@@ -473,9 +473,14 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start2( pjsip_endpoint *endpt,
- */
- has_listener = PJ_FALSE;
-
-- status = pj_ssl_sock_start_accept(listener->ssock, pool,
-+ pj_memcpy(&newsock_param, &ssock_param, sizeof(newsock_param));
-+ newsock_param.async_cnt = 1;
-+ newsock_param.cb.on_data_read = &on_data_read;
-+ newsock_param.cb.on_data_sent = &on_data_sent;
-+ status = pj_ssl_sock_start_accept2(listener->ssock, pool,
- (pj_sockaddr_t*)listener_addr,
-- pj_sockaddr_get_len((pj_sockaddr_t*)listener_addr));
-+ pj_sockaddr_get_len((pj_sockaddr_t*)listener_addr),
-+ &newsock_param);
- if (status == PJ_SUCCESS || status == PJ_EPENDING) {
- pj_ssl_sock_info info;
- has_listener = PJ_TRUE;
---
-cgit v0.11.2
-
diff --git a/third-party/pjproject/patches/0001-Bump-tcp-tls-and-transaction-log-levels-from-1-to-3.patch b/third-party/pjproject/patches/0001-Bump-tcp-tls-and-transaction-log-levels-from-1-to-3.patch
deleted file mode 100644
index 9873abf0e..000000000
--- a/third-party/pjproject/patches/0001-Bump-tcp-tls-and-transaction-log-levels-from-1-to-3.patch
+++ /dev/null
@@ -1,70 +0,0 @@
-From a147b72df1ec150c1d733e882225db86142fb339 Mon Sep 17 00:00:00 2001
-From: George Joseph <george.joseph@fairview5.com>
-Date: Sun, 21 Feb 2016 10:01:53 -0700
-Subject: [PATCH] Bump tcp/tls and transaction log levels from 1 to 3
-
-sip_transport_tcp, sip_transport_tls and sip_transaction are printing messages
-at log level 1 or 2 for things that are transient, recoverable, possibly
-expected, or are handled with return codes. A good example of this is if we're
-trying to send an OPTIONS message to a TCP client that has disappeared. Both
-sip_transport_tcp and sip_transaction are printing "connection refused"
-messages because the remote client isn't listening. This is generally expected
-behavior and it should be up to the app caller to determine if an error message
-is warranted.
----
- pjsip/src/pjsip/sip_transaction.c | 4 ++--
- pjsip/src/pjsip/sip_transport_tcp.c | 2 +-
- pjsip/src/pjsip/sip_transport_tls.c | 2 +-
- 3 files changed, 4 insertions(+), 4 deletions(-)
-
-diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c
-index 46bd971..1b4fdb7 100644
---- a/pjsip/src/pjsip/sip_transaction.c
-+++ b/pjsip/src/pjsip/sip_transaction.c
-@@ -1898,7 +1898,7 @@ static void send_msg_callback( pjsip_send_state *send_state,
-
- err =pj_strerror((pj_status_t)-sent, errmsg, sizeof(errmsg));
-
-- PJ_LOG(2,(tsx->obj_name,
-+ PJ_LOG(3,(tsx->obj_name,
- "Failed to send %s! err=%d (%s)",
- pjsip_tx_data_get_info(send_state->tdata), -sent,
- errmsg));
-@@ -1938,7 +1938,7 @@ static void send_msg_callback( pjsip_send_state *send_state,
- }
-
- } else {
-- PJ_PERROR(2,(tsx->obj_name, (pj_status_t)-sent,
-+ PJ_PERROR(3,(tsx->obj_name, (pj_status_t)-sent,
- "Temporary failure in sending %s, "
- "will try next server",
- pjsip_tx_data_get_info(send_state->tdata)));
-diff --git a/pjsip/src/pjsip/sip_transport_tcp.c b/pjsip/src/pjsip/sip_transport_tcp.c
-index 222cb13..1bbb324 100644
---- a/pjsip/src/pjsip/sip_transport_tcp.c
-+++ b/pjsip/src/pjsip/sip_transport_tcp.c
-@@ -164,7 +164,7 @@ static void tcp_perror(const char *sender, const char *title,
-
- pj_strerror(status, errmsg, sizeof(errmsg));
-
-- PJ_LOG(1,(sender, "%s: %s [code=%d]", title, errmsg, status));
-+ PJ_LOG(3,(sender, "%s: %s [code=%d]", title, errmsg, status));
- }
-
-
-diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c
-index 617d7f5..a83ac32 100644
---- a/pjsip/src/pjsip/sip_transport_tls.c
-+++ b/pjsip/src/pjsip/sip_transport_tls.c
-@@ -170,7 +170,7 @@ static void tls_perror(const char *sender, const char *title,
-
- pj_strerror(status, errmsg, sizeof(errmsg));
-
-- PJ_LOG(1,(sender, "%s: %s [code=%d]", title, errmsg, status));
-+ PJ_LOG(3,(sender, "%s: %s [code=%d]", title, errmsg, status));
- }
-
-
---
-2.5.0
-
diff --git a/third-party/pjproject/patches/0001-ioqueue-Enable-epoll-in-aconfigure.ac.patch b/third-party/pjproject/patches/0001-ioqueue-Enable-epoll-in-aconfigure.ac.patch
deleted file mode 100644
index 36b6c651f..000000000
--- a/third-party/pjproject/patches/0001-ioqueue-Enable-epoll-in-aconfigure.ac.patch
+++ /dev/null
@@ -1,80 +0,0 @@
-From b5c0bc905911f75e08987e6833075481fe16dab2 Mon Sep 17 00:00:00 2001
-From: George Joseph <george.joseph@fairview5.com>
-Date: Mon, 22 Feb 2016 13:05:59 -0700
-Subject: [PATCH] ioqueue: Enable epoll in aconfigure.ac
-
-Although the --enable-epoll option was being accepted, the result
-was always forced to select. This patch updates aconfigure.ac
-to properly set the value of ac_linux_poll if --enable-epoll is
-specified.
----
- README.txt | 1 +
- aconfigure | 11 +++++++----
- aconfigure.ac | 7 +++++--
- pjlib/include/pj/compat/os_auto.h.in | 3 +++
- 4 files changed, 16 insertions(+), 6 deletions(-)
-
-diff --git a/README.txt b/README.txt
-index bc45da8..48415fd 100644
---- a/README.txt
-+++ b/README.txt
-@@ -463,6 +463,7 @@ Using Default Settings
- $ ./configure --help
- ...
- Optional Features:
-+ --enable-epoll Use epoll on Linux instead of select
- --disable-floating-point Disable floating point where possible
- --disable-sound Exclude sound (i.e. use null sound)
- --disable-small-filter Exclude small filter in resampling
-diff --git a/aconfigure.ac b/aconfigure.ac
-index 2f71abb..3e88124 100644
---- a/aconfigure.ac
-+++ b/aconfigure.ac
-@@ -410,6 +410,7 @@ dnl ######################
- dnl # ioqueue selection
- dnl #
- AC_SUBST(ac_os_objs)
-+AC_SUBST(ac_linux_poll)
- AC_MSG_CHECKING([ioqueue backend])
- AC_ARG_ENABLE(epoll,
- AC_HELP_STRING([--enable-epoll],
-@@ -417,10 +418,13 @@ AC_ARG_ENABLE(epoll,
- [
- ac_os_objs=ioqueue_epoll.o
- AC_MSG_RESULT([/dev/epoll])
-+ AC_DEFINE(PJ_HAS_LINUX_EPOLL,1)
-+ ac_linux_poll=epoll
- ],
- [
- ac_os_objs=ioqueue_select.o
-- AC_MSG_RESULT([select()])
-+ AC_MSG_RESULT([select()])
-+ ac_linux_poll=select
- ])
-
- AC_SUBST(ac_shared_libraries)
-@@ -1879,7 +1883,6 @@ esac
-
-
- AC_SUBST(target)
--AC_SUBST(ac_linux_poll,select)
- AC_SUBST(ac_host,unix)
- AC_SUBST(ac_main_obj)
- case $target in
-diff --git a/pjlib/include/pj/compat/os_auto.h.in b/pjlib/include/pj/compat/os_auto.h.in
-index 77980d3..c8e73b2 100644
---- a/pjlib/include/pj/compat/os_auto.h.in
-+++ b/pjlib/include/pj/compat/os_auto.h.in
-@@ -128,6 +128,9 @@
- */
- #undef PJ_SELECT_NEEDS_NFDS
-
-+/* Was Linux epoll support enabled */
-+#undef PJ_HAS_LINUX_EPOLL
-+
- /* Is errno a good way to retrieve OS errors?
- */
- #undef PJ_HAS_ERRNO_VAR
---
-2.5.0
-
diff --git a/third-party/pjproject/patches/0001-pjsip-apps-src-python-setup.py-Take-make-from-the-en.patch b/third-party/pjproject/patches/0001-pjsip-apps-src-python-setup.py-Take-make-from-the-en.patch
deleted file mode 100644
index 80f8bc0b3..000000000
--- a/third-party/pjproject/patches/0001-pjsip-apps-src-python-setup.py-Take-make-from-the-en.patch
+++ /dev/null
@@ -1,51 +0,0 @@
-From 61668b8fcaa0f2a8a05100097284c0c427600033 Mon Sep 17 00:00:00 2001
-From: George Joseph <george.joseph@fairview5.com>
-Date: Mon, 2 May 2016 17:08:15 -0600
-Subject: [PATCH] pjsip-apps/src/python/setup.py: Take "make" from the
- environment
-
-With "make" hard coded in setup.py, it chokes on FreeBSD because the system
-make command isn't GNU compatibile. This patch allows setup.py to take the
-name of the make command from the MAKE environment variable if it exists.
-If it doesn't, it defaults to "make".
----
- pjsip-apps/src/python/setup.py | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
-
-diff --git a/pjsip-apps/src/python/setup.py b/pjsip-apps/src/python/setup.py
-index 69a9859..ea1427d 100644
---- a/pjsip-apps/src/python/setup.py
-+++ b/pjsip-apps/src/python/setup.py
-@@ -60,25 +60,25 @@ if pj_version_suffix:
- pj_version += "-" + pj_version_suffix
-
- #print 'PJ_VERSION = "'+ pj_version + '"'
--
-+MAKE = os.environ.get('MAKE') or "make"
-
- # Fill in pj_inc_dirs
- pj_inc_dirs = []
--f = os.popen("make -f helper.mak inc_dir")
-+f = os.popen("%s -f helper.mak inc_dir" % MAKE)
- for line in f:
- pj_inc_dirs.append(line.rstrip("\r\n"))
- f.close()
-
- # Fill in pj_lib_dirs
- pj_lib_dirs = []
--f = os.popen("make -f helper.mak lib_dir")
-+f = os.popen("%s -f helper.mak lib_dir" % MAKE)
- for line in f:
- pj_lib_dirs.append(line.rstrip("\r\n"))
- f.close()
-
- # Fill in pj_libs
- pj_libs = []
--f = os.popen("make -f helper.mak libs")
-+f = os.popen("%s -f helper.mak libs" % MAKE)
- for line in f:
- pj_libs.append(line.rstrip("\r\n"))
- f.close()
---
-2.5.5
-
diff --git a/third-party/pjproject/patches/0001-sip_parser.c-Fix-pjsip_VIA_PARAM_SPEC_ESC.patch b/third-party/pjproject/patches/0001-sip_parser.c-Fix-pjsip_VIA_PARAM_SPEC_ESC.patch
deleted file mode 100644
index 60c27cb1a..000000000
--- a/third-party/pjproject/patches/0001-sip_parser.c-Fix-pjsip_VIA_PARAM_SPEC_ESC.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From 0fc7ef5f01be9cc74d184c3ca3a973ff1ef44c93 Mon Sep 17 00:00:00 2001
-From: George Joseph <george.joseph@fairview5.com>
-Date: Sun, 10 Apr 2016 12:54:06 -0600
-Subject: [PATCH] sip_parser.c: Fix pjsip_VIA_PARAM_SPEC_ESC
-
-pjsip_VIA_PARAM_SPEC_ESC should have been pjsip_TOKEN_SPEC_ESC + ":" but
-instead of appending ":" to pjsip_VIA_PARAM_SPEC_ESC it was being appended
-to pjsip_VIA_PARAM_SPEC again. This was causing parsing of Via headers
-to fail when an ipv6 address was in a "received" param and
-PJSIP_UNESCAPE_IN_PLACE was used. Probably just a copy/paste error.
----
- pjsip/src/pjsip/sip_parser.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c
-index 378c22f..c18faa3 100644
---- a/pjsip/src/pjsip/sip_parser.c
-+++ b/pjsip/src/pjsip/sip_parser.c
-@@ -327,7 +327,7 @@ static pj_status_t init_parser()
-
- status = pj_cis_dup(&pconst.pjsip_VIA_PARAM_SPEC_ESC, &pconst.pjsip_TOKEN_SPEC_ESC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-- pj_cis_add_str(&pconst.pjsip_VIA_PARAM_SPEC, ":");
-+ pj_cis_add_str(&pconst.pjsip_VIA_PARAM_SPEC_ESC, ":");
-
- status = pj_cis_dup(&pconst.pjsip_HOST_SPEC, &pconst.pjsip_ALNUM_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
---
-2.5.5
-
diff --git a/third-party/pjproject/patches/0001-sip_parser.c-Remove-wholesale-strip-from-parse_param.patch b/third-party/pjproject/patches/0001-sip_parser.c-Remove-wholesale-strip-from-parse_param.patch
deleted file mode 100644
index e0bd9129c..000000000
--- a/third-party/pjproject/patches/0001-sip_parser.c-Remove-wholesale-strip-from-parse_param.patch
+++ /dev/null
@@ -1,55 +0,0 @@
-From ce426249ec1270f27560919791f3e13eaeea9152 Mon Sep 17 00:00:00 2001
-From: George Joseph <george.joseph@fairview5.com>
-Date: Tue, 12 Apr 2016 14:09:53 -0600
-Subject: [PATCH] sip_parser.c: Remove wholesale '[]' strip from
- parse_param_impl
-
-The wholesale stripping of '[]' from header parameters causes issues if
-something (like a port) occurrs after the final ']'.
-
-'[2001:a::b]' will correctly parse to '2001:a::b'
-'[2001:a::b]:8080' will correctly parse to '2001:a::b' but the scanner is left
-with ':8080' and parsing stops with a syntax error.
-
-I can't even find a case where stripping the '[]' is a good thing anyway. Even
-if you continued to parse and resulted in a string that looks like this...
-'2001:a::b:8080', it's not valid.
-
-This came up in Asterisk because Kamailio sends us a Contact with an alias
-URI parameter that has an IPv6 address in it like this:
-Contact: <sip:1171@127.0.0.1:5080;alias=[2001:1:2::3]~43691~6>
-which should be legal but causes a syntax error because of the characters
-after the final ']'. Even if it didn't, the '[]' should still not be stripped.
-
-I've run the Asterisk Test Suite for PJSIP (252 tests) many of which are IPv6
-enabled. No issues were caused by removing the code that strips the '[]'.
-
-I tried running 'make pjsip-test' but that fails even without my change. :)
-
-The Asterisk ticket is: https://issues.asterisk.org/jira/browse/ASTERISK-25123
----
- pjsip/src/pjsip/sip_parser.c | 8 --------
- 1 file changed, 8 deletions(-)
-
-diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c
-index c18faa3..98eb5ea 100644
---- a/pjsip/src/pjsip/sip_parser.c
-+++ b/pjsip/src/pjsip/sip_parser.c
-@@ -1149,14 +1149,6 @@ static void parse_param_imp( pj_scanner *scanner, pj_pool_t *pool,
- pvalue->ptr++;
- pvalue->slen -= 2;
- }
-- } else if (*scanner->curptr == '[') {
-- /* pvalue can be a quoted IPv6; in this case, the
-- * '[' and ']' quote characters are to be removed
-- * from the pvalue.
-- */
-- pj_scan_get_char(scanner);
-- pj_scan_get_until_ch(scanner, ']', pvalue);
-- pj_scan_get_char(scanner);
- } else if(pj_cis_match(spec, *scanner->curptr)) {
- parser_get_and_unescape(scanner, pool, spec, esc_spec, pvalue);
- }
---
-2.5.5
-
diff --git a/third-party/pjproject/patches/0001-sip_transport-Search-for-transport-even-if-listener-.patch b/third-party/pjproject/patches/0001-sip_transport-Search-for-transport-even-if-listener-.patch
deleted file mode 100644
index 001912cfe..000000000
--- a/third-party/pjproject/patches/0001-sip_transport-Search-for-transport-even-if-listener-.patch
+++ /dev/null
@@ -1,114 +0,0 @@
-From 552194179eb6deae8326eb0fef446e69240ea41b Mon Sep 17 00:00:00 2001
-From: George Joseph <george.joseph@fairview5.com>
-Date: Fri, 19 Feb 2016 17:05:53 -0700
-Subject: [PATCH] sip_transport: Search for transport even if listener was
- specified.
-
-If a listener was specified when calling pjsip_tpmgr_acquire_transport2,
-a new transport was always created instead of using an existing one. This
-caused several issues mostly related to the remote end not expecting a new
-connection. I.E. A TCP client who registered to a server is not going to
-be listening for connections coming back from the server and refuses the
-connection.
-
-Now when pjsip_tpmgr_acquire_transport2 is called with a listener, the
-registry is still searched for an existing transport and the listener
-is used as a factory only if no existing transport can be found.
----
- pjsip/src/pjsip/sip_transport.c | 68 ++++++++++++++++++++---------------------
- 1 file changed, 34 insertions(+), 34 deletions(-)
-
-diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c
-index 0410324..620b9c0 100644
---- a/pjsip/src/pjsip/sip_transport.c
-+++ b/pjsip/src/pjsip/sip_transport.c
-@@ -1999,29 +1999,11 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr,
-
- TRACE_((THIS_FILE, "Transport %s acquired", seltp->obj_name));
- return PJ_SUCCESS;
--
--
-- } else if (sel && sel->type == PJSIP_TPSELECTOR_LISTENER &&
-- sel->u.listener)
-- {
-- /* Application has requested that a specific listener is to
-- * be used. In this case, skip transport hash table lookup.
-- */
--
-- /* Verify that the listener type matches the destination type */
-- if (sel->u.listener->type != type) {
-- pj_lock_release(mgr->lock);
-- return PJSIP_ETPNOTSUITABLE;
-- }
--
-- /* We'll use this listener to create transport */
-- factory = sel->u.listener;
--
- } else {
-
- /*
- * This is the "normal" flow, where application doesn't specify
-- * specific transport/listener to be used to send message to.
-+ * specific transport to be used to send message to.
- * In this case, lookup the transport from the hash table.
- */
- pjsip_transport_key key;
-@@ -2081,22 +2063,40 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr,
- return PJ_SUCCESS;
- }
-
-- /*
-- * Transport not found!
-- * Find factory that can create such transport.
-- */
-- factory = mgr->factory_list.next;
-- while (factory != &mgr->factory_list) {
-- if (factory->type == type)
-- break;
-- factory = factory->next;
-- }
-+ if (sel && sel->type == PJSIP_TPSELECTOR_LISTENER &&
-+ sel->u.listener)
-+ {
-+ /* Application has requested that a specific listener is to
-+ * be used.
-+ */
-+
-+ /* Verify that the listener type matches the destination type */
-+ if (sel->u.listener->type != type) {
-+ pj_lock_release(mgr->lock);
-+ return PJSIP_ETPNOTSUITABLE;
-+ }
-
-- if (factory == &mgr->factory_list) {
-- /* No factory can create the transport! */
-- pj_lock_release(mgr->lock);
-- TRACE_((THIS_FILE, "No suitable factory was found either"));
-- return PJSIP_EUNSUPTRANSPORT;
-+ /* We'll use this listener to create transport */
-+ factory = sel->u.listener;
-+
-+ } else {
-+ /*
-+ * Transport not found!
-+ * Find factory that can create such transport.
-+ */
-+ factory = mgr->factory_list.next;
-+ while (factory != &mgr->factory_list) {
-+ if (factory->type == type)
-+ break;
-+ factory = factory->next;
-+ }
-+
-+ if (factory == &mgr->factory_list) {
-+ /* No factory can create the transport! */
-+ pj_lock_release(mgr->lock);
-+ TRACE_((THIS_FILE, "No suitable factory was found either"));
-+ return PJSIP_EUNSUPTRANSPORT;
-+ }
- }
- }
-
---
-2.5.0
-
diff --git a/third-party/pjproject/patches/0001-sip_transport_tcp-tls-Set-factory-on-transports-crea.patch b/third-party/pjproject/patches/0001-sip_transport_tcp-tls-Set-factory-on-transports-crea.patch
new file mode 100644
index 000000000..d8a9adbde
--- /dev/null
+++ b/third-party/pjproject/patches/0001-sip_transport_tcp-tls-Set-factory-on-transports-crea.patch
@@ -0,0 +1,48 @@
+From b7cb93b0e1729589a71e8b30d9a9893f0918e2a2 Mon Sep 17 00:00:00 2001
+From: George Joseph <george.joseph@fairview5.com>
+Date: Mon, 30 May 2016 11:58:22 -0600
+Subject: [PATCH] sip_transport_tcp/tls: Set factory on transports created
+ from accept
+
+The ability to re-use tcp and tls transports when a factory is
+specified now depends on transport->factory being set which is a new field
+in 2.5. This was being set only on new outgoing sockets not on
+incoming sockets. The result was that a client REGISTER created a new
+socket but without the factory set, the next outgoing request to the
+client, OPTIONS, INVITE, etc, would attempt to create another socket
+which the client would refuse.
+
+This patch sets the factory on transports created as a result of an
+accept.
+---
+ pjsip/src/pjsip/sip_transport_tcp.c | 1 +
+ pjsip/src/pjsip/sip_transport_tls.c | 1 +
+ 2 files changed, 2 insertions(+)
+
+diff --git a/pjsip/src/pjsip/sip_transport_tcp.c b/pjsip/src/pjsip/sip_transport_tcp.c
+index 1bbb324..00eb8fc 100644
+--- a/pjsip/src/pjsip/sip_transport_tcp.c
++++ b/pjsip/src/pjsip/sip_transport_tcp.c
+@@ -713,6 +713,7 @@ static pj_status_t tcp_create( struct tcp_listener *listener,
+ tcp->base.send_msg = &tcp_send_msg;
+ tcp->base.do_shutdown = &tcp_shutdown;
+ tcp->base.destroy = &tcp_destroy_transport;
++ tcp->base.factory = &listener->factory;
+
+ /* Create group lock */
+ status = pj_grp_lock_create(pool, NULL, &tcp->grp_lock);
+diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c
+index a83ac32..36ee70d 100644
+--- a/pjsip/src/pjsip/sip_transport_tls.c
++++ b/pjsip/src/pjsip/sip_transport_tls.c
+@@ -742,6 +742,7 @@ static pj_status_t tls_create( struct tls_listener *listener,
+ tls->base.send_msg = &tls_send_msg;
+ tls->base.do_shutdown = &tls_shutdown;
+ tls->base.destroy = &tls_destroy_transport;
++ tls->base.factory = &listener->factory;
+
+ tls->ssock = ssock;
+
+--
+2.5.5
+
diff --git a/third-party/pjproject/patches/0002-aconfigure.ac-Fix-autoconf-issue-with-opencore-amrnb.patch b/third-party/pjproject/patches/0002-aconfigure.ac-Fix-autoconf-issue-with-opencore-amrnb.patch
deleted file mode 100644
index 04d1d9703..000000000
--- a/third-party/pjproject/patches/0002-aconfigure.ac-Fix-autoconf-issue-with-opencore-amrnb.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From 1281b60a1807d1285b101b6eb61c6478f29785fe Mon Sep 17 00:00:00 2001
-From: George Joseph <george.joseph@fairview5.com>
-Date: Wed, 23 Mar 2016 07:48:52 -0600
-Subject: [PATCH] aconfigure.ac: Fix autoconf issue with opencore-amrnb on
- older systems
-
-autoconf 2.63 on CentOS6 produces a bad ./aconfigure file related to
-opencore-amrnb.
-
-./aconfigure: line 15158: syntax error near unexpected token `fi'
-
-To get around this, a 'true;' needed to be added to the Ok case of
-AC_ARG_WITH(opencore-amrnb)
----
- aconfigure | 3 +++
- aconfigure.ac | 2 +-
- 2 files changed, 4 insertions(+), 1 deletion(-)
-
-diff --git a/aconfigure b/aconfigure
-index 33a08f5..4c122c2 100755
---- a/aconfigure
-+++ b/aconfigure
-@@ -7908,6 +7908,9 @@ fi
- # Check whether --with-opencore-amrnb was given.
- if test "${with_opencore_amrnb+set}" = set; then :
- withval=$with_opencore_amrnb; as_fn_error $? "This option is obsolete and replaced by --with-opencore-amr=DIR" "$LINENO" 5
-+else
-+ true;
-+
- fi
-
-
-diff --git a/aconfigure.ac b/aconfigure.ac
-index 3e88124..5d3e833 100644
---- a/aconfigure.ac
-+++ b/aconfigure.ac
-@@ -1631,7 +1631,7 @@ AC_ARG_WITH(opencore-amrnb,
- AC_HELP_STRING([--with-opencore-amrnb=DIR],
- [This option is obsolete and replaced by --with-opencore-amr=DIR]),
- [AC_MSG_ERROR(This option is obsolete and replaced by --with-opencore-amr=DIR)],
-- []
-+ [true;]
- )
-
- dnl # opencore-amr alt prefix
---
-2.5.0
-
diff --git a/third-party/versions.mak b/third-party/versions.mak
index 7b8b59c53..ca40531df 100644
--- a/third-party/versions.mak
+++ b/third-party/versions.mak
@@ -1,2 +1,2 @@
-PJPROJECT_VERSION = 2.4.5
+PJPROJECT_VERSION = 2.5