Newer
Older
if (!strcasecmp(req->rlPart1, "SIP/2.0") ) { /* We have a response */
if (strlen(e) < 3) /* status code is 3 digits */
req->rlPart2 = e;
} else { /* We have a request */
if ( *e == '<' ) { /* XXX the spec says it must not be in <> ! */
ast_log(LOG_WARNING, "bogus uri in <> %s\n", e);
req->rlPart2 = e; /* URI */
e = ast_skip_nonblanks(e);
if (*e)
*e++ = '\0';
e = ast_skip_blanks(e);
if (strcasecmp(e, "SIP/2.0") ) {
ast_log(LOG_WARNING, "Bad request protocol %s\n", e);
/*! \brief Transmit reinvite with SDP
\note A re-invite is basically a new INVITE with the same CALL-ID and TAG as the
INVITE that opened the SIP dialogue
We reinvite so that the audio stream (RTP) go directly between
the SIP UAs. SIP Signalling stays with * in the path.
*/
static int transmit_reinvite_with_sdp(struct sip_pvt *p)
struct sip_request req;
reqprep(&req, p, ast_test_flag(&p->flags[0], SIP_REINVITE_UPDATE) ? SIP_UPDATE : SIP_INVITE, 0, 1);
Mark Spencer
committed
add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
Russell Bryant
committed
if (sipdebug)
add_header(&req, "X-asterisk-Info", "SIP re-invite (External RTP bridge)");
add_sdp(&req, p);
/* Use this as the basis */
Olle Johansson
committed
initialize_initreq(p, &req);
p->lastinvite = p->ocseq;
ast_set_flag(&p->flags[0], SIP_OUTGOING);
return send_request(p, &req, 1, p->ocseq);
/*! \brief Check Contact: URI of SIP message */
static void extract_uri(struct sip_pvt *p, struct sip_request *req)
{
char stripped[256];
Kevin P. Fleming
committed
ast_copy_string(stripped, get_header(req, "Contact"), sizeof(stripped));
c = get_in_brackets(stripped);
n = strchr(c, ';');
if (n)
*n = '\0';
if (!ast_strlen_zero(c))
ast_string_field_set(p, uri, c);
/*! \brief Build contact header - the contact header we send out */
static void build_contact(struct sip_pvt *p)
{
char iabuf[INET_ADDRSTRLEN];
/* Construct Contact: header */
if (ourport != 5060) /* Needs to be 5060, according to the RFC */
ast_string_field_build(p, our_contact, "<sip:%s%s%s:%d>", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport);
ast_string_field_build(p, our_contact, "<sip:%s%s%s>", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip));
/*! \brief Build the Remote Party-ID & From using callingpres options */
Kevin P. Fleming
committed
static void build_rpid(struct sip_pvt *p)
{
const char *privacy=NULL;
const char *screen=NULL;
Kevin P. Fleming
committed
char buf[256];
const char *clid = default_callerid;
const char *clin = NULL;
char iabuf[INET_ADDRSTRLEN];
const char *fromdomain;
if (!ast_strlen_zero(p->rpid) || !ast_strlen_zero(p->rpid_from))
Kevin P. Fleming
committed
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))
Kevin P. Fleming
committed
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129
5130
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 = "pass";
break;
case AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN:
privacy = "off";
screen = "fail";
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 = "pass";
break;
case AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN:
privacy = "full";
screen = "fail";
break;
case AST_PRES_PROHIB_NETWORK_NUMBER:
privacy = "full";
Kevin P. Fleming
committed
break;
case AST_PRES_NUMBER_NOT_AVAILABLE:
Kevin P. Fleming
committed
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(iabuf, sizeof(iabuf), p->ourip));
Kevin P. Fleming
committed
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);
Kevin P. Fleming
committed
ast_string_field_build(p, rpid_from, "\"%s\" <sip:%s@%s>;tag=%s", clin,
fromdomain, p->tag);
Kevin P. Fleming
committed
}
/*! \brief Initiate new SIP request to peer/user */
static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod)
char invite_buf[256] = "";
char *invite = invite_buf;
size_t invite_max = sizeof(invite_buf);
char tmp[BUFSIZ/2];
char tmp2[BUFSIZ/2];
char iabuf[INET_ADDRSTRLEN];
const char *l = NULL, *n = NULL;
int x;
char urioptions[256]="";
if (ast_test_flag(&p->flags[0], SIP_USEREQPHONE)) {
/* Test p->username against allowed characters in AST_DIGIT_ANY
If it matches the allowed characters list, then sipuser = ";user=phone"
If not, then sipuser = ""
*/
/* + is allowed in first position in a tel: uri */
if (p->username && p->username[0] == '+')
for (; x < strlen(p->username); x++) {
if (!strchr(AST_DIGIT_ANYNUM, p->username[x])) {
/* If we have only digits, add ;user=phone to the uri */
if (onlydigits)
strcpy(urioptions, ";user=phone");
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)) {
Martin Pycko
committed
l = CALLERID_UNKNOWN;
n = l;
}
if (!l)
l = default_callerid;
if (ast_strlen_zero(n))
Mark Spencer
committed
/* Allow user to be overridden */
if (!ast_strlen_zero(p->fromuser))
Mark Spencer
committed
l = p->fromuser;
else /* Save for any further attempts */
ast_string_field_set(p, fromuser, l);
/* Allow user to be overridden */
if (!ast_strlen_zero(p->fromname))
n = p->fromname;
else /* Save for any further attempts */
ast_string_field_set(p, fromname, n);
if (pedanticsipchecking) {
ast_uri_encode(n, tmp, sizeof(tmp), 0);
n = tmp;
ast_uri_encode(l, tmp2, sizeof(tmp2), 0);
l = tmp2;
}
if ((ourport != 5060) && ast_strlen_zero(p->fromdomain)) /* Needs to be 5060 */
snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s:%d>;tag=%s", n, l, S_OR(p->fromdomain, ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip)), ourport, p->tag);
Mark Spencer
committed
else
snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s>;tag=%s", n, l, S_OR(p->fromdomain, ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip)), p->tag);
/* If we're calling a registered SIP peer, use the fullcontact to dial to the peer */
if (!ast_strlen_zero(p->fullcontact)) {
/* If we have full contact, trust it */
ast_build_string(&invite, &invite_max, "%s", p->fullcontact);
/* Otherwise, use the username while waiting for registration */
ast_build_string(&invite, &invite_max, "sip:");
if (!ast_strlen_zero(p->username)) {
n = p->username;
if (pedanticsipchecking) {
ast_uri_encode(n, tmp, sizeof(tmp), 0);
n = tmp;
}
ast_build_string(&invite, &invite_max, "%s@", n);
}
ast_build_string(&invite, &invite_max, "%s", p->tohost);
if (ntohs(p->sa.sin_port) != 5060) /* Needs to be 5060 */
ast_build_string(&invite, &invite_max, ":%d", ntohs(p->sa.sin_port));
ast_build_string(&invite, &invite_max, "%s", urioptions);
/* If custom URI options have been provided, append them */
if (p->options && p->options->uri_options)
ast_build_string(&invite, &invite_max, ";%s", p->options->uri_options);
ast_string_field_set(p, uri, invite_buf);
if (sipmethod == SIP_NOTIFY && !ast_strlen_zero(p->theirtag)) {
/* If this is a NOTIFY, use the From: tag in the subscribe (RFC 3265) */
snprintf(to, sizeof(to), "<sip:%s>;tag=%s", p->uri, p->theirtag);
} else if (p->options && p->options->vxml_url) {
/* If there is a VXML URL append it to the SIP URL */
snprintf(to, sizeof(to), "<%s>;%s", p->uri, p->options->vxml_url);
} else {
snprintf(to, sizeof(to), "<%s>", p->uri);
init_req(req, sipmethod, p->uri);
snprintf(tmp, sizeof(tmp), "%d %s", ++p->ocseq, sip_methods[sipmethod].text);
/* 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 */
Kevin P. Fleming
committed
/* Build Remote Party-ID and From */
if (ast_test_flag(&p->flags[0], SIP_SENDRPID) && (sipmethod == SIP_INVITE)) {
Kevin P. Fleming
committed
build_rpid(p);
Kevin P. Fleming
committed
add_header(req, "From", p->rpid_from);
} else {
add_header(req, "From", from);
}
Kevin P. Fleming
committed
add_header(req, "To", to);
ast_string_field_set(p, exten, l);
add_header(req, "Contact", p->our_contact);
add_header(req, "Call-ID", p->callid);
add_header(req, "CSeq", tmp);
add_header(req, "User-Agent", global_useragent);
add_header(req, "Max-Forwards", DEFAULT_MAX_FORWARDS);
if (!ast_strlen_zero(p->rpid))
Kevin P. Fleming
committed
add_header(req, "Remote-Party-ID", p->rpid);
/*! \brief Build REFER/INVITE/OPTIONS message and transmit it */
Kevin P. Fleming
committed
static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init)
req.method = sipmethod;
if (init) {
/* Bump branch even on initial requests */
Tilghman Lesher
committed
p->branch ^= ast_random();
build_via(p);
if (init > 1)
initreqprep(&req, p, sipmethod);
else
reqprep(&req, p, sipmethod, 0, 1);
reqprep(&req, p, sipmethod, 0, 1);
Kevin P. Fleming
committed
if (p->options && p->options->auth)
add_header(&req, p->options->authheader, p->options->auth);
if (sipmethod == SIP_REFER) { /* Call transfer */
if (p->refer) {
char buf[BUFSIZ];
if (!ast_strlen_zero(p->refer->refer_to))
add_header(&req, "Refer-To", p->refer->refer_to);
if (!ast_strlen_zero(p->refer->referred_by)) {
sprintf(buf, "%s <%s>", p->refer->referred_by_name, p->refer->referred_by);
add_header(&req, "Referred-By", buf);
}
}
/* This new INVITE is part of an attended transfer. Make sure that the
other end knows and replace the current call with this new call */
if (p->options && p->options->replaces && !ast_strlen_zero(p->options->replaces)) {
add_header(&req, "Replaces", p->options->replaces);
}
if (p->options && !ast_strlen_zero(p->options->distinctive_ring)) {
Kevin P. Fleming
committed
add_header(&req, "Alert-Info", p->options->distinctive_ring);
add_header(&req, "Allow", ALLOWED_METHODS);
add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
Kevin P. Fleming
committed
if (p->options && p->options->addsipheaders ) {
struct ast_channel *ast;
struct varshead *headp = NULL;
Russell Bryant
committed
const struct ast_var_t *current;
ast = p->owner; /* The owner channel */
if (ast) {
Kevin P. Fleming
committed
char *headdup;
headp = &ast->varshead;
if (!headp)
ast_log(LOG_WARNING,"No Headp for the channel...ooops!\n");
else {
Kevin P. Fleming
committed
AST_LIST_TRAVERSE(headp, current, entries) {
/* SIPADDHEADER: Add SIP header to outgoing call */
Kevin P. Fleming
committed
if (!strncasecmp(ast_var_name(current), "SIPADDHEADER", strlen("SIPADDHEADER"))) {
char *content, *end;
const char *header = ast_var_value(current);
Kevin P. Fleming
committed
headdup = ast_strdupa(header);
/* Strip of the starting " (if it's there) */
Kevin P. Fleming
committed
if (*headdup == '"')
headdup++;
if ((content = strchr(headdup, ':'))) {
*content++ = '\0';
content = ast_skip_blanks(content); /* Skip white space */
/* Strip the ending " (if it's there) */
end = content + strlen(content) -1;
if (*end == '"')
Kevin P. Fleming
committed
add_header(&req, headdup, content);
Russell Bryant
committed
if (sipdebug)
Kevin P. Fleming
committed
ast_log(LOG_DEBUG, "Adding SIP Header \"%s\" with content :%s: \n", headdup, content);
}
}
}
}
}
}
if (sdp && p->rtp) {
add_sdp(&req, p);
add_header_contentLength(&req, 0);
Olle Johansson
committed
if (!p->initreq.headers)
initialize_initreq(p, &req);
return send_request(p, &req, init ? 2 : 1, p->ocseq);
/*! \brief Used in the SUBSCRIBE notification subsystem */
static int transmit_state_notify(struct sip_pvt *p, int state, int full)
char tmp[4000], from[256], to[256];
char *t = tmp, *c, *a, *mfrom, *mto;
size_t maxbytes = sizeof(tmp);
struct sip_request req;
Kevin P. Fleming
committed
char hint[AST_MAX_EXTENSION];
char *statestring = "terminated";
const struct cfsubscription_types *subscriptiontype;
enum state { NOTIFY_OPEN, NOTIFY_INUSE, NOTIFY_CLOSED } local_state = NOTIFY_OPEN;
char *pidfstate = "--";
char *pidfnote= "Ready";
Kevin P. Fleming
committed
memset(from, 0, sizeof(from));
memset(to, 0, sizeof(to));
memset(tmp, 0, sizeof(tmp));
Kevin P. Fleming
committed
switch (state) {
case (AST_EXTENSION_RINGING | AST_EXTENSION_INUSE):
statestring = (global_notifyringing) ? "early" : "confirmed";
Kevin P. Fleming
committed
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
5426
5427
5428
5429
5430
5431
5432
5433
5434
5435
5436
5437
5438
5439
5440
5441
5442
5443
5444
5445
5446
5447
5448
5449
5450
5451
5452
5453
5454
5455
5456
5457
5458
5459
local_state = NOTIFY_INUSE;
pidfstate = "busy";
pidfnote = "Ringing";
break;
case AST_EXTENSION_RINGING:
statestring = "early";
local_state = NOTIFY_INUSE;
pidfstate = "busy";
pidfnote = "Ringing";
break;
case AST_EXTENSION_INUSE:
statestring = "confirmed";
local_state = NOTIFY_INUSE;
pidfstate = "busy";
pidfnote = "On the phone";
break;
case AST_EXTENSION_BUSY:
statestring = "confirmed";
local_state = NOTIFY_CLOSED;
pidfstate = "busy";
pidfnote = "On the phone";
break;
case AST_EXTENSION_UNAVAILABLE:
statestring = "confirmed";
local_state = NOTIFY_CLOSED;
pidfstate = "away";
pidfnote = "Unavailable";
break;
case AST_EXTENSION_NOT_INUSE:
default:
/* Default setting */
break;
}
subscriptiontype = find_subscription_type(p->subscribed);
/* Check which device/devices we are watching and if they are registered */
if (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, p->context, p->exten)) {
/* If they are not registered, we will override notification and show no availability */
if (ast_device_state(hint) == AST_DEVICE_UNAVAILABLE) {
local_state = NOTIFY_CLOSED;
pidfstate = "away";
pidfnote = "Not online";
}
}
Mark Spencer
committed
Kevin P. Fleming
committed
ast_copy_string(from, get_header(&p->initreq, "From"), sizeof(from));
Kevin P. Fleming
committed
c = get_in_brackets(from);
if (strncmp(c, "sip:", 4)) {
ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c);
return -1;
}
Kevin P. Fleming
committed
if ((a = strchr(c, ';')))
*a = '\0';
mfrom = c;
Mark Spencer
committed
Kevin P. Fleming
committed
ast_copy_string(to, get_header(&p->initreq, "To"), sizeof(to));
c = get_in_brackets(to);
if (strncmp(c, "sip:", 4)) {
ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c);
return -1;
}
if ((a = strchr(c, ';')))
*a = '\0';
mto = c;
Kevin P. Fleming
committed
reqprep(&req, p, SIP_NOTIFY, 0, 1);
Kevin P. Fleming
committed
add_header(&req, "Event", subscriptiontype->event);
add_header(&req, "Content-Type", subscriptiontype->mediatype);
switch(state) {
case AST_EXTENSION_DEACTIVATED:
if (p->subscribed == TIMEOUT)
add_header(&req, "Subscription-State", "terminated;reason=timeout");
else {
add_header(&req, "Subscription-State", "terminated;reason=probation");
add_header(&req, "Retry-After", "60");
Mark Spencer
committed
}
Kevin P. Fleming
committed
break;
case AST_EXTENSION_REMOVED:
add_header(&req, "Subscription-State", "terminated;reason=noresource");
break;
default:
if (p->expiry)
add_header(&req, "Subscription-State", "active");
else /* Expired */
add_header(&req, "Subscription-State", "terminated;reason=timeout");
Kevin P. Fleming
committed
}
switch (p->subscribed) {
case XPIDF_XML:
case CPIM_PIDF_XML:
ast_build_string(&t, &maxbytes, "<?xml version=\"1.0\"?>\n");
ast_build_string(&t, &maxbytes, "<!DOCTYPE presence PUBLIC \"-//IETF//DTD RFCxxxx XPIDF 1.0//EN\" \"xpidf.dtd\">\n");
ast_build_string(&t, &maxbytes, "<presence>\n");
ast_build_string(&t, &maxbytes, "<presentity uri=\"%s;method=SUBSCRIBE\" />\n", mfrom);
ast_build_string(&t, &maxbytes, "<atom id=\"%s\">\n", p->exten);
ast_build_string(&t, &maxbytes, "<address uri=\"%s;user=ip\" priority=\"0.800000\">\n", mto);
Kevin P. Fleming
committed
ast_build_string(&t, &maxbytes, "<status status=\"%s\" />\n", (local_state == NOTIFY_OPEN) ? "open" : (local_state == NOTIFY_INUSE) ? "inuse" : "closed");
ast_build_string(&t, &maxbytes, "<msnsubstatus substatus=\"%s\" />\n", (local_state == NOTIFY_OPEN) ? "online" : (local_state == NOTIFY_INUSE) ? "onthephone" : "offline");
ast_build_string(&t, &maxbytes, "</address>\n</atom>\n</presence>\n");
Kevin P. Fleming
committed
break;
case PIDF_XML: /* Eyebeam supports this format */
ast_build_string(&t, &maxbytes, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
Kevin P. Fleming
committed
ast_build_string(&t, &maxbytes, "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\" \nxmlns:pp=\"urn:ietf:params:xml:ns:pidf:person\"\nxmlns:es=\"urn:ietf:params:xml:ns:pidf:rpid:status:rpid-status\"\nxmlns:ep=\"urn:ietf:params:xml:ns:pidf:rpid:rpid-person\"\nentity=\"%s\">\n", mfrom);
Kevin P. Fleming
committed
ast_build_string(&t, &maxbytes, "<pp:person><status>\n");
if (pidfstate[0] != '-')
Kevin P. Fleming
committed
ast_build_string(&t, &maxbytes, "<ep:activities><ep:%s/></ep:activities>\n", pidfstate);
Kevin P. Fleming
committed
ast_build_string(&t, &maxbytes, "</status></pp:person>\n");
ast_build_string(&t, &maxbytes, "<note>%s</note>\n", pidfnote); /* Note */
ast_build_string(&t, &maxbytes, "<tuple id=\"%s\">\n", p->exten); /* Tuple start */
ast_build_string(&t, &maxbytes, "<contact priority=\"1\">%s</contact>\n", mto);
if (pidfstate[0] == 'b') /* Busy? Still open ... */
ast_build_string(&t, &maxbytes, "<status><basic>open</basic></status>\n");
else
ast_build_string(&t, &maxbytes, "<status><basic>%s</basic></status>\n", (local_state != NOTIFY_CLOSED) ? "open" : "closed");
ast_build_string(&t, &maxbytes, "</tuple>\n</presence>\n");
break;
case DIALOG_INFO_XML: /* SNOM subscribes in this format */
ast_build_string(&t, &maxbytes, "<?xml version=\"1.0\"?>\n");
Kevin P. Fleming
committed
ast_build_string(&t, &maxbytes, "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"%d\" state=\"%s\" entity=\"%s\">\n", p->dialogver++, full ? "full":"partial", mto);
if ((state & AST_EXTENSION_RINGING) && global_notifyringing)
ast_build_string(&t, &maxbytes, "<dialog id=\"%s\" direction=\"recipient\">\n", p->exten);
else
ast_build_string(&t, &maxbytes, "<dialog id=\"%s\">\n", p->exten);
ast_build_string(&t, &maxbytes, "<state>%s</state>\n", statestring);
ast_build_string(&t, &maxbytes, "</dialog>\n</dialog-info>\n");
break;
case NONE:
default:
break;
Kevin P. Fleming
committed
if (t > tmp + sizeof(tmp))
ast_log(LOG_WARNING, "Buffer overflow detected!! (Please file a bug report)\n");
add_header_contentLength(&req, strlen(tmp));
add_line(&req, tmp);
return send_request(p, &req, 1, p->ocseq);
}
/*! \brief Notify user of messages waiting in voicemail
\note - Notification only works for registered peers with mailbox= definitions
in sip.conf
- We use the SIP Event package message-summary
MIME type defaults to "application/simple-message-summary";
Kevin P. Fleming
committed
static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs, char *vmexten)
char tmp[500];
char *t = tmp;
size_t maxbytes = sizeof(tmp);
initreqprep(&req, p, SIP_NOTIFY);
add_header(&req, "Event", "message-summary");
add_header(&req, "Content-Type", default_notifymime);
ast_build_string(&t, &maxbytes, "Messages-Waiting: %s\r\n", newmsgs ? "yes" : "no");
ast_build_string(&t, &maxbytes, "Message-Account: sip:%s@%s\r\n",
S_OR(vmexten, default_vmexten), S_OR(p->fromdomain, ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip)));
ast_build_string(&t, &maxbytes, "Voice-Message: %d/%d (0/0)\r\n", newmsgs, oldmsgs);
Kevin P. Fleming
committed
if (p->subscribed) {
if (p->expiry)
add_header(&req, "Subscription-State", "active");
else /* Expired */
add_header(&req, "Subscription-State", "terminated;reason=timeout");
}
if (t > tmp + sizeof(tmp))
ast_log(LOG_WARNING, "Buffer overflow detected!! (Please file a bug report)\n");
add_header_contentLength(&req, strlen(tmp));
Olle Johansson
committed
if (!p->initreq.headers)
initialize_initreq(p, &req);
/*! \brief Transmit SIP request */
static int transmit_sip_request(struct sip_pvt *p,struct sip_request *req)
{
Olle Johansson
committed
if (!p->initreq.headers)
initialize_initreq(p, req);
return send_request(p, req, 0, p->ocseq);
/*! \brief Notify a transferring party of the status of transfer */
static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *message, int terminate)
{
struct sip_request req;
char tmp[BUFSIZ/2];
reqprep(&req, p, SIP_NOTIFY, 0, 1);
snprintf(tmp, sizeof(tmp), "refer;id=%d", cseq);
add_header(&req, "Event", tmp);
if (terminate)
add_header(&req, "Subscription-state", "terminated;reason=noresource");
add_header(&req, "Content-Type", "message/sipfrag;version=2.0");
Olle Johansson
committed
add_header(&req, "Allow", ALLOWED_METHODS);
add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
Olle Johansson
committed
snprintf(tmp, sizeof(tmp), "SIP/2.0 %s\r\n", message);
add_header_contentLength(&req, strlen(tmp));
add_line(&req, tmp);
Olle Johansson
committed
if (!p->initreq.headers)
initialize_initreq(p, &req);
return send_request(p, &req, 1, p->ocseq);
}
/*! \build Convert registration state status to string */
static char *regstate2str(enum sipregistrystate regstate)
case REG_STATE_FAILED:
return "Failed";
case REG_STATE_UNREGISTERED:
return "Unregistered";
case REG_STATE_REGSENT:
return "Request Sent";
case REG_STATE_AUTHSENT:
return "Auth. Sent";
case REG_STATE_REGISTERED:
return "Registered";
case REG_STATE_REJECTED:
return "Rejected";
case REG_STATE_TIMEOUT:
return "Timeout";
case REG_STATE_NOAUTH:
return "No Authentication";
default:
return "Unknown";
}
}
/*! \brief Update registration with SIP Proxy*/
static int sip_reregister(void *data)
{
/* if we are here, we know that we need to reregister. */
struct sip_registry *r= ASTOBJ_REF((struct sip_registry *) data);
/* if we couldn't get a reference to the registry object, punt */
if (!r)
return 0;
Mark Spencer
committed
Olle Johansson
committed
if (r->call && recordhistory)
append_history(r->call, "RegistryRenew", "Account: %s@%s", r->username, r->hostname);
Mark Spencer
committed
/* Since registry's are only added/removed by the the monitor thread, this
may be overkill to reference/dereference at all here */
Russell Bryant
committed
if (sipdebug)
Mark Spencer
committed
ast_log(LOG_NOTICE, " -- Re-registration for %s@%s\n", r->username, r->hostname);
__sip_do_register(r);
ASTOBJ_UNREF(r, sip_registry_destroy);
/*! \brief Register with SIP proxy */
static int __sip_do_register(struct sip_registry *r)
res = transmit_register(r, SIP_REGISTER, NULL, NULL);
/*! \brief Registration timeout, register again */
Mark Spencer
committed
/* if we are here, our registration timed out, so we'll just do it over */
struct sip_registry *r = ASTOBJ_REF((struct sip_registry *) data);
struct sip_pvt *p;
Mark Spencer
committed
/* if we couldn't get a reference to the registry object, punt */
if (!r)
return 0;
Kevin P. Fleming
committed
ast_log(LOG_NOTICE, " -- Registration for '%s@%s' timed out, trying again (Attempt #%d)\n", r->username, r->hostname, r->regattempts);
/* Unlink us, destroy old call. Locking is not relevant here because all this happens
in the single SIP manager thread. */
p = r->call;
Mark Spencer
committed
if (p->registry)
ASTOBJ_UNREF(p->registry, sip_registry_destroy);
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
/* Pretend to ACK anything just in case */
__sip_pretend_ack(p); /* XXX we need p locked, not sure we have */
Kevin P. Fleming
committed
/* If we have a limit, stop registration and give up */
if (global_regattempts_max && (r->regattempts > global_regattempts_max)) {
Kevin P. Fleming
committed
/* Ok, enough is enough. Don't try any more */
/* We could add an external notification here...
steal it from app_voicemail :-) */
ast_log(LOG_NOTICE, " -- Giving up forever trying to register '%s@%s'\n", r->username, r->hostname);
r->regstate = REG_STATE_FAILED;
Kevin P. Fleming
committed
} else {
r->regstate = REG_STATE_UNREGISTERED;
Kevin P. Fleming
committed
r->timeout = -1;
res=transmit_register(r, SIP_REGISTER, NULL, NULL);
}
manager_event(EVENT_FLAG_SYSTEM, "Registry", "Channel: SIP\r\nUsername: %s\r\nDomain: %s\r\nStatus: %s\r\n", r->username, r->hostname, regstate2str(r->regstate));
ASTOBJ_UNREF(r, sip_registry_destroy);
/*! \brief Transmit register to SIP proxy or UA */
static int transmit_register(struct sip_registry *r, int sipmethod, char *auth, char *authheader)
{
struct sip_request req;
char from[256];
char to[256];
char tmp[80];
char addr[80];
struct sip_pvt *p;
/* exit if we are already in process with this registrar ?*/
if ( r == NULL || ((auth==NULL) && (r->regstate==REG_STATE_REGSENT || r->regstate==REG_STATE_AUTHSENT))) {
Kevin P. Fleming
committed
ast_log(LOG_NOTICE, "Strange, trying to register %s@%s when registration already pending\n", r->username, r->hostname);
Kevin P. Fleming
committed
if (r->call) { /* We have a registration */
Kevin P. Fleming
committed
ast_log(LOG_WARNING, "Already have a REGISTER going on to %s@%s?? \n", r->username, r->hostname);
Kevin P. Fleming
committed
make_our_tag(p->tag, sizeof(p->tag)); /* create a new local tag for every register attempt */
ast_string_field_free(p, theirtag); /* forget their old tag, so we don't match tags when getting response */
Kevin P. Fleming
committed
/* Build callid for registration if we haven't registered before */
build_callid_registry(r, __ourip, default_fromdomain);
Mark Spencer
committed
/* Allocate SIP packet for registration */
Olle Johansson
committed
if (!(p = sip_alloc( r->callid, NULL, 0, SIP_REGISTER))) {
ast_log(LOG_WARNING, "Unable to allocate registration transaction (memory or socket error)\n");
Olle Johansson
committed
if (recordhistory)
append_history(p, "RegistryInit", "Account: %s@%s", r->username, r->hostname);
Mark Spencer
committed
/* Find address to hostname */
Mark Spencer
committed
if (create_addr(p, r->hostname)) {
/* we have what we hope is a temporary network error,
* probably DNS. We need to reschedule a registration try */
sip_destroy(p);
if (r->timeout > -1) {
ast_sched_del(sched, r->timeout);
Kevin P. Fleming
committed
r->timeout = ast_sched_add(sched, global_reg_timeout*1000, sip_reg_timeout, r);
ast_log(LOG_WARNING, "Still have a registration timeout for %s@%s (create_addr() error), %d\n", r->username, r->hostname, r->timeout);
} else {
r->timeout = ast_sched_add(sched, global_reg_timeout*1000, sip_reg_timeout, r);
ast_log(LOG_WARNING, "Probably a DNS error for registration to %s@%s, trying REGISTER again (after %d seconds)\n", r->username, r->hostname, global_reg_timeout);
Kevin P. Fleming
committed
r->regattempts++;
return 0;
}
/* Copy back Call-ID in case create_addr changed it */
ast_string_field_set(r, callid, p->callid);
if (r->portno)
p->sa.sin_port = htons(r->portno);
Olle Johansson
committed
else /* Set registry port to the port set from the peer definition/srv or default */
r->portno = ntohs(p->sa.sin_port);
ast_set_flag(&p->flags[0], SIP_OUTGOING); /* Registration is outgoing call */
Mark Spencer
committed
r->call=p; /* Save pointer to SIP packet */
p->registry = ASTOBJ_REF(r); /* Add pointer to registry in packet */
Mark Spencer
committed
if (!ast_strlen_zero(r->secret)) /* Secret (password) */
ast_string_field_set(p, peersecret, r->secret);
if (!ast_strlen_zero(r->md5secret))
ast_string_field_set(p, peermd5secret, r->md5secret);
Mark Spencer
committed
/* User name in this realm
- if authuser is set, use that, otherwise use username */
if (!ast_strlen_zero(r->authuser)) {
ast_string_field_set(p, peername, r->authuser);
ast_string_field_set(p, authname, r->authuser);
} else if (!ast_strlen_zero(r->username)) {
ast_string_field_set(p, peername, r->username);
ast_string_field_set(p, authname, r->username);
ast_string_field_set(p, fromuser, r->username);
Mark Spencer
committed
}
if (!ast_strlen_zero(r->username))
ast_string_field_set(p, username, r->username);
Mark Spencer
committed
/* Save extension in packet */
ast_string_field_set(p, exten, r->contact);
/*
check which address we should use in our contact header
based on whether the remote host is on the external or
internal network so we can register through nat
*/
if (ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip))
p->ourip = bindaddr.sin_addr;
Kevin P. Fleming
committed
if (auth == NULL) {
if (r->timeout > -1) {
ast_log(LOG_WARNING, "Still have a registration timeout, #%d - deleting it\n", r->timeout);
ast_sched_del(sched, r->timeout);
}
Kevin P. Fleming
committed
r->timeout = ast_sched_add(sched, global_reg_timeout * 1000, sip_reg_timeout, r);
ast_log(LOG_DEBUG, "Scheduled a registration timeout for %s id #%d \n", r->hostname, r->timeout);
if (strchr(r->username, '@')) {
Kevin P. Fleming
committed
snprintf(from, sizeof(from), "<sip:%s>;tag=%s", r->username, p->tag);
if (!ast_strlen_zero(p->theirtag))
snprintf(to, sizeof(to), "<sip:%s>;tag=%s", r->username, p->theirtag);
else
snprintf(to, sizeof(to), "<sip:%s>", r->username);
Kevin P. Fleming
committed
snprintf(from, sizeof(from), "<sip:%s@%s>;tag=%s", r->username, p->tohost, p->tag);
if (!ast_strlen_zero(p->theirtag))
snprintf(to, sizeof(to), "<sip:%s@%s>;tag=%s", r->username, p->tohost, p->theirtag);
else
snprintf(to, sizeof(to), "<sip:%s@%s>", r->username, p->tohost);
Kevin P. Fleming
committed
/* Fromdomain is what we are registering to, regardless of actual
host name from SRV */
if (!ast_strlen_zero(p->fromdomain))
Kevin P. Fleming
committed
snprintf(addr, sizeof(addr), "sip:%s", p->fromdomain);
else
snprintf(addr, sizeof(addr), "sip:%s", r->hostname);
ast_string_field_set(p, uri, addr);
Tilghman Lesher
committed
p->branch ^= ast_random();
init_req(&req, sipmethod, addr);
Mark Spencer
committed
/* Add to CSEQ */
snprintf(tmp, sizeof(tmp), "%u %s", ++r->ocseq, sip_methods[sipmethod].text);
build_via(p);
add_header(&req, "Via", p->via);
add_header(&req, "User-Agent", global_useragent);
add_header(&req, "Max-Forwards", DEFAULT_MAX_FORWARDS);
Mark Spencer
committed
if (auth) /* Add auth header */
add_header(&req, authheader, auth);
else if (!ast_strlen_zero(r->nonce)) {
Mark Spencer
committed
char digest[1024];
/* We have auth data to reuse, build a digest header! */
Russell Bryant
committed
if (sipdebug)
Mark Spencer
committed
ast_log(LOG_DEBUG, " >>> Re-using Auth data for %s@%s\n", r->username, r->hostname);
ast_string_field_set(p, realm, r->realm);
ast_string_field_set(p, nonce, r->nonce);
ast_string_field_set(p, domain, r->domain);
ast_string_field_set(p, opaque, r->opaque);
ast_string_field_set(p, qop, r->qop);
p->noncecount = r->noncecount++;
Mark Spencer
committed
memset(digest,0,sizeof(digest));
Kevin P. Fleming
committed
if(!build_reply_digest(p, sipmethod, digest, sizeof(digest)))
add_header(&req, "Authorization", digest);
else
ast_log(LOG_NOTICE, "No authorization available for authentication of registration to %s@%s\n", r->username, r->hostname);
Mark Spencer
committed
}
snprintf(tmp, sizeof(tmp), "%d", default_expiry);
add_header(&req, "Contact", p->our_contact);
add_header_contentLength(&req, 0);
Olle Johansson
committed
initialize_initreq(p, &req);
if (sip_debug_test_pvt(p))
Kevin P. Fleming
committed
ast_verbose("REGISTER %d headers, %d lines\n", p->initreq.headers, p->initreq.lines);
r->regstate = auth ? REG_STATE_AUTHSENT : REG_STATE_REGSENT;
Kevin P. Fleming
committed
r->regattempts++; /* Another attempt */
if (option_debug > 3)
ast_verbose("REGISTER attempt %d to %s@%s\n", r->regattempts, r->username, r->hostname);
return send_request(p, &req, 2, p->ocseq);
/*! \brief Transmit text with SIP MESSAGE method */
Mark Spencer
committed
static int transmit_message_with_text(struct sip_pvt *p, const char *text)
reqprep(&req, p, SIP_MESSAGE, 0, 1);
return send_request(p, &req, 1, p->ocseq);
/*! \brief Allocate SIP refer structure */
Kevin P. Fleming
committed
static int sip_refer_allocate(struct sip_pvt *p)
{
p->refer = ast_calloc(1, sizeof(struct sip_refer));
return p->refer ? 1 : 0;
/*! \brief Transmit SIP REFER message */
Mark Spencer
committed
static int transmit_refer(struct sip_pvt *p, const char *dest)
const char *of;
char *c;
char *ttag, *ftag;
char *theirtag = ast_strdupa(p->theirtag);
if (option_debug || sipdebug)
ast_log(LOG_DEBUG, "SIP transfer of %s to %s\n", p->callid, dest);
/* Are we transfering an inbound or outbound call ? */
if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
of = get_header(&p->initreq, "To");
ttag = theirtag;
ftag = p->tag;
} else {
of = get_header(&p->initreq, "From");
ftag = theirtag;
ttag = p->tag;
}
Kevin P. Fleming
committed
ast_copy_string(from, of, sizeof(from));
Kevin P. Fleming
committed
of = get_in_brackets(from);
ast_string_field_set(p, from, of);
if (strncmp(of, "sip:", 4))
ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n");
of += 4;
/* Get just the username part */
if ((c = strchr(dest, '@')))
else if ((c = strchr(of, '@')))
*c++ = '\0';
snprintf(referto, sizeof(referto), "<sip:%s@%s>", dest, c);
snprintf(referto, sizeof(referto), "<sip:%s>", dest);
add_header(&req, "Max-Forwards", DEFAULT_MAX_FORWARDS);
/* save in case we get 407 challenge */
sip_refer_allocate(p);
ast_copy_string(p->refer->refer_to, referto, sizeof(p->refer->refer_to));
ast_copy_string(p->refer->referred_by, p->our_contact, sizeof(p->refer->referred_by));
p->refer->status = REFER_SENT; /* Set refer status */
reqprep(&req, p, SIP_REFER, 0, 1);
add_header(&req, "Refer-To", referto);
add_header(&req, "Allow", ALLOWED_METHODS);
add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
if (!ast_strlen_zero(p->our_contact))
add_header(&req, "Referred-By", p->our_contact);
return send_request(p, &req, 1, p->ocseq);
/* We should propably wait for a NOTIFY here until we ack the transfer */
/* Maybe fork a new thread and wait for a STATUS of REFER_200OK on the refer status before returning to app_transfer */
/*! \todo In theory, we should hang around and wait for a reply, before
returning to the dial plan here. Don't know really how that would
affect the transfer() app or the pbx, but, well, to make this
useful we should have a STATUS code on transfer().
*/
/*! \brief Send SIP INFO dtmf message, see Cisco documentation on cisco.com */
static int transmit_info_with_digit(struct sip_pvt *p, char digit)
{
struct sip_request req;
reqprep(&req, p, SIP_INFO, 0, 1);