Newer
Older
/*--- transmit_response_with_t38_sdp: Used for 200 OK and 183 early media ---*/
static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans)
{
struct sip_request resp;
int seqno;
if (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1) {
ast_log(LOG_WARNING, "Unable to get seqno from '%s'\n", get_header(req, "CSeq"));
return -1;
}
respprep(&resp, p, msg, req);
if (p->udptl) {
ast_udptl_offered_from_local(p->udptl, 0);
add_t38_sdp(&resp, p);
} else {
ast_log(LOG_ERROR, "Can't add SDP to response, since we have no UDPTL session allocated. Call-ID %s\n", p->callid);
}
return send_response(p, &resp, retrans, seqno);
}
/*! \brief copy SIP request (mostly used to save request for responses) */
static void copy_request(struct sip_request *dst, const struct sip_request *src)
{
long offset;
int x;
offset = ((void *)dst) - ((void *)src);
/* First copy stuff */
memcpy(dst, src, sizeof(*dst));
/* Now fix pointer arithmetic */
for (x=0; x < src->headers; x++)
for (x=0; x < src->lines; x++)
/*! \brief Used for 200 OK and 183 early media */
static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable)
Kevin P. Fleming
committed
if (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1) {
ast_log(LOG_WARNING, "Unable to get seqno from '%s'\n", get_header(req, "CSeq"));
return -1;
}
if (p->rtp) {
Olle Johansson
committed
try_suggested_sip_codec(p);
add_sdp(&resp, p);
} else {
ast_log(LOG_ERROR, "Can't add SDP to response, since we have no RTP session allocated. Call-ID %s\n", p->callid);
}
return send_response(p, &resp, reliable, seqno);
/*! \brief Parse first line of incoming SIP request */
static int determine_firstline_parts(struct sip_request *req)
char *e = ast_skip_blanks(req->header[0]); /* there shouldn't be any */
if (!*e)
req->rlPart1 = e; /* method or protocol */
e = ast_skip_nonblanks(e);
if (*e)
*e++ = '\0';
e = ast_skip_blanks(e);
if ( !*e )
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;
return send_request(p, &req, 1, p->ocseq);
/*! \brief Transmit reinvite with T38 SDP
6125
6126
6127
6128
6129
6130
6131
6132
6133
6134
6135
6136
6137
6138
6139
6140
6141
6142
6143
6144
6145
We reinvite so that the T38 processing can take place.
SIP Signalling stays with * in the path.
*/
static int transmit_reinvite_with_t38_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);
add_header(&req, "Allow", ALLOWED_METHODS);
add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
if (sipdebug)
add_header(&req, "X-asterisk-info", "SIP re-invite (T38 switchover)");
ast_udptl_offered_from_local(p->udptl, 1);
add_t38_sdp(&req, p);
/* Use this as the basis */
initialize_initreq(p, &req);
p->lastinvite = p->ocseq;
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];
char *c;
Kevin P. Fleming
committed
ast_copy_string(stripped, get_header(req, "Contact"), sizeof(stripped));
c = get_in_brackets(stripped);
c = strsep(&c, ";"); /* trim ; and beyond */
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)
{
/* Construct Contact: header */
if (ourport != 5060) /* Needs to be 5060, according to the RFC */
Russell Bryant
committed
ast_string_field_build(p, our_contact, "<sip:%s%s%s:%d>", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip), ourport);
Russell Bryant
committed
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 */
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;
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
6188
6189
6190
6191
6192
6193
6194
6195
6196
6197
6198
6199
6200
6201
6202
6203
6204
6205
6206
6207
6208
6209
6210
6211
6212
6213
6214
6215
6216
6217
6218
6219
6220
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;
}
Russell Bryant
committed
fromdomain = S_OR(p->fromdomain, ast_inet_ntoa(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];
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;
}
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 */
Russell Bryant
committed
snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s:%d>;tag=%s", n, l, S_OR(p->fromdomain, ast_inet_ntoa(p->ourip)), ourport, p->tag);
Mark Spencer
committed
else
Russell Bryant
committed
snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s>;tag=%s", n, l, S_OR(p->fromdomain, ast_inet_ntoa(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) { /* Seems like init always is 2 */
/* 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) {
if (p->udptl && p->t38.state == T38_LOCAL_DIRECT) {
ast_udptl_offered_from_local(p->udptl, 1);
if (option_debug)
ast_log(LOG_DEBUG, "T38 is in state %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
add_t38_sdp(&req, p);
} else if (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, *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
6511
6512
6513
6514
6515
6516
6517
6518
6519
6520
6521
6522
6523
6524
6525
6526
6527
6528
6529
6530
6531
6532
6533
6534
6535
6536
6537
6538
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_ONHOLD:
break;
Kevin P. Fleming
committed
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;
}
mfrom = strsep(&c, ";"); /* trim ; and beyond */
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;
}
mto = strsep(&c, ";"); /* trim ; and beyond */
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",
Russell Bryant
committed
S_OR(vmexten, default_vmexten), S_OR(p->fromdomain, ast_inet_ntoa(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)
if (!p->initreq.headers) /* Initialize first request before sending */
Olle Johansson
committed
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);
add_header(&req, "Subscription-state", terminate ? "terminated;reason=noresource" : "active");
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);
}
/*! \brief 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, const char *auth, const 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 */
snprintf(addr, sizeof(addr), "sip:%s", S_OR(p->fromdomain, 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)