From 0d11889082d7ce823a2965f75ac896c27d62ae25 Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Sat, 1 Nov 2014 22:04:34 +0100 Subject: presence/options patch from Juha most of this work was done by Juha Heinanen, and reviewed by me. thanks! - menu: add support for sending SIP OPTIONS requests to any peer, using the 'o' command - presence: add support for PUBLISH, set ;pubint=N to activate - ua: add Allow/Supported header to OPTIONS reply add MESSAGE to allowed methods --- modules/account/account.c | 1 + modules/menu/menu.c | 65 ++++++++++ modules/presence/module.mk | 2 +- modules/presence/notifier.c | 43 +------ modules/presence/presence.c | 61 +++++++++- modules/presence/presence.h | 7 ++ modules/presence/publisher.c | 277 ++++++++++++++++++++++++++++++++++++++++++ modules/presence/subscriber.c | 4 +- 8 files changed, 415 insertions(+), 45 deletions(-) create mode 100644 modules/presence/publisher.c (limited to 'modules') diff --git a/modules/account/account.c b/modules/account/account.c index e92d977..9659bd0 100644 --- a/modules/account/account.c +++ b/modules/account/account.c @@ -50,6 +50,7 @@ static int account_write_template(const char *file) "# ;outbound2=sip:secondary.example.com\n" "# ;ptime={10,20,30,40,...}\n" "# ;regint=3600\n" + "# ;pubint=0 (publishing off)\n" "# ;regq=0.5\n" "# ;rtpkeep={zero,stun,dyna,rtcp}\n" "# ;sipnat={outbound}\n" diff --git a/modules/menu/menu.c b/modules/menu/menu.c index e0e6e22..fd6ef6f 100644 --- a/modules/menu/menu.c +++ b/modules/menu/menu.c @@ -213,6 +213,70 @@ static int dial_handler(struct re_printf *pf, void *arg) } +static void options_resp_handler(int err, const struct sip_msg *msg, void *arg) +{ + (void)arg; + + if (err) { + warning("options reply error: %m\n", err); + return; + } + + if (msg->scode < 200) + return; + + if (msg->scode < 300) { + + mbuf_set_pos(msg->mb, 0); + info("----- OPTIONS of %r -----\n%b", + &(msg->to.auri), mbuf_buf(msg->mb), + mbuf_get_left(msg->mb)); + return; + } + + info("%r: OPTIONS failed: %u %r\n", &(msg->to.auri), + msg->scode, &msg->reason); +} + + +static int options_command(struct re_printf *pf, void *arg) +{ + const struct cmd_arg *carg = arg; + int err = 0; + + (void)pf; + + if (str_isset(carg->prm)) { + + mbuf_rewind(dialbuf); + (void)mbuf_write_str(dialbuf, carg->prm); + + err = ua_options_send(uag_cur(), carg->prm, + options_resp_handler, NULL); + } + else if (dialbuf->end > 0) { + + char *uri; + + dialbuf->pos = 0; + err = mbuf_strdup(dialbuf, &uri, dialbuf->end); + if (err) + return err; + + err = ua_options_send(uag_cur(), uri, + options_resp_handler, NULL); + + mem_deref(uri); + } + + if (err) { + warning("menu: ua_options failed: %m\n", err); + } + + return err; +} + + static int cmd_answer(struct re_printf *pf, void *unused) { (void)pf; @@ -290,6 +354,7 @@ static const struct cmd cmdv[] = { {'l', 0, "List active calls", cmd_print_calls }, {'m', 0, "Module debug", mod_debug }, {'n', 0, "Network debug", net_debug }, + {'o', CMD_PRM, "Options", options_command }, {'r', 0, "Registration info", ua_print_reg_status }, {'s', 0, "System info", print_system_info }, {'t', 0, "Timer debug", tmr_status }, diff --git a/modules/presence/module.mk b/modules/presence/module.mk index 6ec5d17..b9b6a22 100644 --- a/modules/presence/module.mk +++ b/modules/presence/module.mk @@ -5,7 +5,7 @@ # MOD := presence -$(MOD)_SRCS += presence.c subscriber.c notifier.c +$(MOD)_SRCS += presence.c subscriber.c notifier.c publisher.c $(MOD)_LFLAGS += include mk/mod.mk diff --git a/modules/presence/notifier.c b/modules/presence/notifier.c index ced7b75..f9b7c67 100644 --- a/modules/presence/notifier.c +++ b/modules/presence/notifier.c @@ -22,7 +22,6 @@ struct notifier { struct ua *ua; }; -static enum presence_status my_status = PRESENCE_OPEN; static struct list notifierl; static struct sipevent_sock *evsock; @@ -184,52 +183,19 @@ static int notifier_add(struct sipevent_sock *sock, const struct sip_msg *msg, } -static void notifier_update_status(enum presence_status status) +void notifier_update_status(void) { struct le *le; - if (status == my_status) - return; - - info("presence: update my status from '%s' to '%s'\n", - contact_presence_str(my_status), - contact_presence_str(status)); - - my_status = status; - for (le = notifierl.head; le; le = le->next) { struct notifier *not = le->data; - (void)notify(not, status); + (void)notify(not, my_status); } } -static int cmd_online(struct re_printf *pf, void *arg) -{ - (void)pf; - (void)arg; - notifier_update_status(PRESENCE_OPEN); - return 0; -} - - -static int cmd_offline(struct re_printf *pf, void *arg) -{ - (void)pf; - (void)arg; - notifier_update_status(PRESENCE_CLOSED); - return 0; -} - - -static const struct cmd cmdv[] = { - {'[', 0, "Set presence online", cmd_online }, - {']', 0, "Set presence offline", cmd_offline }, -}; - - static bool sub_handler(const struct sip_msg *msg, void *arg) { struct ua *ua; @@ -255,16 +221,13 @@ int notifier_init(void) int err; err = sipevent_listen(&evsock, uag_sip(), 32, 32, sub_handler, NULL); - if (err) - return err; - return cmd_register(cmdv, ARRAY_SIZE(cmdv)); + return err; } void notifier_close(void) { - cmd_unregister(cmdv); list_flush(¬ifierl); evsock = mem_deref(evsock); } diff --git a/modules/presence/presence.c b/modules/presence/presence.c index 7ed4908..92e982e 100644 --- a/modules/presence/presence.c +++ b/modules/presence/presence.c @@ -7,6 +7,56 @@ #include #include "presence.h" +enum presence_status my_status = PRESENCE_OPEN; + + +static int cmd_online(struct re_printf *pf, void *arg) +{ + (void)pf; + (void)arg; + + if (my_status == PRESENCE_OPEN) + return 0; + + info("presence: update my status from '%s' to '%s'\n", + contact_presence_str(my_status), + contact_presence_str(PRESENCE_OPEN)); + + my_status = PRESENCE_OPEN; + + publisher_update_status(); + notifier_update_status(); + + return 0; +} + + +static int cmd_offline(struct re_printf *pf, void *arg) +{ + (void)pf; + (void)arg; + + if (my_status == PRESENCE_CLOSED) + return 0; + + info("presence: update my status from '%s' to '%s'\n", + contact_presence_str(my_status), + contact_presence_str(PRESENCE_CLOSED)); + + my_status = PRESENCE_CLOSED; + + publisher_update_status(); + notifier_update_status(); + + return 0; +} + + +static const struct cmd cmdv[] = { + {'[', 0, "Set presence online", cmd_online }, + {']', 0, "Set presence offline", cmd_offline }, +}; + static int module_init(void) { @@ -16,17 +66,26 @@ static int module_init(void) if (err) return err; + err = publisher_init(); + if (err) + return err; + err = notifier_init(); if (err) return err; - return 0; + return cmd_register(cmdv, ARRAY_SIZE(cmdv)); } static int module_close(void) { + cmd_unregister(cmdv); + + publisher_close(); + notifier_close(); + subscriber_close(); return 0; diff --git a/modules/presence/presence.h b/modules/presence/presence.h index a1c5e31..430afde 100644 --- a/modules/presence/presence.h +++ b/modules/presence/presence.h @@ -4,6 +4,7 @@ * Copyright (C) 2010 Creytiv.com */ +enum presence_status my_status; int subscriber_init(void); void subscriber_close(void); @@ -11,3 +12,9 @@ void subscriber_close(void); int notifier_init(void); void notifier_close(void); +void notifier_update_status(void); + + +int publisher_init(void); +void publisher_close(void); +void publisher_update_status(void); diff --git a/modules/presence/publisher.c b/modules/presence/publisher.c new file mode 100644 index 0000000..195b055 --- /dev/null +++ b/modules/presence/publisher.c @@ -0,0 +1,277 @@ +/** + * @file publisher.c Presence Publisher (RFC 3842) + * + * Copyright (C) 2010 Creytiv.com + * Copyright (C) 2014 Juha Heinanen + */ + +#include +#include +#include +#include "../../src/core.h" +#include "presence.h" + + +struct publisher { + struct le le; + struct tmr tmr; + unsigned failc; + char *etag; + unsigned int expires; + unsigned int refresh; + struct ua *ua; +}; + +static struct list publ = LIST_INIT; + +static void tmr_handler(void *arg); +static int publish(struct publisher *pub); + + +static void response_handler(int err, const struct sip_msg *msg, void *arg) +{ + struct publisher *pub = arg; + const struct sip_hdr *etag_hdr; + + if (err) + return; + + if (msg->scode < 200) { + return; + } + + if (msg->scode < 300) { + + if (pub->expires == 0) + return; + + etag_hdr = sip_msg_xhdr(msg, "SIP-ETag"); + if (etag_hdr) { + mem_deref(pub->etag); + pl_strdup(&(pub->etag), &(etag_hdr->val)); + pub->refresh = 1; + tmr_start(&pub->tmr, pub->expires * 900, + tmr_handler, pub); + } + else { + warning("%s: publisher got 200 OK without etag\n", + ua_aor(pub->ua)); + } + } + else if (msg->scode == 412) { + + mem_deref(pub->etag); + pub->etag = NULL; + pub->refresh = 0; + publish(pub); + + } + else { + warning("%s: publisher got error response %u %r\n", + ua_aor(pub->ua), msg->scode, &msg->reason); + } + + return; +} + + +/* move this to presence.c */ +static const char *presence_status_str(enum presence_status st) +{ + switch (st) { + + case PRESENCE_OPEN: return "open"; + case PRESENCE_CLOSED: return "closed"; + case PRESENCE_UNKNOWN: return "unkown"; + default: return "?"; + } +} + + +static int publish(struct publisher *pub) +{ + int err; + const char *aor = ua_aor(pub->ua); + struct mbuf *mb; + + mb = mbuf_alloc(1024); + if (!mb) + return ENOMEM; + + if (pub->expires && !pub->refresh) + err = mbuf_printf(mb, + "\r\n" + "\r\n" + " \r\n" + " \r\n" + " \r\n" + " %s\r\n" + " \r\n" + " %s\r\n" + " \r\n" + "\r\n" + ,aor, presence_status_str(my_status), aor); + else + err = mbuf_printf(mb, ""); + if (err) + goto out; + + mb->pos = 0; + + /* XXX: can be simplified with 1 function call, by adding a + print-handler that prints "SIP-If-Match: ETAG" */ + if (pub->etag) + err = sip_req_send(pub->ua, "PUBLISH", aor, + pub->expires ? response_handler : NULL, + pub, + "%s" + "Event: presence\r\n" + "Expires: %u\r\n" + "SIP-If-Match: %s\r\n" + "Content-Length: %zu\r\n" + "\r\n" + "%b", + pub->expires + ? "Content-Type: application/pidf+xml\r\n" + : "", + + pub->expires, + pub->etag, + mbuf_get_left(mb), + mbuf_buf(mb), + mbuf_get_left(mb)); + else + err = sip_req_send(pub->ua, "PUBLISH", aor, + pub->expires ? response_handler : NULL, + pub, + "%s" + "Event: presence\r\n" + "Expires: %u\r\n" + "Content-Length: %zu\r\n" + "\r\n" + "%b", + pub->expires + ? "Content-Type: application/pidf+xml\r\n" + : "", + pub->expires, + mbuf_get_left(mb), + mbuf_buf(mb), + mbuf_get_left(mb)); + if (err) { + warning("publisher: send PUBLISH: (%m)\n", err); + } + +out: + mem_deref(mb); + + return err; +} + + +/* move to presence.c */ +static uint32_t wait_fail(unsigned failc) +{ + switch (failc) { + + case 1: return 30; + case 2: return 300; + case 3: return 3600; + default: return 86400; + } +} + + +static void tmr_handler(void *arg) +{ + struct publisher *pub = arg; + + if (publish(pub)) { + tmr_start(&pub->tmr, wait_fail(++pub->failc) * 1000, + tmr_handler, pub); + } +} + + +static void destructor(void *arg) +{ + struct publisher *pub = arg; + + list_unlink(&pub->le); + tmr_cancel(&pub->tmr); + mem_deref(pub->ua); + mem_deref(pub->etag); +} + + +void publisher_update_status(void) +{ + struct le *le; + + for (le = publ.head; le; le = le->next) { + + struct publisher *pub = le->data; + + pub->refresh = 0; + publish(pub); + } +} + + +static int publisher_alloc(struct ua *ua) +{ + struct publisher *pub; + + pub = mem_zalloc(sizeof(*pub), destructor); + if (!pub) + return ENOMEM; + + pub->ua = mem_ref(ua); + pub->expires = account_pubint(ua_account(ua)); + + tmr_init(&pub->tmr); + tmr_start(&pub->tmr, 100, tmr_handler, pub); + + list_append(&publ, &pub->le, pub); + + return 0; +} + + +int publisher_init(void) +{ + struct le *le; + int err = 0; + + for (le = list_head(uag_list()); le; le = le->next) { + + struct ua *ua = le->data; + struct account *acc = ua_account(ua); + + if (account_pubint(acc) > 0) + err |= publisher_alloc(ua); + } + + if (err) + return err; + + return 0; +} + + +void publisher_close(void) +{ + struct le *le; + + for (le = list_head(&publ); le; le = le->next) { + + struct publisher *pub = le->data; + + pub->expires = 0; + publish(pub); + } + + list_flush(&publ); +} diff --git a/modules/presence/subscriber.c b/modules/presence/subscriber.c index 418d0d0..66c15a2 100644 --- a/modules/presence/subscriber.c +++ b/modules/presence/subscriber.c @@ -111,9 +111,7 @@ static void notify_handler(struct sip *sip, const struct sip_msg *msg, } if (!re_regex((const char *)mbuf_buf(msg->mb), mbuf_get_left(msg->mb), - "[^<]*[^<]*[^<]*", - NULL, &pl, NULL)) { - + "[^<]+", &pl)) { if (!pl_strcasecmp(&pl, "open")) status = PRESENCE_OPEN; } -- cgit v1.2.3