diff options
author | Russell Bryant <russell@russellbryant.com> | 2011-06-01 21:31:40 +0000 |
---|---|---|
committer | Russell Bryant <russell@russellbryant.com> | 2011-06-01 21:31:40 +0000 |
commit | 3f4d0e87431073c8fe8c2eaa5b42eec6292ea518 (patch) | |
tree | ad58ec0cb5e2b08c905aaf6075e7f6b4561335e2 /channels | |
parent | eca8a0a6251310d6c46c211bed2514b56454c39e (diff) |
Support routing text messages outside of a call.
Asterisk now has protocol independent support for processing text messages
outside of a call. Messages are routed through the Asterisk dialplan.
SIP MESSAGE and XMPP are currently supported. There are options in sip.conf
and jabber.conf that enable these features.
There is a new application, MessageSend(). There are two new functions,
MESSAGE() and MESSAGE_DATA(). Documentation will be available on
the project wiki, wiki.asterisk.org.
Thanks to Terry Wilson for the assistance with development and to David Vossel
for helping with some additional testing.
Review: https://reviewboard.asterisk.org/r/1042/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@321546 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'channels')
-rw-r--r-- | channels/chan_sip.c | 329 | ||||
-rw-r--r-- | channels/sip/include/sip.h | 5 |
2 files changed, 310 insertions, 24 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c index cbfb8f733..6556b3a07 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -263,6 +263,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/cel.h" #include "asterisk/data.h" #include "asterisk/aoc.h" +#include "asterisk/message.h" #include "sip/include/sip.h" #include "sip/include/globals.h" #include "sip/include/config_parser.h" @@ -1252,7 +1253,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); @@ -1261,7 +1263,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, int seqno); static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno); static void copy_request(struct sip_request *dst, const struct sip_request *src); -static void receive_message(struct sip_pvt *p, struct sip_request *req); +static void 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, const struct ast_event *event, int cache_only); @@ -1532,7 +1534,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, int 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); @@ -1547,6 +1549,7 @@ static void handle_response_notify(struct sip_pvt *p, int resp, const char *rest static void handle_response_refer(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno); static void handle_response_subscribe(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno); static int handle_response_register(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno); +static void handle_response_message(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno); static void handle_response(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno); /*------ SRTP Support -------- */ @@ -4444,7 +4447,7 @@ static int sip_sendtext(struct ast_channel *ast, const char *text) } if (debug) 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; } @@ -13117,16 +13120,49 @@ 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; + + 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) { @@ -13357,6 +13393,10 @@ static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int seqn 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); } @@ -15912,15 +15952,52 @@ static int get_msg_text(char *buf, int len, struct sip_request *req, int addnewl return 0; } +static int get_msg_text2(struct ast_str **buf, struct sip_request *req, int addnewline) +{ + 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%s", line, addnewline ? "\n" : ""); + } + + 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]; + struct ast_str *buf; 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? */ @@ -15929,7 +16006,15 @@ static void receive_message(struct sip_pvt *p, struct sip_request *req) return; } - if (get_msg_text(buf, sizeof(buf), req, FALSE)) { + 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, FALSE)) { ast_log(LOG_WARNING, "Unable to retrieve text from %s\n", p->callid); transmit_response(p, "202 Accepted", req); if (!p->owner) @@ -15939,23 +16024,93 @@ static void receive_message(struct sip_pvt *p, struct sip_request *req) 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; + } + + if (sip_cfg.auth_message_requests) { + int res; + + copy_request(&p->initreq, req); + 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); + } + peer = unref_peer(peer, "from find_peer() in receive_message"); + } + } + + 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_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 */ @@ -20549,6 +20704,8 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc handle_response_register(p, resp, rest, req, seqno); else if (sipmethod == SIP_UPDATE) { handle_response_update(p, resp, rest, req, seqno); + } else if (sipmethod == SIP_MESSAGE) { + handle_response_message(p, resp, rest, req, seqno); } else if (sipmethod == SIP_BYE) { if (p->options) p->options->auth_type = resp; @@ -20894,11 +21051,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 @@ -23378,18 +23535,129 @@ static int handle_request_bye(struct sip_pvt *p, struct sip_request *req) return 1; } +/*! + * \internal + * \brief Handle auth requests to a MESSAGE request + */ +static void handle_response_message(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno) +{ + char *header, *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 on MESSAGE to '%s'\n", get_header(&p->initreq, "From")); + pvt_set_needdestroy(p, "MESSAGE authentication failed"); + return; + } + + 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"); + pvt_set_needdestroy(p, "MESSAGE authentication failed"); + return; + } + + if (p->do_history) { + append_history(p, "MessageAuth", "Try: %d", p->authtries); + } + + transmit_message_with_text(p, p->msg_body, 0, 1); +} + /*! \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 *peer; + struct sip_peer *peer_ptr; + + if (!(pvt = sip_alloc(NULL, NULL, 0, SIP_MESSAGE, NULL))) { + return -1; + } + + peer = ast_strdupa(to); + if (strchr(peer, '@')) { + strsep(&peer, "@"); + } else { + strsep(&peer, ":"); + } + if (ast_strlen_zero(peer)) { + 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, "unref_peer, from sip_msg_send, 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); + 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); + + if (create_addr(pvt, peer, NULL, TRUE, NULL)) { + sip_pvt_unlock(pvt); + dialog_unlink_all(pvt, TRUE, TRUE); + dialog_unref(pvt, "create_addr failed sending a MESSAGE"); + return -1; + } + 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); @@ -24589,7 +24857,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); @@ -27368,6 +27636,8 @@ 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.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 */ @@ -27616,6 +27886,10 @@ 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, "mohinterpret")) { ast_copy_string(default_mohinterpret, v->value, sizeof(default_mohinterpret)); } else if (!strcasecmp(v->name, "mohsuggest")) { @@ -29586,6 +29860,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"); @@ -29694,6 +29973,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); diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h index 4b69010b3..0eb8be350 100644 --- a/channels/sip/include/sip.h +++ b/channels/sip/include/sip.h @@ -211,6 +211,8 @@ #define DEFAULT_CALLEVENTS FALSE /*!< Extra manager SIP call events */ #define DEFAULT_ALWAYSAUTHREJECT TRUE /*!< Don't reject authentication requests always */ #define DEFAULT_AUTH_OPTIONS FALSE +#define DEFAULT_AUTH_MESSAGE TRUE +#define DEFAULT_ACCEPT_OUTOFCALL_MESSAGE TRUE #define DEFAULT_REGEXTENONQUALIFY FALSE #define DEFAULT_LEGACY_USEROPTION_PARSING FALSE #define DEFAULT_T1MIN 100 /*!< 100 MS for minimal roundtrip time */ @@ -680,6 +682,8 @@ struct sip_settings { int allowguest; /*!< allow unauthenticated peers to connect? */ int alwaysauthreject; /*!< Send 401 Unauthorized for all failing requests */ int auth_options_requests; /*!< Authenticate OPTIONS requests */ + int auth_message_requests; /*!< Authenticate MESSAGE requests */ + int accept_outofcall_message; /*!< Accept MESSAGE outside of a call */ int compactheaders; /*!< send compact sip headers */ int allow_external_domains; /*!< Accept calls to external SIP domains? */ int callevents; /*!< Whether we send manager events or not */ @@ -966,6 +970,7 @@ struct sip_pvt { AST_STRING_FIELD(parkinglot); /*!< Parkinglot */ AST_STRING_FIELD(engine); /*!< RTP engine to use */ AST_STRING_FIELD(dialstring); /*!< The dialstring used to call this SIP endpoint */ + AST_STRING_FIELD(msg_body); /*!< Text for a MESSAGE body */ ); char via[128]; /*!< Via: header */ int maxforwards; /*!< SIP Loop prevention */ |