Add support for Digium Phones.

git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.8-digiumphones@357459 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Jason Parker
2012-02-28 21:52:48 +00:00
parent f11923e651
commit f9834fd128
41 changed files with 6074 additions and 153 deletions

View File

@@ -264,6 +264,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/cel.h"
#include "asterisk/data.h"
#include "asterisk/aoc.h"
#include "asterisk/custom_control_frame.h"
#include "asterisk/message.h"
#include "sip/include/sip.h"
#include "sip/include/globals.h"
#include "sip/include/config_parser.h"
@@ -1084,6 +1086,13 @@ static void destroy_escs(void)
}
}
struct state_notify_data {
int state;
int presence_state;
const char *presence_subtype;
const char *presence_message;
};
/*!
* \details
* This container holds the dialogs that will be destroyed immediately.
@@ -1259,7 +1268,8 @@ static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version, int old
static int transmit_info_with_aoc(struct sip_pvt *p, struct ast_aoc_decoded *decoded);
static int transmit_info_with_digit(struct sip_pvt *p, const char digit, unsigned int duration);
static int transmit_info_with_vidupdate(struct sip_pvt *p);
static int transmit_message_with_text(struct sip_pvt *p, const char *text);
static int transmit_message_with_text(struct sip_pvt *p, const char *text, int init, int auth);
static int transmit_message_with_msg(struct sip_pvt *p, const struct ast_msg *msg);
static int transmit_refer(struct sip_pvt *p, const char *dest);
static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs, const char *vmexten);
static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *message, int terminate);
@@ -1268,7 +1278,7 @@ static int transmit_register(struct sip_registry *r, int sipmethod, const char *
static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, uint32_t seqno);
static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, uint32_t 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 receive_message(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e);
static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req, char **name, char **number, int set_call_forward);
static int sip_send_mwi_to_peer(struct sip_peer *peer, int cache_only);
@@ -1356,7 +1366,8 @@ static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target
static int do_magic_pickup(struct ast_channel *channel, const char *extension, const char *context);
/*--- Device monitoring and Device/extension state/event handling */
static int cb_extensionstate(char *context, char* exten, int state, void *data);
static int extensionstate_update(char *context, char *exten, struct state_notify_data *data, struct sip_pvt *p);
static int cb_extensionstate(char *context, char *exten, struct ast_state_cb_info *info, void *data);
static int sip_devicestate(void *data);
static int sip_poke_noanswer(const void *data);
static int sip_poke_peer(struct sip_peer *peer, int force);
@@ -1489,7 +1500,7 @@ 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 enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_request *oreq, int *cc_recall_core_id);
static int get_msg_text(char *buf, int len, struct sip_request *req);
static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout);
static int transmit_state_notify(struct sip_pvt *p, struct state_notify_data *data, 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 int get_domain(const char *str, char *domain, int len);
@@ -1541,7 +1552,7 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int
static int handle_request_bye(struct sip_pvt *p, struct sip_request *req);
static int handle_request_register(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *sin, const char *e);
static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req);
static int handle_request_message(struct sip_pvt *p, struct sip_request *req);
static int handle_request_message(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e);
static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, uint32_t seqno, const char *e);
static void handle_request_info(struct sip_pvt *p, struct sip_request *req);
static int handle_request_options(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e);
@@ -3836,7 +3847,10 @@ static int __sip_autodestruct(const void *data)
/* If this is a subscription, tell the phone that we got a timeout */
if (p->subscribed && p->subscribed != MWI_NOTIFICATION && p->subscribed != CALL_COMPLETION) {
transmit_state_notify(p, AST_EXTENSION_DEACTIVATED, 1, TRUE); /* Send last notification */
struct state_notify_data data = { 0, };
data.state = AST_EXTENSION_DEACTIVATED;
transmit_state_notify(p, &data, 1, TRUE); /* Send last notification */
p->subscribed = NONE;
append_history(p, "Subscribestatus", "timeout");
ast_debug(3, "Re-scheduled destruction of SIP subscription %s\n", p->callid ? p->callid : "<unknown>");
@@ -4487,7 +4501,7 @@ static int sip_sendtext(struct ast_channel *ast, const char *text)
ast_verbose("Sending text %s on %s\n", text, ast->name);
}
transmit_message_with_text(dialog, text);
transmit_message_with_text(dialog, text, 0, 0);
return 0;
}
@@ -6822,6 +6836,54 @@ static int initialize_udptl(struct sip_pvt *p)
return 0;
}
/*!
* \brief Sends AST_CUSTOM_FRAME of type sip info.
*
* \note pvt is expected to be locked before entering this function.
*/
static int sip_handle_custom_info(struct sip_pvt *pvt, struct ast_custom_payload *pl)
{
struct ast_variable *headers = NULL;
char *content_type = NULL;
char *content = NULL;
char *useragent_filter = NULL;
struct ast_variable *var;
struct sip_request req;
int res = -1;
if (ast_custom_payload_sipinfo_decode(pl, &headers, &content_type, &content, &useragent_filter)) {
goto custom_info_cleanup;
}
if (!(ast_strlen_zero(useragent_filter))) {
int match = (strstr(pvt->useragent, useragent_filter)) ? 1 : 0;
if (!match) {
goto custom_info_cleanup;
}
}
reqprep(&req, pvt, SIP_INFO, 0, 1);
for (var = headers; var; var = var->next) {
add_header(&req, var->name, var->value);
}
if (!ast_strlen_zero(content) && !ast_strlen_zero(content_type)) {
add_header(&req, "Content-Type", content_type);
add_content(&req, content);
}
res = send_request(pvt, &req, XMIT_RELIABLE, pvt->ocseq);
custom_info_cleanup:
ast_free(content);
ast_free(content_type);
ast_free(useragent_filter);
ast_variables_destroy(headers);
return res;
}
/*! \brief Play indication to user
* With SIP a lot of indications is sent as messages, letting the device play
the indication - busy signal, congestion etc
@@ -6951,6 +7013,11 @@ static int sip_indicate(struct ast_channel *ast, int condition, const void *data
case AST_CONTROL_REDIRECTING:
update_redirecting(p, data, datalen);
break;
case AST_CONTROL_CUSTOM:
if (datalen && ast_custom_payload_type((struct ast_custom_payload *) data) == AST_CUSTOM_SIP_INFO) {
sip_handle_custom_info(p, (struct ast_custom_payload *) data);
}
break;
case AST_CONTROL_AOC:
{
struct ast_aoc_decoded *decoded = ast_aoc_decode((struct ast_aoc_encoded *) data, datalen, ast);
@@ -12505,8 +12572,13 @@ static int find_calling_channel(void *obj, void *arg, void *data, int flags)
return res ? CMP_MATCH | CMP_STOP : 0;
}
static int allow_notify_user_presence(struct sip_pvt *p)
{
return (strstr(p->useragent, "Digium")) ? 1 : 0;
}
/*! \brief Builds XML portion of NOTIFY messages for presence or dialog updates */
static void state_notify_build_xml(int state, int full, const char *exten, const char *context, struct ast_str **tmp, struct sip_pvt *p, int subscribed, const char *mfrom, const char *mto)
static void state_notify_build_xml(struct state_notify_data *data, int full, const char *exten, const char *context, struct ast_str **tmp, struct sip_pvt *p, int subscribed, const char *mfrom, const char *mto)
{
enum state { NOTIFY_OPEN, NOTIFY_INUSE, NOTIFY_CLOSED } local_state = NOTIFY_OPEN;
const char *statestring = "terminated";
@@ -12514,7 +12586,7 @@ static void state_notify_build_xml(int state, int full, const char *exten, const
const char *pidfnote= "Ready";
char hint[AST_MAX_EXTENSION];
switch (state) {
switch (data->state) {
case (AST_EXTENSION_RINGING | AST_EXTENSION_INUSE):
statestring = (sip_cfg.notifyringing) ? "early" : "confirmed";
local_state = NOTIFY_INUSE;
@@ -12559,9 +12631,16 @@ static void state_notify_build_xml(int state, int full, const char *exten, const
/* Check which device/devices we are watching and if they are registered */
if (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten)) {
char *hint2 = hint, *individual_hint = NULL;
char *hint2;
char *individual_hint = NULL;
int hint_count = 0, unavailable_count = 0;
/* strip off any possible PRESENCE providers from hint */
if ((hint2 = strrchr(hint, ','))) {
*hint2 = '\0';
}
hint2 = hint;
while ((individual_hint = strsep(&hint2, "&"))) {
hint_count++;
@@ -12609,12 +12688,23 @@ static void state_notify_build_xml(int state, int full, const char *exten, const
ast_str_append(tmp, 0, "<status><basic>open</basic></status>\n");
else
ast_str_append(tmp, 0, "<status><basic>%s</basic></status>\n", (local_state != NOTIFY_CLOSED) ? "open" : "closed");
if (allow_notify_user_presence(p) && (data->presence_state > 0)) {
ast_str_append(tmp, 0, "</tuple>\n");
ast_str_append(tmp, 0, "<tuple id=\"digium-presence\">\n");
ast_str_append(tmp, 0, "<status>\n");
ast_str_append(tmp, 0, "<digium_presence type=\"%s\" subtype=\"%s\">%s</digium_presence>\n",
ast_presence_state2str(data->presence_state),
S_OR(data->presence_subtype, ""),
S_OR(data->presence_message, ""));
ast_str_append(tmp, 0, "</status>\n");
}
ast_str_append(tmp, 0, "</tuple>\n</presence>\n");
break;
case DIALOG_INFO_XML: /* SNOM subscribes in this format */
ast_str_append(tmp, 0, "<?xml version=\"1.0\"?>\n");
ast_str_append(tmp, 0, "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"%u\" state=\"%s\" entity=\"%s\">\n", p->dialogver, full ? "full" : "partial", mto);
if ((state & AST_EXTENSION_RINGING) && sip_cfg.notifyringing) {
if ((data->state & AST_EXTENSION_RINGING) && sip_cfg.notifyringing) {
const char *local_display = exten;
char *local_target = ast_strdupa(mto);
@@ -12669,7 +12759,7 @@ static void state_notify_build_xml(int state, int full, const char *exten, const
ast_str_append(tmp, 0, "<dialog id=\"%s\">\n", exten);
}
ast_str_append(tmp, 0, "<state>%s</state>\n", statestring);
if (state == AST_EXTENSION_ONHOLD) {
if (data->state == AST_EXTENSION_ONHOLD) {
ast_str_append(tmp, 0, "<local>\n<target uri=\"%s\">\n"
"<param pname=\"+sip.rendering\" pvalue=\"no\"/>\n"
"</target>\n</local>\n", mto);
@@ -12713,7 +12803,7 @@ static int transmit_cc_notify(struct ast_cc_agent *agent, struct sip_pvt *subscr
}
/*! \brief Used in the SUBSCRIBE notification subsystem (RFC3265) */
static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout)
static int transmit_state_notify(struct sip_pvt *p, struct state_notify_data *data, int full, int timeout)
{
struct ast_str *tmp = ast_str_alloca(4000);
char from[256], to[256];
@@ -12745,7 +12835,7 @@ static int transmit_state_notify(struct sip_pvt *p, int state, int full, int tim
reqprep(&req, p, SIP_NOTIFY, 0, 1);
switch(state) {
switch(data->state) {
case AST_EXTENSION_DEACTIVATED:
if (timeout)
add_header(&req, "Subscription-State", "terminated;reason=timeout");
@@ -12768,19 +12858,19 @@ static int transmit_state_notify(struct sip_pvt *p, int state, int full, int tim
case XPIDF_XML:
case CPIM_PIDF_XML:
add_header(&req, "Event", subscriptiontype->event);
state_notify_build_xml(state, full, p->exten, p->context, &tmp, p, p->subscribed, mfrom, mto);
state_notify_build_xml(data, full, p->exten, p->context, &tmp, p, p->subscribed, mfrom, mto);
add_header(&req, "Content-Type", subscriptiontype->mediatype);
p->dialogver++;
break;
case PIDF_XML: /* Eyebeam supports this format */
add_header(&req, "Event", subscriptiontype->event);
state_notify_build_xml(state, full, p->exten, p->context, &tmp, p, p->subscribed, mfrom, mto);
state_notify_build_xml(data, full, p->exten, p->context, &tmp, p, p->subscribed, mfrom, mto);
add_header(&req, "Content-Type", subscriptiontype->mediatype);
p->dialogver++;
break;
case DIALOG_INFO_XML: /* SNOM subscribes in this format */
add_header(&req, "Event", subscriptiontype->event);
state_notify_build_xml(state, full, p->exten, p->context, &tmp, p, p->subscribed, mfrom, mto);
state_notify_build_xml(data, full, p->exten, p->context, &tmp, p, p->subscribed, mfrom, mto);
add_header(&req, "Content-Type", subscriptiontype->mediatype);
p->dialogver++;
break;
@@ -13409,16 +13499,50 @@ static int transmit_register(struct sip_registry *r, int sipmethod, const char *
return res;
}
/*! \brief Transmit text with SIP MESSAGE method */
static int transmit_message_with_text(struct sip_pvt *p, const char *text)
/*! \brief Transmit text with SIP MESSAGE method based on an ast_msg */
static int transmit_message_with_msg(struct sip_pvt *p, const struct ast_msg *msg)
{
struct sip_request req;
reqprep(&req, p, SIP_MESSAGE, 0, 1);
add_text(&req, text);
struct ast_msg_var_iterator *i;
const char *var, *val;
build_via(p);
initreqprep(&req, p, SIP_MESSAGE, NULL);
ast_string_field_set(p, msg_body, ast_msg_get_body(msg));
initialize_initreq(p, &req);
i = ast_msg_var_iterator_init(msg);
while (ast_msg_var_iterator_next(msg, i, &var, &val)) {
add_header(&req, var, val);
ast_msg_var_unref_current(i);
}
ast_msg_var_iterator_destroy(i);
add_text(&req, ast_msg_get_body(msg));
return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
}
/*! \brief Transmit text with SIP MESSAGE method */
static int transmit_message_with_text(struct sip_pvt *p, const char *text, int init, int auth)
{
struct sip_request req;
if (init) {
initreqprep(&req, p, SIP_MESSAGE, NULL);
ast_string_field_set(p, msg_body, text);
initialize_initreq(p, &req);
} else {
reqprep(&req, p, SIP_MESSAGE, 0, 1);
}
if (auth) {
return transmit_request_with_auth(p, SIP_MESSAGE, p->ocseq, XMIT_RELIABLE, 0);
} else {
add_text(&req, text);
return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
}
}
/*! \brief Allocate SIP refer structure */
static int sip_refer_allocate(struct sip_pvt *p)
{
@@ -13649,6 +13773,10 @@ static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, uint32_t
add_header(&resp, "X-Asterisk-HangupCauseCode", buf);
}
if (sipmethod == SIP_MESSAGE) {
add_text(&resp, p->msg_body);
}
return send_request(p, &resp, reliable, seqno ? seqno : p->ocseq);
}
@@ -14536,37 +14664,35 @@ static void cb_extensionstate_destroy(int id, void *data)
dialog_unref(p, "the extensionstate containing this dialog ptr was destroyed");
}
/*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem
\note If you add an "hint" priority to the extension in the dial plan,
you will get notifications on device state changes */
static int cb_extensionstate(char *context, char* exten, int state, void *data)
static int extensionstate_update(char *context, char *exten, struct state_notify_data *data, struct sip_pvt *p)
{
struct sip_pvt *p = data;
sip_pvt_lock(p);
switch(state) {
switch(data->state) {
case AST_EXTENSION_DEACTIVATED: /* Retry after a while */
case AST_EXTENSION_REMOVED: /* Extension is gone */
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); /* Delete subscription in 32 secs */
ast_verb(2, "Extension state: Watcher for hint %s %s. Notify User %s\n", exten, state == AST_EXTENSION_DEACTIVATED ? "deactivated" : "removed", p->username);
ast_verb(2, "Extension state: Watcher for hint %s %s. Notify User %s\n", exten, data->state == AST_EXTENSION_DEACTIVATED ? "deactivated" : "removed", p->username);
p->subscribed = NONE;
append_history(p, "Subscribestatus", "%s", state == AST_EXTENSION_REMOVED ? "HintRemoved" : "Deactivated");
append_history(p, "Subscribestatus", "%s", data->state == AST_EXTENSION_REMOVED ? "HintRemoved" : "Deactivated");
break;
default: /* Tell user */
p->laststate = state;
p->laststate = data->state;
p->last_presence_state = data->presence_state;
ast_string_field_set(p, last_presence_subtype, S_OR(data->presence_subtype, ""));
ast_string_field_set(p, last_presence_message, S_OR(data->presence_message, ""));
break;
}
if (p->subscribed != NONE) { /* Only send state NOTIFY if we know the format */
if (!p->pendinginvite) {
transmit_state_notify(p, state, 1, FALSE);
transmit_state_notify(p, data, 1, FALSE);
} else {
/* We already have a NOTIFY sent that is not answered. Queue the state up.
if many state changes happen meanwhile, we will only send a notification of the last one */
ast_set_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE);
}
}
ast_verb(2, "Extension Changed %s[%s] new state %s for Notify User %s %s\n", exten, context, ast_extension_state2str(state), p->username,
ast_verb(2, "Extension Changed %s[%s] new state %s for Notify User %s %s\n", exten, context, ast_extension_state2str(data->state), p->username,
ast_test_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE) ? "(queued)" : "");
sip_pvt_unlock(p);
@@ -14574,6 +14700,27 @@ static int cb_extensionstate(char *context, char* exten, int state, void *data)
return 0;
}
/*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem
\note If you add an "hint" priority to the extension in the dial plan,
you will get notifications on device state changes */
static int cb_extensionstate(char *context, char *exten, struct ast_state_cb_info *info, void *data)
{
struct sip_pvt *p = data;
struct state_notify_data notify_data = {
.state = info->exten_state,
.presence_state = info->presence_state,
.presence_subtype = info->presence_subtype,
.presence_message = info->presence_message,
};
if ((info->reason == AST_HINT_UPDATE_PRESENCE) && !(allow_notify_user_presence(p))) {
/* ignore a presence triggered update if we know the useragent doesn't care */
return 0;
}
return extensionstate_update(context, exten, &notify_data, p);
}
/*! \brief Send a fake 401 Unauthorized response when the administrator
wants to hide the names of local devices from fishers
*/
@@ -16033,6 +16180,9 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
if (!ast_strlen_zero(peer->context)) {
ast_string_field_set(p, context, peer->context);
}
if (!ast_strlen_zero(peer->messagecontext)) {
ast_string_field_set(p, messagecontext, peer->messagecontext);
}
if (!ast_strlen_zero(peer->mwi_from)) {
ast_string_field_set(p, mwi_from, peer->mwi_from);
}
@@ -16247,16 +16397,54 @@ static int get_msg_text(char *buf, int len, struct sip_request *req)
return 0;
}
static int get_msg_text2(struct ast_str **buf, struct sip_request *req)
{
int i, res = 0;
ast_str_reset(*buf);
for (i = 0; res >= 0 && i < req->lines; i++) {
const char *line = REQ_OFFSET_TO_STR(req, line[i]);
res = ast_str_append(buf, 0, "%s\n", line);
}
return res < 0 ? -1 : 0;
}
static void set_message_vars_from_req(struct ast_msg *msg, struct sip_request *req)
{
size_t x;
char name_buf[1024] = "";
char val_buf[1024] = "";
char *c;
for (x = 0; x < req->headers; x++) {
const char *header = REQ_OFFSET_TO_STR(req, header[x]);
if ((c = strchr(header, ':'))) {
ast_copy_string(name_buf, header, MIN((c - header + 1), sizeof(name_buf)));
ast_copy_string(val_buf, ast_skip_blanks(c + 1), sizeof(val_buf));
ast_trim_blanks(name_buf);
ast_msg_set_var(msg, name_buf, val_buf);
}
}
}
AST_THREADSTORAGE(sip_msg_buf);
/*! \brief Receive SIP MESSAGE method messages
\note We only handle messages within current calls currently
Reference: RFC 3428 */
static void receive_message(struct sip_pvt *p, struct sip_request *req)
static void receive_message(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e)
{
char buf[1400];
char *bufp;
struct ast_str *buf;
char *cbuf;
size_t len;
struct ast_frame f;
const char *content_type = get_header(req, "Content-Type");
struct ast_msg *msg;
int res;
char *from, *to;
if (strncmp(content_type, "text/plain", strlen("text/plain"))) { /* No text/plain attachment */
transmit_response(p, "415 Unsupported Media Type", req); /* Good enough, or? */
@@ -16265,7 +16453,15 @@ static void receive_message(struct sip_pvt *p, struct sip_request *req)
return;
}
if (get_msg_text(buf, sizeof(buf), req)) {
if (!(buf = ast_str_thread_get(&sip_msg_buf, 128))) {
transmit_response(p, "500 Internal Server Error", req);
if (!p->owner) {
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
}
return;
}
if (get_msg_text2(&buf, req)) {
ast_log(LOG_WARNING, "Unable to retrieve text from %s\n", p->callid);
transmit_response(p, "500 Internal Server Error", req);
if (!p->owner) {
@@ -16274,32 +16470,129 @@ static void receive_message(struct sip_pvt *p, struct sip_request *req)
return;
}
/* Strip trailing line feeds from message body. (get_msg_text may add
/* Strip trailing line feeds from message body. (get_msg_text2 may add
* a trailing linefeed and we don't need any at the end) */
bufp = buf + strlen(buf);
while (--bufp >= buf && *bufp == '\n') {
*bufp = '\0';
cbuf = ast_str_buffer(buf);
len = ast_str_strlen(buf);
while (len > 0) {
if (cbuf[--len] != '\n') {
++len;
break;
}
}
ast_str_truncate(buf, len);
if (p->owner) {
if (sip_debug_test_pvt(p))
ast_verbose("SIP Text message received: '%s'\n", buf);
ast_verbose("SIP Text message received: '%s'\n", ast_str_buffer(buf));
memset(&f, 0, sizeof(f));
f.frametype = AST_FRAME_TEXT;
f.subclass.integer = 0;
f.offset = 0;
f.data.ptr = buf;
f.datalen = strlen(buf) + 1;
f.data.ptr = ast_str_buffer(buf);
f.datalen = ast_str_strlen(buf) + 1;
ast_queue_frame(p->owner, &f);
transmit_response(p, "202 Accepted", req); /* We respond 202 accepted, since we relay the message */
return;
}
/* Message outside of a call, we do not support that */
ast_log(LOG_WARNING, "Received message to %s from %s, dropped it...\n Content-Type:%s\n Message: %s\n", get_header(req, "To"), get_header(req, "From"), content_type, buf);
transmit_response(p, "405 Method Not Allowed", req);
if (!sip_cfg.accept_outofcall_message) {
/* Message outside of a call, we do not support that */
ast_debug(1, "MESSAGE outside of a call administratively disabled.\n");
transmit_response(p, "405 Method Not Allowed", req);
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
return;
}
copy_request(&p->initreq, req);
if (sip_cfg.auth_message_requests) {
int res;
set_pvt_allowed_methods(p, req);
res = check_user(p, req, SIP_MESSAGE, e, XMIT_UNRELIABLE, addr);
if (res == AUTH_CHALLENGE_SENT) {
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
return;
}
if (res < 0) { /* Something failed in authentication */
if (res == AUTH_FAKE_AUTH) {
ast_log(LOG_NOTICE, "Sending fake auth rejection for device %s\n", get_header(req, "From"));
transmit_fake_auth_response(p, SIP_OPTIONS, req, XMIT_UNRELIABLE);
} else {
ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", get_header(req, "From"));
transmit_response(p, "403 Forbidden", req);
}
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
return;
}
/* Auth was successful. Proceed. */
} else {
struct sip_peer *peer;
/*
* MESSAGE outside of a call, not authenticating it.
* Check to see if we match a peer anyway so that we can direct
* it to the right context.
*/
peer = find_peer(NULL, &p->recv, TRUE, FINDPEERS, 0, p->socket.type);
if (peer) {
/* Only if no auth is required. */
if (ast_strlen_zero(peer->secret) && ast_strlen_zero(peer->md5secret)) {
ast_string_field_set(p, context, peer->context);
}
if (!ast_strlen_zero(peer->messagecontext)) {
ast_string_field_set(p, messagecontext, peer->messagecontext);
}
ast_string_field_set(p, peername, peer->name);
peer = unref_peer(peer, "from find_peer() in receive_message");
}
}
/* Override the context with the message context _BEFORE_
* getting the destination. This way we can guarantee the correct
* extension is used in the message context when it is present. */
if (!ast_strlen_zero(p->messagecontext)) {
ast_string_field_set(p, context, p->messagecontext);
} else if (!ast_strlen_zero(sip_cfg.messagecontext)) {
ast_string_field_set(p, context, sip_cfg.messagecontext);
}
get_destination(p, NULL, NULL);
if (!(msg = ast_msg_alloc())) {
transmit_response(p, "500 Internal Server Error", req);
if (!p->owner) {
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
}
return;
}
to = ast_strdupa(REQ_OFFSET_TO_STR(req, rlPart2));
from = ast_strdupa(get_header(req, "From"));
res = ast_msg_set_to(msg, "%s", to);
res |= ast_msg_set_from(msg, "%s", get_in_brackets(from));
res |= ast_msg_set_body(msg, "%s", ast_str_buffer(buf));
res |= ast_msg_set_context(msg, "%s", p->context);
res |= ast_msg_set_var(msg, "SIP_RECVADDR", ast_sockaddr_stringify(&p->recv));
if (!ast_strlen_zero(p->peername)) {
res |= ast_msg_set_var(msg, "SIP_PEERNAME", p->peername);
}
res |= ast_msg_set_exten(msg, "%s", p->exten);
if (res) {
ast_msg_destroy(msg);
} else {
set_message_vars_from_req(msg, req);
ast_msg_queue(msg);
}
transmit_response(p, "202 Accepted", req);
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
return;
}
/*! \brief CLI Command to show calls within limits set by call_limit */
@@ -20394,9 +20687,15 @@ static void handle_response_notify(struct sip_pvt *p, int resp, const char *rest
pvt_set_needdestroy(p, "received 200 response");
}
if (ast_test_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE)) {
struct state_notify_data data = {
.state = p->laststate,
.presence_state = p->last_presence_state,
.presence_subtype = p->last_presence_subtype,
.presence_message = p->last_presence_message,
};
/* Ready to send the next state we have on queue */
ast_clear_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE);
cb_extensionstate((char *)p->context, (char *)p->exten, p->laststate, (void *) p);
extensionstate_update((char *)p->context, (char *)p->exten, &data, (void *) p);
}
}
break;
@@ -20810,7 +21109,6 @@ static void handle_response_peerpoke(struct sip_pvt *p, int resp, struct sip_req
}
/*!
* \internal
* \brief Handle responses to INFO messages
*
* \note The INFO method MUST NOT change the state of calls or
@@ -20846,6 +21144,43 @@ static void handle_response_info(struct sip_pvt *p, int resp, const char *rest,
/*!
* \internal
* \brief Handle auth requests to a MESSAGE request
* \return TRUE if authentication failed.
*/
static int do_message_auth(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno)
{
char *header;
char *respheader;
char digest[1024];
if (p->options) {
p->options->auth_type = (resp == 401 ? WWW_AUTH : PROXY_AUTH);
}
if (p->authtries == MAX_AUTHTRIES) {
ast_log(LOG_NOTICE, "Failed to authenticate MESSAGE with host '%s'\n",
ast_sockaddr_stringify(&p->sa));
return -1;
}
++p->authtries;
auth_headers((resp == 401 ? WWW_AUTH : PROXY_AUTH), &header, &respheader);
memset(digest, 0, sizeof(digest));
if (reply_digest(p, req, header, SIP_MESSAGE, digest, sizeof(digest))) {
/* There's nothing to use for authentication */
ast_debug(1, "Nothing to use for MESSAGE authentication\n");
return -1;
}
if (p->do_history) {
append_history(p, "MessageAuth", "Try: %d", p->authtries);
}
transmit_message_with_text(p, p->msg_body, 0, 1);
return 0;
}
/*!
* \brief Handle responses to MESSAGE messages
*
* \note The MESSAGE method should not change the state of calls
@@ -20855,14 +21190,14 @@ static void handle_response_info(struct sip_pvt *p, int resp, const char *rest,
static void handle_response_message(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno)
{
int sipmethod = SIP_MESSAGE;
/* Out-of-dialog MESSAGE currently not supported. */
//int in_dialog = ast_test_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
int in_dialog = ast_test_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
switch (resp) {
case 401: /* Not www-authorized on SIP method */
case 407: /* Proxy auth required */
ast_log(LOG_WARNING, "Host '%s' requests authentication (%d) for '%s'\n",
ast_sockaddr_stringify(&p->sa), resp, sip_methods[sipmethod].text);
if (do_message_auth(p, resp, rest, req, seqno) && !in_dialog) {
pvt_set_needdestroy(p, "MESSAGE authentication failed");
}
break;
case 405: /* Method not allowed */
case 501: /* Not Implemented */
@@ -20872,15 +21207,25 @@ static void handle_response_message(struct sip_pvt *p, int resp, const char *res
}
ast_log(LOG_WARNING, "Host '%s' does not implement '%s'\n",
ast_sockaddr_stringify(&p->sa), sip_methods[sipmethod].text);
if (!in_dialog) {
pvt_set_needdestroy(p, "MESSAGE not implemented or allowed");
}
break;
default:
if (100 <= resp && resp < 200) {
/* Must allow provisional responses for out-of-dialog requests. */
} else if (200 <= resp && resp < 300) {
p->authtries = 0; /* Reset authentication counter */
if (!in_dialog) {
pvt_set_needdestroy(p, "MESSAGE delivery accepted");
}
} else if (300 <= resp && resp < 700) {
ast_verb(3, "Got SIP %s response %d \"%s\" back from host '%s'\n",
sip_methods[sipmethod].text, resp, rest, ast_sockaddr_stringify(&p->sa));
if (!in_dialog) {
pvt_set_needdestroy(p, (300 <= resp && resp < 600)
? "MESSAGE delivery failed" : "MESSAGE delivery refused");
}
}
break;
}
@@ -21357,11 +21702,11 @@ static void *sip_park_thread(void *stuff)
#ifdef WHEN_WE_KNOW_THAT_THE_CLIENT_SUPPORTS_MESSAGE
if (res) {
transmit_message_with_text(transferer->tech_pvt, "Unable to park call.\n");
transmit_message_with_text(transferer->tech_pvt, "Unable to park call.\n", 0, 0);
} else {
/* Then tell the transferer what happened */
sprintf(buf, "Call parked on extension '%d'", ext);
transmit_message_with_text(transferer->tech_pvt, buf);
transmit_message_with_text(transferer->tech_pvt, buf, 0, 0);
}
#endif
@@ -23899,17 +24244,97 @@ static int handle_request_bye(struct sip_pvt *p, struct sip_request *req)
}
/*! \brief Handle incoming MESSAGE request */
static int handle_request_message(struct sip_pvt *p, struct sip_request *req)
static int handle_request_message(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e)
{
if (!req->ignore) {
if (req->debug)
ast_verbose("Receiving message!\n");
receive_message(p, req);
receive_message(p, req, addr, e);
} else
transmit_response(p, "202 Accepted", req);
return 1;
}
static int sip_msg_send(const struct ast_msg *msg, const char *to, const char *from);
static const struct ast_msg_tech sip_msg_tech = {
.name = "sip",
.msg_send = sip_msg_send,
};
static int sip_msg_send(const struct ast_msg *msg, const char *to, const char *from)
{
struct sip_pvt *pvt;
int res;
char *to_uri, *to_host, *to_user;
struct sip_peer *peer_ptr;
if (!(pvt = sip_alloc(NULL, NULL, 0, SIP_MESSAGE, NULL))) {
return -1;
}
to_uri = ast_strdupa(to);
parse_uri(to_uri, "sip:,sips:", &to_user, NULL, &to_host, NULL);
if (ast_strlen_zero(to_host)) {
ast_log(LOG_WARNING, "MESSAGE(to) is invalid for SIP - '%s'\n", to);
return -1;
}
if (!ast_strlen_zero(from)) {
if ((peer_ptr = find_peer(from, NULL, 0, 1, 0, 0))) {
ast_string_field_set(pvt, fromname, S_OR(peer_ptr->cid_name, peer_ptr->name));
ast_string_field_set(pvt, fromuser, S_OR(peer_ptr->cid_num, peer_ptr->name));
unref_peer(peer_ptr, "sip_unref_peer, from sip_msg_send, sip_find_peer");
} else if (strchr(from, '<')) { /* from is callerid-style */
char *sender;
char *name = NULL, *location = NULL, *user = NULL, *domain = NULL;
sender = ast_strdupa(from);
ast_callerid_parse(sender, &name, &location);
ast_string_field_set(pvt, fromname, name);
if (strchr(location, ':')) { /* Must be a URI */
parse_uri(location, "sip:,sips:", &user, NULL, &domain, NULL);
SIP_PEDANTIC_DECODE(user);
SIP_PEDANTIC_DECODE(domain);
extract_host_from_hostport(&domain);
ast_string_field_set(pvt, fromuser, user);
ast_string_field_set(pvt, fromdomain, domain);
} else { /* Treat it as an exten/user */
ast_string_field_set(pvt, fromuser, location);
}
} else { /* assume we just have the name, use defaults for the rest */
ast_string_field_set(pvt, fromname, from);
}
}
sip_pvt_lock(pvt);
/* Look up the host to contact */
if (create_addr(pvt, to_host, NULL, TRUE, NULL)) {
sip_pvt_unlock(pvt);
dialog_unlink_all(pvt);
dialog_unref(pvt, "create_addr failed sending a MESSAGE");
return -1;
}
if (!ast_strlen_zero(to_user)) {
ast_string_field_set(pvt, username, to_user);
}
ast_sip_ouraddrfor(&pvt->sa, &pvt->ourip, pvt);
ast_set_flag(&pvt->flags[0], SIP_OUTGOING);
/* XXX Does pvt->expiry need to be set? */
res = transmit_message_with_msg(pvt, msg);
sip_pvt_unlock(pvt);
sip_scheddestroy(pvt, DEFAULT_TRANS_TIMEOUT);
dialog_unref(pvt, "sent a MESSAGE");
return res;
}
static enum sip_publish_type determine_sip_publish_type(struct sip_request *req, const char * const event, const char * const etag, const char * const expires, int *expires_int)
{
int etag_present = !ast_strlen_zero(etag);
@@ -24501,7 +24926,6 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
{
int gotdest = 0;
int res = 0;
int firststate;
struct sip_peer *authpeer = NULL;
const char *eventheader = get_header(req, "Event"); /* Get Event package name */
int resubscribe = (p->subscribed != NONE) && !req->ignore;
@@ -24850,7 +25274,10 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
unref_peer(peer, "release a peer ref now that MWI is sent");
}
} else if (p->subscribed != CALL_COMPLETION) {
if ((firststate = ast_extension_state(NULL, p->context, p->exten)) < 0) {
struct state_notify_data data = { 0, };
char *subtype = NULL;
char *message = NULL;
if ((data.state = ast_extension_state(NULL, p->context, p->exten)) < 0) {
ast_log(LOG_NOTICE, "Got SUBSCRIBE for extension %s@%s from %s, but there is no hint for that extension.\n", p->exten, p->context, ast_sockaddr_stringify(&p->sa));
transmit_response(p, "404 Not found", req);
@@ -24860,14 +25287,21 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
}
return 0;
}
if (allow_notify_user_presence(p)) {
data.presence_state = ast_hint_presence_state(NULL, p->context, p->exten, &subtype, &message);
data.presence_subtype = subtype;
data.presence_message = message;
}
ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
transmit_response(p, "200 OK", req);
transmit_state_notify(p, firststate, 1, FALSE); /* Send first notification */
append_history(p, "Subscribestatus", "%s", ast_extension_state2str(firststate));
transmit_state_notify(p, &data, 1, FALSE); /* Send first notification */
append_history(p, "Subscribestatus", "%s", ast_extension_state2str(data.state));
/* hide the 'complete' exten/context in the refer_to field for later display */
ast_string_field_build(p, subscribeuri, "%s@%s", p->exten, p->context);
/* Deleted the slow iteration of all sip dialogs to find old subscribes from this peer for exten@context */
ast_free(subtype);
ast_free(message);
}
if (!p->expiry) {
pvt_set_needdestroy(p, "forcing expiration");
@@ -25160,7 +25594,7 @@ static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct as
res = handle_request_bye(p, req);
break;
case SIP_MESSAGE:
res = handle_request_message(p, req);
res = handle_request_message(p, req, addr, e);
break;
case SIP_PUBLISH:
res = handle_request_publish(p, req, addr, seqno, e);
@@ -27037,6 +27471,7 @@ static void set_peer_defaults(struct sip_peer *peer)
ast_copy_flags(&peer->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY);
ast_copy_flags(&peer->flags[2], &global_flags[2], SIP_PAGE3_FLAGS_TO_COPY);
ast_string_field_set(peer, context, sip_cfg.default_context);
ast_string_field_set(peer, messagecontext, sip_cfg.messagecontext);
ast_string_field_set(peer, subscribecontext, sip_cfg.default_subscribecontext);
ast_string_field_set(peer, language, default_language);
ast_string_field_set(peer, mohinterpret, default_mohinterpret);
@@ -27333,6 +27768,8 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
} else if (!strcasecmp(v->name, "context")) {
ast_string_field_set(peer, context, v->value);
ast_set_flag(&peer->flags[1], SIP_PAGE2_HAVEPEERCONTEXT);
} else if (!strcasecmp(v->name, "outofcall_message_context")) {
ast_string_field_set(peer, messagecontext, v->value);
} else if (!strcasecmp(v->name, "subscribecontext")) {
ast_string_field_set(peer, subscribecontext, v->value);
} else if (!strcasecmp(v->name, "fromdomain")) {
@@ -28035,6 +28472,9 @@ static int reload_config(enum channelreloadreason reason)
sip_cfg.directrtpsetup = FALSE; /* Experimental feature, disabled by default */
sip_cfg.alwaysauthreject = DEFAULT_ALWAYSAUTHREJECT;
sip_cfg.auth_options_requests = DEFAULT_AUTH_OPTIONS;
sip_cfg.auth_message_requests = DEFAULT_AUTH_MESSAGE;
sip_cfg.messagecontext[0] = '\0';
sip_cfg.accept_outofcall_message = DEFAULT_ACCEPT_OUTOFCALL_MESSAGE;
sip_cfg.allowsubscribe = FALSE;
sip_cfg.disallowed_methods = SIP_UNKNOWN;
sip_cfg.contact_ha = NULL; /* Reset the contact ACL */
@@ -28284,6 +28724,12 @@ static int reload_config(enum channelreloadreason reason)
if (ast_true(v->value)) {
sip_cfg.auth_options_requests = 1;
}
} else if (!strcasecmp(v->name, "auth_message_requests")) {
sip_cfg.auth_message_requests = ast_true(v->value) ? 1 : 0;
} else if (!strcasecmp(v->name, "accept_outofcall_message")) {
sip_cfg.accept_outofcall_message = ast_true(v->value) ? 1 : 0;
} else if (!strcasecmp(v->name, "outofcall_message_context")) {
ast_copy_string(sip_cfg.messagecontext, v->value, sizeof(sip_cfg.messagecontext));
} else if (!strcasecmp(v->name, "mohinterpret")) {
ast_copy_string(default_mohinterpret, v->value, sizeof(default_mohinterpret));
} else if (!strcasecmp(v->name, "mohsuggest")) {
@@ -30270,6 +30716,11 @@ static int load_module(void)
memcpy(&sip_tech_info, &sip_tech, sizeof(sip_tech));
memset((void *) &sip_tech_info.send_digit_begin, 0, sizeof(sip_tech_info.send_digit_begin));
if (ast_msg_tech_register(&sip_msg_tech)) {
/* LOAD_FAILURE stops Asterisk, so cleanup is a moot point. */
return AST_MODULE_LOAD_FAILURE;
}
/* Make sure we can register our sip channel type */
if (ast_channel_register(&sip_tech)) {
ast_log(LOG_ERROR, "Unable to register channel type 'SIP'\n");
@@ -30379,6 +30830,8 @@ static int unload_module(void)
/* First, take us out of the channel type list */
ast_channel_unregister(&sip_tech);
ast_msg_tech_unregister(&sip_msg_tech);
/* Unregister dial plan functions */
ast_custom_function_unregister(&sipchaninfo_function);
ast_custom_function_unregister(&sippeer_function);