[asterisk-commits] mmichelson: branch group/v14_colp r146401 - /team/group/v14_colp/channels/
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Sat Oct 4 13:23:03 CDT 2008
Author: mmichelson
Date: Sat Oct 4 13:23:03 2008
New Revision: 146401
URL: http://svn.digium.com/view/asterisk?view=rev&rev=146401
Log:
Backport of most of chan_sip from the issue8824 branch.
The check_user_full function is quite a bit different
between 1.4, trunk, and the issue8824 branch, so I'm going
to have to get that fixed up for the next commit.
Note: This code does not compile right now and I know this.
This will be fixed in the next commit, which should come
shortly hereafter.
Modified:
team/group/v14_colp/channels/chan_sip.c
Modified: team/group/v14_colp/channels/chan_sip.c
URL: http://svn.digium.com/view/asterisk/team/group/v14_colp/channels/chan_sip.c?view=diff&rev=146401&r1=146400&r2=146401
==============================================================================
--- team/group/v14_colp/channels/chan_sip.c (original)
+++ team/group/v14_colp/channels/chan_sip.c Sat Oct 4 13:23:03 2008
@@ -197,8 +197,6 @@
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#endif
-#define CALLERID_UNKNOWN "Unknown"
-
#define DEFAULT_MAXMS 2000 /*!< Qualification: Must be faster than 2 seconds by default */
#define DEFAULT_FREQ_OK 60 * 1000 /*!< Qualification: How often to check for the host to be up */
#define DEFAULT_FREQ_NOTOK 10 * 1000 /*!< Qualification: How often to check, if the host is down... */
@@ -471,6 +469,54 @@
{ SIP_OPT_RESPRIORITY, NOT_SUPPORTED, "resource-priority" },
};
+/*! \brief Diversion header reasons
+ *
+ * The core defines a bunch of constants used to define
+ * redirecting reasons. This provides a translation table
+ * between those and the strings which may be present in
+ * a SIP Diversion header
+ */
+static const struct sip_reasons {
+ enum AST_REDIRECTING_REASON code;
+ char * const text;
+} sip_reason_table[] = {
+ { AST_REDIRECTING_REASON_UNKNOWN, "unknown" },
+ { AST_REDIRECTING_REASON_USER_BUSY, "user-busy" },
+ { AST_REDIRECTING_REASON_NO_ANSWER, "no-answer" },
+ { AST_REDIRECTING_REASON_UNAVAILABLE, "unavailable" },
+ { AST_REDIRECTING_REASON_UNCONDITIONAL, "unconditional" },
+ { AST_REDIRECTING_REASON_TIME_OF_DAY, "time-of-day" },
+ { AST_REDIRECTING_REASON_DO_NOT_DISTURB, "do-not-disturb" },
+ { AST_REDIRECTING_REASON_DEFLECTION, "deflection" },
+ { AST_REDIRECTING_REASON_FOLLOW_ME, "follow-me" },
+ { AST_REDIRECTING_REASON_OUT_OF_ORDER, "out-of-service" },
+ { AST_REDIRECTING_REASON_AWAY, "away" },
+ { AST_REDIRECTING_REASON_CALL_FWD_DTE, "unknown"}
+};
+
+static enum AST_REDIRECTING_REASON sip_reason_str_to_code(const char *text)
+{
+ enum AST_REDIRECTING_REASON ast = AST_REDIRECTING_REASON_UNKNOWN;
+ int i;
+
+ for (i = 0; i < ARRAY_LEN(sip_reason_table); ++i) {
+ if (!strcasecmp(text, sip_reason_table[i].text)) {
+ ast = sip_reason_table[i].code;
+ break;
+ }
+ }
+
+ return ast;
+}
+
+static const char *sip_reason_code_to_str(enum AST_REDIRECTING_REASON code)
+{
+ if (code >= 0 && code < ARRAY_LEN(sip_reason_table)) {
+ return sip_reason_table[code].text;
+ }
+
+ return "unknown";
+}
/*! \brief SIP Methods we support */
#define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY"
@@ -759,12 +805,11 @@
#define SIP_PROG_INBAND_YES (2 << 25)
#define SIP_NO_HISTORY (1 << 27) /*!< Suppress recording request/response history */
#define SIP_CALL_LIMIT (1 << 28) /*!< Call limit enforced for this call */
-#define SIP_SENDRPID (1 << 29) /*!< Remote Party-ID Support */
#define SIP_INC_COUNT (1 << 30) /*!< Did this connection increment the counter of in-use calls? */
#define SIP_G726_NONSTANDARD (1 << 31) /*!< Use non-standard packing for G726-32 data */
#define SIP_FLAGS_TO_COPY \
- (SIP_PROMISCREDIR | SIP_TRUSTRPID | SIP_SENDRPID | SIP_DTMF | SIP_REINVITE | \
+ (SIP_PROMISCREDIR | SIP_TRUSTRPID | SIP_PAGE2_SENDRPID | SIP_DTMF | SIP_REINVITE | \
SIP_PROG_INBAND | SIP_USECLIENTCODE | SIP_NAT | SIP_G726_NONSTANDARD | \
SIP_USEREQPHONE | SIP_INSECURE_PORT | SIP_INSECURE_INVITE)
@@ -801,6 +846,9 @@
#define SIP_PAGE2_OUTGOING_CALL (1 << 27) /*!< 27: Is this an outgoing call? */
#define SIP_PAGE2_UDPTL_DESTINATION (1 << 28) /*!< 28: Use source IP of RTP as destination if NAT is enabled */
#define SIP_PAGE2_DIALOG_ESTABLISHED (1 << 29) /*!< 29: Has a dialog been established? */
+#define SIP_PAGE2_SENDRPID (3 << 30) /*!< Remote Party-ID Support */
+#define SIP_PAGE2_SENDRPID_RPID (1 << 30) /*!< Use "Remote-Party-ID" header */
+#define SIP_PAGE2_SENDRPID_PAI (2 << 30) /*!< Use "P-Asserted-Identity" header */
#define SIP_PAGE2_FLAGS_TO_COPY \
(SIP_PAGE2_ALLOWSUBSCRIBE | SIP_PAGE2_ALLOWOVERLAP | SIP_PAGE2_VIDEOSUPPORT | \
@@ -936,6 +984,7 @@
AST_STRING_FIELD(mohinterpret); /*!< MOH class to use when put on hold */
AST_STRING_FIELD(mohsuggest); /*!< MOH class to suggest when putting a peer on hold */
AST_STRING_FIELD(rdnis); /*!< Referring DNIS */
+ AST_STRING_FIELD(redircause); /*!< Referring cause */
AST_STRING_FIELD(theirtag); /*!< Their tag */
AST_STRING_FIELD(username); /*!< [user] name */
AST_STRING_FIELD(peername); /*!< [peer] name, not set if [user] */
@@ -949,8 +998,6 @@
AST_STRING_FIELD(via); /*!< Via: header */
AST_STRING_FIELD(fullcontact); /*!< The Contact: that the UA registers with us */
AST_STRING_FIELD(our_contact); /*!< Our contact header */
- AST_STRING_FIELD(rpid); /*!< Our RPID header */
- AST_STRING_FIELD(rpid_from); /*!< Our RPID From header */
);
unsigned int ocseq; /*!< Current outgoing seqno */
unsigned int icseq; /*!< Current incoming seqno */
@@ -1277,7 +1324,7 @@
static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno);
static void copy_request(struct sip_request *dst, const struct sip_request *src);
static void receive_message(struct sip_pvt *p, struct sip_request *req);
-static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req);
+static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req, char **name, char **number);
static int sip_send_mwi_to_peer(struct sip_peer *peer);
static int does_peer_need_mwi(struct sip_peer *peer);
@@ -1491,12 +1538,15 @@
static int set_address_from_contact(struct sip_pvt *pvt);
static void check_via(struct sip_pvt *p, const struct sip_request *req);
static char *get_calleridname(const char *input, char *output, size_t outputsize);
-static int get_rpid_num(const char *input, char *output, int maxlen);
-static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq);
+static int get_rpid(struct sip_pvt *p, struct sip_request *oreq);
+static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, char **number, int *reason);
static int get_destination(struct sip_pvt *p, struct sip_request *oreq);
static int get_msg_text(char *buf, int len, struct sip_request *req);
static void free_old_route(struct sip_route *route);
static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout);
+static void update_connectedline(struct sip_pvt *p, const void *data, size_t datalen);
+static void update_redirecting(struct sip_pvt *p, const void *data, size_t datalen);
+static void change_redirecting_information(struct sip_pvt *p, struct sip_request *req);
/*--- Constructing requests and responses */
static void initialize_initreq(struct sip_pvt *p, struct sip_request *req);
@@ -1518,6 +1568,7 @@
static int add_line(struct sip_request *req, const char *line);
static int add_text(struct sip_request *req, const char *text);
static int add_digit(struct sip_request *req, char digit, unsigned int duration);
+static int add_rpid(struct sip_request *req, struct sip_pvt *p);
static int add_vidupdate(struct sip_request *req);
static void add_route(struct sip_request *req, struct sip_route *route);
static int copy_header(struct sip_request *req, const struct sip_request *orig, const char *field);
@@ -1526,7 +1577,6 @@
static void set_destination(struct sip_pvt *p, char *uri);
static void append_date(struct sip_request *req);
static void build_contact(struct sip_pvt *p);
-static void build_rpid(struct sip_pvt *p);
/*------Request handling functions */
static int handle_request(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int *recount, int *nounlock);
@@ -2855,6 +2905,8 @@
ast_string_field_set(dialog, mohinterpret, peer->mohinterpret);
ast_string_field_set(dialog, tohost, peer->tohost);
ast_string_field_set(dialog, fullcontact, peer->fullcontact);
+ ast_string_field_set(dialog, cid_num, peer->cid_num);
+ ast_string_field_set(dialog, cid_name, peer->cid_name);
if (!dialog->initreq.headers && !ast_strlen_zero(peer->fromdomain)) {
char *tmpcall;
char *c;
@@ -4000,6 +4052,12 @@
break;
case AST_CONTROL_SRCUPDATE:
ast_rtp_new_source(p->rtp);
+ break;
+ case AST_CONTROL_CONNECTED_LINE:
+ update_connectedline(p, data, datalen);
+ break;
+ case AST_CONTROL_REDIRECTING:
+ update_redirecting(p, data, datalen);
break;
case -1:
res = -1;
@@ -6074,9 +6132,6 @@
add_header(req, "User-Agent", global_useragent);
add_header(req, "Max-Forwards", DEFAULT_MAX_FORWARDS);
- if (!ast_strlen_zero(p->rpid))
- add_header(req, "Remote-Party-ID", p->rpid);
-
return 0;
}
@@ -6262,6 +6317,73 @@
add_header(req, "Content-Type", "application/dtmf-relay");
add_header_contentLength(req, strlen(tmp));
add_line(req, tmp);
+ return 0;
+}
+
+/*! \brief Add Remote-Party-ID header to SIP message */
+static int add_rpid(struct sip_request *req, struct sip_pvt *p) {
+ char tmp[256];
+ char *lid_num = NULL;
+ char *lid_name = NULL;
+ int lid_pres;
+ const char *fromdomain;
+ const char *privacy = NULL;
+ const char *screen = NULL;
+
+ if (p->owner && p->owner->connected.id.number)
+ lid_num = p->owner->connected.id.number;
+ if (p->owner && p->owner->connected.id.name)
+ lid_name = p->owner->connected.id.name;
+ lid_pres = (p->owner) ? p->owner->connected.id.number_presentation : AST_PRES_NUMBER_NOT_AVAILABLE;
+
+ if (ast_strlen_zero(lid_num))
+ return 0;
+ if (ast_strlen_zero(lid_name))
+ lid_name = lid_num;
+ fromdomain = S_OR(p->fromdomain, ast_inet_ntoa(p->ourip));
+
+ snprintf(tmp, sizeof(tmp), "\"%s\" <sip:%s@%s>;party=%s", lid_name, lid_num, fromdomain, ast_test_flag(&p->flags[0], SIP_OUTGOING) ? "calling" : "called");
+
+ switch (lid_pres) {
+ case AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED:
+ case AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN:
+ privacy = "off";
+ screen = "no";
+ break;
+ case AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN:
+ case AST_PRES_ALLOWED_NETWORK_NUMBER:
+ privacy = "off";
+ screen = "yes";
+ break;
+ case AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED:
+ case AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN:
+ privacy = "full";
+ screen = "no";
+ break;
+ case AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN:
+ case AST_PRES_PROHIB_NETWORK_NUMBER:
+ privacy = "full";
+ screen = "yes";
+ break;
+ case AST_PRES_NUMBER_NOT_AVAILABLE:
+ break;
+ default:
+ if ((lid_pres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED)
+ privacy = "full";
+ else
+ privacy = "off";
+ screen = "no";
+ break;
+ }
+
+ if (!ast_strlen_zero(privacy) && !ast_strlen_zero(screen))
+ snprintf(tmp + strlen(tmp), sizeof(tmp) - strlen(tmp), ";privacy=%s;screen=%s", privacy, screen);
+
+ if (ast_test_flag(&p->flags[0], SIP_PAGE2_SENDRPID_RPID)) {
+ add_header(req, "Remote-Party-ID", tmp);
+ } else {
+ add_header(req, "P-Asserted-Identity", tmp);
+ }
return 0;
}
@@ -6922,85 +7044,6 @@
ast_string_field_build(p, our_contact, "<sip:%s%s%s>", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip));
}
-/*! \brief Build the Remote Party-ID & From using callingpres options */
-static void build_rpid(struct sip_pvt *p)
-{
- int send_pres_tags = TRUE;
- const char *privacy=NULL;
- const char *screen=NULL;
- char buf[256];
- const char *clid = default_callerid;
- const char *clin = NULL;
- const char *fromdomain;
-
- if (!ast_strlen_zero(p->rpid) || !ast_strlen_zero(p->rpid_from))
- return;
-
- if (p->owner && p->owner->cid.cid_num)
- clid = p->owner->cid.cid_num;
- if (p->owner && p->owner->cid.cid_name)
- clin = p->owner->cid.cid_name;
- if (ast_strlen_zero(clin))
- clin = clid;
-
- switch (p->callingpres) {
- case AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED:
- privacy = "off";
- screen = "no";
- break;
- case AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN:
- privacy = "off";
- screen = "yes";
- break;
- case AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN:
- privacy = "off";
- screen = "no";
- break;
- case AST_PRES_ALLOWED_NETWORK_NUMBER:
- privacy = "off";
- screen = "yes";
- break;
- case AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED:
- privacy = "full";
- screen = "no";
- break;
- case AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN:
- privacy = "full";
- screen = "yes";
- break;
- case AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN:
- privacy = "full";
- screen = "no";
- break;
- case AST_PRES_PROHIB_NETWORK_NUMBER:
- privacy = "full";
- screen = "yes";
- break;
- case AST_PRES_NUMBER_NOT_AVAILABLE:
- send_pres_tags = FALSE;
- break;
- default:
- ast_log(LOG_WARNING, "Unsupported callingpres (%d)\n", p->callingpres);
- if ((p->callingpres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED)
- privacy = "full";
- else
- privacy = "off";
- screen = "no";
- break;
- }
-
- fromdomain = S_OR(p->fromdomain, ast_inet_ntoa(p->ourip));
-
- snprintf(buf, sizeof(buf), "\"%s\" <sip:%s@%s>", clin, clid, fromdomain);
- if (send_pres_tags)
- snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ";privacy=%s;screen=%s", privacy, screen);
- ast_string_field_set(p, rpid, buf);
-
- ast_string_field_build(p, rpid_from, "\"%s\" <sip:%s@%s>;tag=%s", clin,
- S_OR(p->fromuser, clid),
- fromdomain, p->tag);
-}
-
/*! \brief Initiate new SIP request to peer/user */
static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod)
{
@@ -7036,15 +7079,9 @@
snprintf(p->lastmsg, sizeof(p->lastmsg), "Init: %s", sip_methods[sipmethod].text);
- if (p->owner) {
- l = p->owner->cid.cid_num;
- n = p->owner->cid.cid_name;
- }
- /* if we are not sending RPID and user wants his callerid restricted */
- if (!ast_test_flag(&p->flags[0], SIP_SENDRPID) &&
- ((p->callingpres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED)) {
- l = CALLERID_UNKNOWN;
- n = l;
+ if (p->owner && (p->owner->connected.id.number_presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) {
+ l = p->owner->connected.id.number;
+ n = p->owner->connected.id.name;
}
if (ast_strlen_zero(l))
l = default_callerid;
@@ -7117,11 +7154,7 @@
/* SLD: FIXME?: do Route: here too? I think not cos this is the first request.
* OTOH, then we won't have anything in p->route anyway */
/* Build Remote Party-ID and From */
- if (ast_test_flag(&p->flags[0], SIP_SENDRPID) && (sipmethod == SIP_INVITE)) {
- build_rpid(p);
- add_header(req, "From", p->rpid_from);
- } else
- add_header(req, "From", from);
+ add_header(req, "From", from);
add_header(req, "To", to);
ast_string_field_set(p, exten, l);
build_contact(p);
@@ -7131,8 +7164,45 @@
if (!ast_strlen_zero(global_useragent))
add_header(req, "User-Agent", global_useragent);
add_header(req, "Max-Forwards", DEFAULT_MAX_FORWARDS);
- if (!ast_strlen_zero(p->rpid))
- add_header(req, "Remote-Party-ID", p->rpid);
+}
+
+/*! \brief Add "Diversion" header to outgoing message
+ *
+ * We need to add a Diversion header if the owner channel of
+ * this dialog has redirecting information associated with it.
+ * XXX Add locking if necessary later.
+ *
+ * \param req The request/response to which we will add the header
+ * \param pvt The sip_pvt which represents the call-leg
+ * \param apr Redirecting data used to make the diversion header
+ */
+static void add_diversion_header(struct sip_request *req, struct sip_pvt *pvt)
+{
+ const char *diverting_number;
+ const char *diverting_name;
+ const char *reason;
+ char header_text[256];
+
+ if (!pvt->owner) {
+ return;
+ }
+
+ diverting_number = pvt->owner->cid.cid_rdnis;
+ diverting_name = pvt->owner->redirecting.from.name;
+ reason = sip_reason_code_to_str(pvt->owner->redirecting.reason);
+
+ if (ast_strlen_zero(diverting_number)) {
+ return;
+ }
+
+ /* We at least have a number to place in the Diversion header, which is enough */
+ if (ast_strlen_zero(diverting_name)) {
+ snprintf(header_text, sizeof(header_text), "<sip:%s@%s>;reason=\"%s\"", diverting_number, ast_inet_ntoa(pvt->ourip), reason);
+ } else {
+ snprintf(header_text, sizeof(header_text), "\"%s\" <sip:%s@%s>;reason=\"%s\"", diverting_name, diverting_number, ast_inet_ntoa(pvt->ourip), reason);
+ }
+
+ add_header(req, "Diversion", header_text);
}
/*! \brief Build REFER/INVITE/OPTIONS message and transmit it */
@@ -7215,6 +7285,11 @@
}
ast_channel_unlock(chan);
+ }
+ if ((sipmethod == SIP_INVITE || sipmethod == SIP_UPDATE) && ast_test_flag(&p->flags[0], SIP_PAGE2_SENDRPID))
+ add_rpid(&req, p);
+ if (sipmethod == SIP_INVITE) {
+ add_diversion_header(&req, p);
}
if (sdp) {
if (p->udptl && (p->t38.state == T38_LOCAL_DIRECT || p->t38.state == T38_LOCAL_REINVITE)) {
@@ -7492,6 +7567,72 @@
p->lastnoninvite = p->ocseq;
return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
+}
+
+/*! \brief Send a provisional response indicating that a call was redirected
+ */
+static void update_redirecting(struct sip_pvt *p, const void *data, size_t datalen)
+{
+ struct sip_request resp;
+
+ if (p->owner->_state == AST_STATE_UP || ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
+ return;
+ }
+
+ respprep(&resp, p, "181 Call is being forwarded", &p->initreq);
+ add_diversion_header(&resp, p);
+ send_response(p, &resp, XMIT_UNRELIABLE, 0);
+}
+
+/*! \brief Notify peer that the connected line has changed */
+static void update_connectedline(struct sip_pvt *p, const void *data, size_t datalen)
+{
+
+ if (!ast_test_flag(&p->flags[0], SIP_PAGE2_SENDRPID))
+ return;
+ if (ast_strlen_zero(p->owner->connected.id.number))
+ return;
+
+ append_history(p, "ConnectedLine", "%s party is now %s <%s>", ast_test_flag(&p->flags[0], SIP_OUTGOING) ? "Calling" : "Called", p->owner->connected.id.name, p->owner->connected.id.number);
+
+ if (p->owner->_state == AST_STATE_UP || ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
+ struct sip_request req;
+
+ if (p->invitestate == INV_CONFIRMED || p->invitestate == INV_TERMINATED) {
+ reqprep(&req, p, ast_test_flag(&p->flags[0], SIP_REINVITE_UPDATE) ? SIP_UPDATE : SIP_INVITE, 0, 1);
+
+ add_header(&req, "Allow", ALLOWED_METHODS);
+ add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
+ add_rpid(&req, p);
+ add_sdp(&req, p);
+
+ initialize_initreq(p, &req);
+ p->lastinvite = p->ocseq;
+ ast_set_flag(&p->flags[0], SIP_OUTGOING);
+ send_request(p, &req, XMIT_CRITICAL, p->ocseq);
+ } else {
+ reqprep(&req, p, SIP_UPDATE, 0, 1);
+ add_rpid(&req, p);
+ add_header_contentLength(&req, 0);
+ send_request(p, &req, XMIT_CRITICAL, p->ocseq);
+ }
+ } else {
+ struct sip_request resp;
+
+ if ((p->owner->_state == AST_STATE_RING) && !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT)) {
+ respprep(&resp, p, "180 Ringing", &p->initreq);
+ add_rpid(&resp, p);
+ send_response(p, &resp, XMIT_UNRELIABLE, 0);
+ ast_set_flag(&p->flags[0], SIP_RINGING);
+ } else if (p->owner->_state == AST_STATE_RINGING) {
+ respprep(&resp, p, "183 Session Progress", &p->initreq);
+ add_rpid(&resp, p);
+ send_response(p, &resp, XMIT_UNRELIABLE, 0);
+ ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
+ } else {
+ ast_log(LOG_DEBUG, "Unable able to send update to '%s' in state '%s'\n", p->owner->name, ast_state2str(p->owner->_state));
+ }
+ }
}
/*! \brief Convert registration state status to string */
@@ -8893,30 +9034,211 @@
return res;
}
-
-/*! \brief Get referring dnis */
-static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq)
-{
- char tmp[256], *c, *a;
+/*! \brief Get name, number and presentation from remote party id header,
+ * returns true if a valid header was found and it was different from the
+ * current caller id.
+ */
+static int get_rpid(struct sip_pvt *p, struct sip_request *oreq)
+{
+ char tmp[256];
struct sip_request *req;
-
+ char *cid_num = "";
+ char *cid_name = "";
+ int callingpres = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
+ char *privacy = "";
+ char *screen = "";
+ char *start, *end;
+
+ if (!ast_test_flag(&p->flags[0], SIP_TRUSTRPID))
+ return 0;
req = oreq;
if (!req)
req = &p->initreq;
+ ast_copy_string(tmp, get_header(req, "Remote-Party-ID"), sizeof(tmp));
+ if (ast_strlen_zero(tmp))
+ return 0;
+
+ start = tmp;
+ if (*start == '"') {
+ *start++ = '\0';
+ end = strchr(start, '"');
+ if (!end)
+ return 0;
+ *end++ = '\0';
+ cid_name = start;
+ start = ast_skip_blanks(end);
+ }
+
+ if (*start != '<')
+ return 0;
+ *start++ = '\0';
+ end = strchr(start, '@');
+ if (!end)
+ return 0;
+ *end++ = '\0';
+ if (strncasecmp(start, "sip:", 4))
+ return 0;
+ cid_num = start + 4;
+ if (ast_is_shrinkable_phonenumber(cid_num))
+ ast_shrink_phone_number(cid_num);
+ start = end;
+
+ end = strchr(start, '>');
+ if (!end)
+ return 0;
+ *end++ = '\0';
+ if (*end) {
+ start = end;
+ if (*start != ';')
+ return 0;
+ *start++ = '\0';
+ while (!ast_strlen_zero(start)) {
+ end = strchr(start, ';');
+ if (end)
+ *end++ = '\0';
+ if (!strncasecmp(start, "privacy=", 8))
+ privacy = start + 8;
+ else if (!strncasecmp(start, "screen=", 7))
+ screen = start + 7;
+ start = end;
+ }
+
+ if (!strcasecmp(privacy, "full")) {
+ if (!strcasecmp(screen, "yes"))
+ callingpres = AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN;
+ else if (!strcasecmp(screen, "no"))
+ callingpres = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
+ } else {
+ if (!strcasecmp(screen, "yes"))
+ callingpres = AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN;
+ else if (!strcasecmp(screen, "no"))
+ callingpres = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
+ }
+ }
+
+ /* Only return true if the supplied caller id is different */
+ if (!strcasecmp(p->cid_num, cid_num) && !strcasecmp(p->cid_name, cid_name) && p->callingpres == callingpres)
+ return 0;
+
+ ast_string_field_set(p, cid_num, cid_num);
+ ast_string_field_set(p, cid_name, cid_name);
+ p->callingpres = callingpres;
+
+ if (p->owner) {
+ ast_set_callerid(p->owner, cid_num, cid_name, NULL);
+ p->owner->cid.cid_pres = callingpres;
+ }
+
+ return 1;
+}
+
+/*! \brief Translate referring cause */
+static void sip_set_redirstr(struct sip_pvt *p, char *reason) {
+
+ if (!strcmp(reason, "unknown")) {
+ ast_string_field_set(p, redircause, "UNKNOWN");
+ } else if (!strcmp(reason, "user-busy")) {
+ ast_string_field_set(p, redircause, "BUSY");
+ } else if (!strcmp(reason, "no-answer")) {
+ ast_string_field_set(p, redircause, "NOANSWER");
+ } else if (!strcmp(reason, "unavailable")) {
+ ast_string_field_set(p, redircause, "UNREACHABLE");
+ } else if (!strcmp(reason, "unconditional")) {
+ ast_string_field_set(p, redircause, "UNCONDITIONAL");
+ } else if (!strcmp(reason, "time-of-day")) {
+ ast_string_field_set(p, redircause, "UNKNOWN");
+ } else if (!strcmp(reason, "do-not-disturb")) {
+ ast_string_field_set(p, redircause, "UNKNOWN");
+ } else if (!strcmp(reason, "deflection")) {
+ ast_string_field_set(p, redircause, "UNKNOWN");
+ } else if (!strcmp(reason, "follow-me")) {
+ ast_string_field_set(p, redircause, "UNKNOWN");
+ } else if (!strcmp(reason, "out-of-service")) {
+ ast_string_field_set(p, redircause, "UNREACHABLE");
+ } else if (!strcmp(reason, "away")) {
+ ast_string_field_set(p, redircause, "UNREACHABLE");
+ } else {
+ ast_string_field_set(p, redircause, "UNKNOWN");
+ }
+}
+
+/*! \brief Get referring dnis */
+static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, char **number, int *reason)
+{
+ char tmp[256], *exten, *rexten, *rdomain, *rname = NULL;
+ char *params, *reason_param = NULL;
+ struct sip_request *req;
+
+ req = oreq ? oreq : &p->initreq;
+
ast_copy_string(tmp, get_header(req, "Diversion"), sizeof(tmp));
if (ast_strlen_zero(tmp))
- return 0;
- c = get_in_brackets(tmp);
- if (strncasecmp(c, "sip:", 4)) {
- ast_log(LOG_WARNING, "Huh? Not an RDNIS SIP header (%s)?\n", c);
return -1;
- }
- c += 4;
- a = c;
- strsep(&a, "@;"); /* trim anything after @ or ; */
+
+ if ((params = strchr(tmp, '>'))) {
+ params = strchr(params, ';');
+ }
+
+ exten = get_in_brackets(tmp);
+ if (!strncasecmp(exten, "sip:", 4)) {
+ exten += 4;
+ } else if (!strncasecmp(exten, "sips:", 5)) {
+ exten += 5;
+ } else {
+ ast_log(LOG_WARNING, "Huh? Not an RDNIS SIP header (%s)?\n", exten);
+ return -1;
+ }
+
+ /* Get diversion-reason param if present */
+ if (params) {
+ *params = '\0'; /* Cut off parameters */
+ params++;
+ while (*params == ';' || *params == ' ')
+ params++;
+ /* Check if we have a reason parameter */
+ if ((reason_param = strcasestr(params, "reason="))) {
+ reason_param+=7;
+ /* Remove enclosing double-quotes */
+ if (*reason_param == '"')
+ ast_strip_quoted(reason_param, "\"", "\"");
+ if (!ast_strlen_zero(reason_param)) {
+ sip_set_redirstr(p, reason_param);
+ if (p->owner) {
+ pbx_builtin_setvar_helper(p->owner, "__PRIREDIRECTREASON", p->redircause);
+ pbx_builtin_setvar_helper(p->owner, "__SIPREDIRECTREASON", reason_param);
+ }
+ }
+ }
+ }
+
+ rdomain = exten;
+ rexten = strsep(&rdomain, "@"); /* trim anything after @ */
+ if (p->owner)
+ pbx_builtin_setvar_helper(p->owner, "__SIPRDNISDOMAIN", rdomain);
+
if (sip_debug_test_pvt(p))
- ast_verbose("RDNIS is %s\n", c);
- ast_string_field_set(p, rdnis, c);
+ ast_verbose("RDNIS for this call is %s (reason %s)\n", exten, reason ? reason_param : "");
+
+ /*ast_string_field_set(p, rdnis, rexten);*/
+
+ if (*tmp == '\"') {
+ char *end_quote;
+ rname = tmp + 1;
+ end_quote = strchr(rname, '\"');
+ *end_quote = '\0';
+ }
+
+ if (number) {
+ *number = ast_strdup(rexten);
+ }
+
+ if (name && rname) {
+ *name = ast_strdup(rname);
+ }
+
+ if (reason && !ast_strlen_zero(reason_param)) {
+ *reason = sip_reason_str_to_code(reason_param);
+ }
return 0;
}
@@ -9432,38 +9754,6 @@
return output;
}
-/*! \brief Get caller id number from Remote-Party-ID header field
- * Returns true if number should be restricted (privacy setting found)
- * output is set to NULL if no number found
- */
-static int get_rpid_num(const char *input, char *output, int maxlen)
-{
- char *start;
- char *end;
-
- start = strchr(input,':');
- if (!start) {
- output[0] = '\0';
- return 0;
- }
- start++;
-
- /* we found "number" */
- ast_copy_string(output,start,maxlen);
- output[maxlen-1] = '\0';
-
- end = strchr(output,'@');
- if (end)
- *end = '\0';
- else
- output[0] = '\0';
- if (strstr(input,"privacy=full") || strstr(input,"privacy=uri"))
- return AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
-
- return 0;
-}
-
-
/*! \brief Check if matching user or peer is defined
Match user on From: user name and peer on IP/port
This is used on first invite (not re-invites) and subscribe requests
@@ -9477,8 +9767,6 @@
struct sip_peer *peer;
char from[256], *c;
char *of;
- char rpid_num[50];
- const char *rpid;
enum check_auth_result res = AUTH_SUCCESSFUL;
char *t;
char calleridname[50];
@@ -9499,11 +9787,6 @@
get_calleridname(from, calleridname, sizeof(calleridname));
if (calleridname[0])
ast_string_field_set(p, cid_name, calleridname);
-
- rpid = get_header(req, "Remote-Party-ID");
- memset(rpid_num, 0, sizeof(rpid_num));
- if (!ast_strlen_zero(rpid))
- p->callingpres = get_rpid_num(rpid, rpid_num, sizeof(rpid_num));
of = get_in_brackets(from);
if (ast_strlen_zero(p->exten)) {
@@ -9721,14 +10004,19 @@
/* XXX this takes the name from the caller... can we override ? */
ast_string_field_set(p, authname, peer->username);
}
- if (!ast_strlen_zero(peer->cid_num)) {
- char *tmp = ast_strdupa(peer->cid_num);
- if (ast_is_shrinkable_phonenumber(tmp))
- ast_shrink_phone_number(tmp);
- ast_string_field_set(p, cid_num, tmp);
+ if (!get_rpid(p, req)) {
+ if (!ast_strlen_zero(peer->cid_num)) {
+ char *tmp = ast_strdupa(peer->cid_num);
+ if (ast_is_shrinkable_phonenumber(tmp))
+ ast_shrink_phone_number(tmp);
+ ast_string_field_set(p, cid_num, tmp);
+ }
+ if (!ast_strlen_zero(peer->cid_name))
+ ast_string_field_set(p, cid_name, peer->cid_name);
+ if (peer->callingpres)
+ p->callingpres = peer->callingpres;
}
- if (!ast_strlen_zero(peer->cid_name))
- ast_string_field_set(p, cid_name, peer->cid_name);
+
ast_string_field_set(p, fullcontact, peer->fullcontact);
if (!ast_strlen_zero(peer->context))
ast_string_field_set(p, context, peer->context);
@@ -10574,7 +10862,7 @@
ast_cli(fd, " User=Phone : %s\n", ast_test_flag(&peer->flags[0], SIP_USEREQPHONE)?"Yes":"No");
ast_cli(fd, " Video Support: %s\n", ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT)?"Yes":"No");
ast_cli(fd, " Trust RPID : %s\n", ast_test_flag(&peer->flags[0], SIP_TRUSTRPID) ? "Yes" : "No");
- ast_cli(fd, " Send RPID : %s\n", ast_test_flag(&peer->flags[0], SIP_SENDRPID) ? "Yes" : "No");
+ ast_cli(fd, " Send RPID : %s\n", ast_test_flag(&peer->flags[0], SIP_PAGE2_SENDRPID) ? "Yes" : "No");
ast_cli(fd, " Subscriptions: %s\n", ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE) ? "Yes" : "No");
ast_cli(fd, " Overlap dial : %s\n", ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWOVERLAP) ? "Yes" : "No");
@@ -12104,53 +12392,216 @@
"- t38passthrough 1 if T38 is offered or enabled in this channel, otherwise 0\n"
};
+/*XXX TODO Write this*/
+static int read_to_parts(struct sip_pvt *p, struct sip_request *req, char **name, char **number)
+{
+
+ char to_header[256];
+ char *to_name = NULL;
+ char *to_number = NULL;
+ char *separator;
+
+ ast_copy_string(to_header, get_header(req, "To"), sizeof(to_header));
+
+ /* Let's get that number first! */
+ to_number = get_in_brackets(to_header);
+
+ if (!strncasecmp(to_number, "sip:", 4)) {
+ to_number += 4;
+ } else if (!strncasecmp(to_number, "sips:", 5)) {
+ to_number += 5;
+ } else {
+ ast_log(LOG_WARNING, "Not a SIP URI? (%s)!\n", to_number);
+ return -1;
+ }
+
+ /* Remove the host and such since we just want the number */
+ if ((separator = strchr(to_number, '@'))) {
+ *separator = '\0';
+ }
+
+ /* We have the number. Let's get the name now. */
+
+ if (*to_header == '\"') {
+ to_name = to_header + 1;
+ if (!(separator = (char *)find_closing_quote(to_name, NULL))) {
+ ast_log(LOG_NOTICE, "No closing quote in name section of To: header (%s)\n", to_header);
+ return -1;
+ }
+ *separator = '\0';
+ }
+
+ if (number) {
+ *number = ast_strdup(to_number);
+ }
+ if (name && !ast_strlen_zero(to_name)) {
+ *name = ast_strdup(to_name);
+ }
+
+ return 0;
+}
+
+/*! \brief update redirecting information for a channel based on headers
+ *
+ */
+static void change_redirecting_information(struct sip_pvt *p, struct sip_request *req)
+{
+ char *redirecting_from_name = NULL;
+ char *redirecting_from_number = NULL;
+ char *redirecting_to_name = NULL;
+ char *redirecting_to_number = NULL;
+ int reason = AST_REDIRECTING_REASON_UNCONDITIONAL;
+ int is_response = req->method == SIP_RESPONSE;
+ int res = 0;
+
+ res = get_rdnis(p, req, &redirecting_from_name, &redirecting_from_number, &reason);
+ if (res == -1) {
+ if (is_response) {
+ read_to_parts(p, req, &redirecting_from_name, &redirecting_from_number);
+ } else {
+ return;
+ }
+ }
+
+ /* At this point, all redirecting "from" info should be filled in appropriately
+ * on to the "to" info
+ */
+
+ if (is_response) {
+ parse_moved_contact(p, req, &redirecting_to_name, &redirecting_to_number);
+ } else {
+ read_to_parts(p, req, &redirecting_to_name, &redirecting_to_number);
+ }
+
+ /* This check could probably moved up higher so we don't do any unnecessary
+ * parsing
+ */
+ if (!p->owner) {
+ return;
+ }
+
+ if (!ast_strlen_zero(redirecting_from_number)) {
+ if (p->owner->redirecting.from.number) {
+ ast_free(p->owner->redirecting.from.number);
+ }
+ if (option_debug > 2) {
+ ast_log(LOG_DEBUG, "Got redirecting from number %s\n", redirecting_from_number);
+ }
+ p->owner->redirecting.from.number = redirecting_from_number;
+ }
+ if (!ast_strlen_zero(redirecting_from_name)) {
+ if (p->owner->redirecting.from.name) {
+ ast_free(p->owner->redirecting.from.name);
+ }
+ if (option_debug > 2) {
+ ast_log(LOG_DEBUG, "Got redirecting from name %s\n", redirecting_from_name);
+ }
+ p->owner->redirecting.from.name = redirecting_from_name;
+ }
+ if (!ast_strlen_zero(redirecting_to_number)) {
+ if (p->owner->redirecting.to.number) {
+ ast_free(p->owner->redirecting.to.number);
+ }
+ if (option_debug > 2) {
+ ast_log(LOG_DEBUG, "Got redirecting to number %s\n", redirecting_to_number);
+ }
+ p->owner->redirecting.to.number = redirecting_to_number;
+ }
+ if (!ast_strlen_zero(redirecting_to_name)) {
+ if (p->owner->redirecting.to.name) {
+ ast_free(p->owner->redirecting.to.name);
+ }
+ if (option_debug > 2) {
+ ast_log(LOG_DEBUG, "Got redirecting to name %s\n", redirecting_from_number);
+ }
+ p->owner->redirecting.to.name = redirecting_to_name;
+ }
+ p->owner->redirecting.reason = reason;
+}
+
/*! \brief Parse 302 Moved temporalily response */
-static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req)
-{
- char tmp[SIPBUFSIZE];
- char *s, *e, *uri, *t;
+static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req, char **name, char **number)
+{
+ char contact[SIPBUFSIZE];
+ char *contact_name = NULL;
+ char *contact_number = NULL;
+ char *separator;
char *domain;
- ast_copy_string(tmp, get_header(req, "Contact"), sizeof(tmp));
- if ((t = strchr(tmp, ',')))
- *t = '\0';
- s = get_in_brackets(tmp);
- uri = ast_strdupa(s);
+ ast_copy_string(contact, get_header(req, "Contact"), sizeof(contact));
+ if ((separator = strchr(contact, ',')))
+ *separator = '\0';
+
+ /* ooh, a name */
+ if (*contact == '"') {
+ contact_name = contact + 1;
+ if ((separator = strchr(contact_name, '"'))) {
+ *separator++ = '\0';
+ }
+ }
+
+ contact_number = get_in_brackets(contact);
+ if ((separator = strchr(contact_number, ';'))) {
+ *separator++ = '\0';
+ }
+
if (ast_test_flag(&p->flags[0], SIP_PROMISCREDIR)) {
- if (!strncasecmp(s, "sip:", 4))
- s += 4;
- e = strchr(s, ';');
- if (e)
- *e = '\0';
- if (option_debug)
- ast_log(LOG_DEBUG, "Found promiscuous redirection to 'SIP/%s'\n", s);
+ if (!strncasecmp(contact_number, "sip:", 4))
+ contact_number += 4;
+ else if (!strncasecmp(contact_number, "sips:", 5))
+ contact_number += 5;
+ separator = strchr(contact_number, '/');
+ if (separator)
+ *separator = '\0';
+ if (option_debug > 1) {
+ ast_log(LOG_DEBUG, "Found promiscuous redirection to 'SIP/%s'\n", contact_number);
+ }
if (p->owner)
- ast_string_field_build(p->owner, call_forward, "SIP/%s", s);
+ ast_string_field_build(p->owner, call_forward, "SIP/%s", contact_number);
} else {
- e = strchr(tmp, '@');
- if (e) {
- *e++ = '\0';
- domain = e;
+ separator = strchr(contact, '@');
+ if (separator) {
+ *separator++ = '\0';
+ domain = separator;
} else {
/* No username part */
- domain = tmp;
- }
- e = strchr(s, ';'); /* Strip of parameters in the username part */
- if (e)
- *e = '\0';
- e = strchr(domain, ';'); /* Strip of parameters in the domain part */
- if (e)
- *e = '\0';
-
- if (!strncasecmp(s, "sip:", 4))
- s += 4;
- if (option_debug > 1)
- ast_log(LOG_DEBUG, "Received 302 Redirect to extension '%s' (domain %s)\n", s, domain);
+ domain = contact;
+ }
+ separator = strchr(contact, '/'); /* WHEN do we hae a forward slash in the URI? */
+ if (separator)
+ *separator = '\0';
+
+ if (!strncasecmp(contact_number, "sip:", 4))
+ contact_number += 4;
+ else if (!strncasecmp(contact_number, "sips:", 5))
+ contact_number += 5;
+ separator = strchr(contact_number, ';'); /* And username ; parameters? */
+ if (separator)
+ *separator = '\0';
+ if (option_debug < 3) {
+ ast_log(LOG_DEBUG, "Received 302 Redirect to extension '%s' (domain %s)\n", contact_number, domain);
+ }
if (p->owner) {
- pbx_builtin_setvar_helper(p->owner, "SIPREDIRECTURI", uri);
pbx_builtin_setvar_helper(p->owner, "SIPDOMAIN", domain);
- ast_string_field_set(p->owner, call_forward, s);
- }
+ ast_string_field_set(p->owner, call_forward, contact_number);
+ }
+ }
+
+ /* We've gotten the number for the contact, now get the name */
+
+ if (*contact == '\"') {
+ contact_name = contact + 1;
+ if (!(separator = (char *)find_closing_quote(contact_name, NULL))) {
+ ast_log(LOG_NOTICE, "No closing quote on name in Contact header? %s\n", contact);
+ }
+ *separator = '\0';
+ }
+
+ if (name && !ast_strlen_zero(contact_name)) {
+ *name = ast_strdup(contact_name);
+ }
+ if (number) {
+ *number = ast_strdup(contact_number);
}
}
@@ -12211,6 +12662,7 @@
int xmitres = 0;
int reinvite = (p->owner && p->owner->_state == AST_STATE_UP);
struct ast_channel *bridgepeer = NULL;
+ struct ast_party_connected_line connected;
if (option_debug > 3) {
if (reinvite)
@@ -12278,6 +12730,17 @@
if (!ast_test_flag(req, SIP_PKT_IGNORE) && (p->invitestate != INV_CANCELLED) && sip_cancel_destroy(p))
ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
/* Ignore 183 Session progress without SDP */
+ if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner) {
+ if (get_rpid(p, req)) {
+ /* Queue a connected line update */
+ ast_party_connected_line_init(&connected);
+ connected.id.number = (char *) p->cid_num;
+ connected.id.name = (char *) p->cid_name;
+ connected.id.number_presentation = p->callingpres;
+ connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
+ ast_queue_connected_line_update(p->owner, &connected);
+ }
+ }
if (find_sdp(req)) {
if (p->invitestate != INV_CANCELLED)
p->invitestate = INV_EARLY_MEDIA;
@@ -12372,6 +12835,9 @@
if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner) {
if (!reinvite) {
+ struct ast_party_connected_line connected;
+ ast_party_connected_line_collect_caller(&connected, &p->owner->cid);
+ ast_queue_connected_line_update(p->owner, &connected);
ast_queue_control(p->owner, AST_CONTROL_ANSWER);
} else { /* RE-invite */
ast_queue_frame(p->owner, &ast_null_frame);
@@ -13028,7 +13494,7 @@
case 301: /* Moved permenantly */
case 302: /* Moved temporarily */
case 305: /* Use Proxy */
- parse_moved_contact(p, req);
+ change_redirecting_information(p, req);
/* Fall through */
case 486: /* Busy here */
case 600: /* Busy everywhere */
@@ -14082,10 +14548,11 @@
}
-/*! \brief Handle incoming INVITE request
-\note If the INVITE has a Replaces header, it is part of an
+/*!
+ * \brief Handle incoming INVITE request
+ * \note If the INVITE has a Replaces header, it is part of an
* attended transfer. If so, we do not go through the dial
- * plan but tries to find the active call and masquerade
+ * plan but try to find the active call and masquerade
* into it
*/
static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin, int *recount, char *e, int *nounlock)
@@ -14305,6 +14772,16 @@
parse_ok_contact(p, req);
} else { /* Re-invite on existing call */
ast_clear_flag(&p->flags[0], SIP_OUTGOING); /* This is now an inbound dialog */
+ if (get_rpid(p, req)) {
+ struct ast_party_connected_line connected;
+
+ ast_party_connected_line_init(&connected);
+ connected.id.number = (char *) p->cid_num;
+ connected.id.name = (char *) p->cid_name;
+ connected.id.number_presentation = p->callingpres;
[... 91 lines stripped ...]
More information about the asterisk-commits
mailing list