diff options
-rw-r--r-- | include/baresip.h | 1 | ||||
-rw-r--r-- | modules/account/account.c | 1 | ||||
-rw-r--r-- | modules/menu/menu.c | 65 | ||||
-rw-r--r-- | modules/presence/module.mk | 2 | ||||
-rw-r--r-- | modules/presence/notifier.c | 43 | ||||
-rw-r--r-- | modules/presence/presence.c | 61 | ||||
-rw-r--r-- | modules/presence/presence.h | 7 | ||||
-rw-r--r-- | modules/presence/publisher.c | 277 | ||||
-rw-r--r-- | modules/presence/subscriber.c | 4 | ||||
-rw-r--r-- | src/account.c | 9 | ||||
-rw-r--r-- | src/contact.c | 2 | ||||
-rw-r--r-- | src/core.h | 1 | ||||
-rw-r--r-- | src/sipreq.c | 5 | ||||
-rw-r--r-- | src/ua.c | 101 |
14 files changed, 494 insertions, 85 deletions
diff --git a/include/baresip.h b/include/baresip.h index 966c618..c2cb29c 100644 --- a/include/baresip.h +++ b/include/baresip.h @@ -42,6 +42,7 @@ struct list *account_aucodecl(const struct account *acc); struct list *account_vidcodecl(const struct account *acc); struct sip_addr *account_laddr(const struct account *acc); uint32_t account_regint(const struct account *acc); +uint32_t account_pubint(const struct account *acc); /* 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 <baresip.h> #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 <string.h> +#include <re.h> +#include <baresip.h> +#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, + "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\r\n" + "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\"\r\n" + " xmlns:dm=\"urn:ietf:params:xml:ns:pidf:data-model\"\r\n" + " xmlns:rpid=\"urn:ietf:params:xml:ns:pidf:rpid\"\r\n" + " entity=\"%s\">\r\n" + " <dm:person id=\"p4159\"><rpid:activities/></dm:person>\r\n" + " <tuple id=\"t4109\">\r\n" + " <status>\r\n" + " <basic>%s</basic>\r\n" + " </status>\r\n" + " <contact>%s</contact>\r\n" + " </tuple>\r\n" + "</presence>\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), - "<status>[^<]*<basic>[^<]*</basic>[^<]*</status>", - NULL, &pl, NULL)) { - + "<basic>[^<]+</basic>", &pl)) { if (!pl_strcasecmp(&pl, "open")) status = PRESENCE_OPEN; } diff --git a/src/account.c b/src/account.c index fed2b00..0269b04 100644 --- a/src/account.c +++ b/src/account.c @@ -279,6 +279,9 @@ static int sip_params_decode(struct account *acc, const struct sip_addr *aor) acc->regint = REG_INTERVAL + (rand_u32()&0xff); err |= param_u32(&acc->regint, &aor->params, "regint"); + acc->pubint = 0; + err |= param_u32(&acc->pubint, &aor->params, "pubint"); + err |= param_dstr(&acc->regq, &aor->params, "regq"); for (i=0; i<ARRAY_SIZE(acc->outbound); i++) { @@ -510,6 +513,12 @@ uint32_t account_regint(const struct account *acc) } +uint32_t account_pubint(const struct account *acc) +{ + return acc ? acc->pubint : 0; +} + + static const char *answermode_str(enum answermode mode) { switch (mode) { diff --git a/src/contact.c b/src/contact.c index 5161a1c..c06c7a1 100644 --- a/src/contact.c +++ b/src/contact.c @@ -128,7 +128,7 @@ const char *contact_presence_str(enum presence_status status) switch (status) { default: - case PRESENCE_UNKNOWN: return "\x1b[32m\x1b[;m"; + case PRESENCE_UNKNOWN: return "\x1b[32mUnknown\x1b[;m"; case PRESENCE_OPEN: return "\x1b[32mOnline\x1b[;m"; case PRESENCE_CLOSED: return "\x1b[31mOffline\x1b[;m"; case PRESENCE_BUSY: return "\x1b[31mBusy\x1b[;m"; @@ -58,6 +58,7 @@ struct account { char *outbound[2]; /**< Optional SIP outbound proxies */ uint32_t ptime; /**< Configured packet time in [ms] */ uint32_t regint; /**< Registration interval in [seconds] */ + uint32_t pubint; /**< Publication interval in [seconds] */ char *regq; /**< Registration Q-value */ char *rtpkeep; /**< RTP Keepalive mechanism */ char *sipnat; /**< SIP Nat mechanism */ diff --git a/src/sipreq.c b/src/sipreq.c index 1518645..c1510b6 100644 --- a/src/sipreq.c +++ b/src/sipreq.c @@ -73,7 +73,8 @@ static void resp_handler(int err, const struct sip_msg *msg, void *arg) } out: - sr->resph(err, msg, sr->arg); + if (sr->resph) + sr->resph(err, msg, sr->arg); /* destroy now */ mem_deref(sr); @@ -104,7 +105,7 @@ int sip_req_send(struct ua *ua, const char *method, const char *uri, struct sip_req *sr; int err; - if (!ua || !method || !uri || !resph || !fmt) + if (!ua || !method || !uri || !fmt) return EINVAL; routev[0] = ua_outbound(ua); @@ -394,16 +394,19 @@ static void handle_options(struct ua *ua, const struct sip_msg *msg) err = sip_treplyf(NULL, NULL, uag.sip, msg, true, 200, "OK", + "Allow: %s\r\n" + "%H" "%H" "Content-Type: application/sdp\r\n" "Content-Length: %zu\r\n" "\r\n" "%b", + uag_allowed_methods(), + ua_print_supported, ua, sip_contact_print, &contact, mbuf_get_left(desc), mbuf_buf(desc), mbuf_get_left(desc)); - if (err) { warning("ua: options: sip_treplyf: %m\n", err); } @@ -586,6 +589,47 @@ int ua_alloc(struct ua **uap, const char *aor) } +static int uri_complete(struct ua *ua, struct mbuf *buf, const char *uri) +{ + size_t len; + int err = 0; + + len = str_len(uri); + + /* Append sip: scheme if missing */ + if (0 != re_regex(uri, len, "sip:")) + err |= mbuf_printf(buf, "sip:"); + + err |= mbuf_write_str(buf, uri); + + /* Append domain if missing */ + if (0 != re_regex(uri, len, "[^@]+@[^]+", NULL, NULL)) { +#if HAVE_INET6 + if (AF_INET6 == ua->acc->luri.af) + err |= mbuf_printf(buf, "@[%r]", + &ua->acc->luri.host); + else +#endif + err |= mbuf_printf(buf, "@%r", + &ua->acc->luri.host); + + /* Also append port if specified and not 5060 */ + switch (ua->acc->luri.port) { + + case 0: + case SIP_PORT: + break; + + default: + err |= mbuf_printf(buf, ":%u", ua->acc->luri.port); + break; + } + } + + return err; +} + + /** * Connect an outgoing call to a given SIP uri * @@ -605,14 +649,11 @@ int ua_connect(struct ua *ua, struct call **callp, struct call *call = NULL; struct mbuf *dialbuf; struct pl pl; - size_t len; int err = 0; if (!ua || !str_isset(uri)) return EINVAL; - len = str_len(uri); - dialbuf = mbuf_alloc(64); if (!dialbuf) return ENOMEM; @@ -620,35 +661,7 @@ int ua_connect(struct ua *ua, struct call **callp, if (params) err |= mbuf_printf(dialbuf, "<"); - /* Append sip: scheme if missing */ - if (0 != re_regex(uri, len, "sip:")) - err |= mbuf_printf(dialbuf, "sip:"); - - err |= mbuf_write_str(dialbuf, uri); - - /* Append domain if missing */ - if (0 != re_regex(uri, len, "[^@]+@[^]+", NULL, NULL)) { -#if HAVE_INET6 - if (AF_INET6 == ua->acc->luri.af) - err |= mbuf_printf(dialbuf, "@[%r]", - &ua->acc->luri.host); - else -#endif - err |= mbuf_printf(dialbuf, "@%r", - &ua->acc->luri.host); - - /* Also append port if specified and not 5060 */ - switch (ua->acc->luri.port) { - - case 0: - case SIP_PORT: - break; - - default: - err |= mbuf_printf(dialbuf, ":%u", ua->acc->luri.port); - break; - } - } + err |= uri_complete(ua, dialbuf, uri); if (params) { err |= mbuf_printf(dialbuf, ";%s", params); @@ -771,12 +784,23 @@ int ua_print_status(struct re_printf *pf, const struct ua *ua) int ua_options_send(struct ua *ua, const char *uri, options_resp_h *resph, void *arg) { - int err; + struct mbuf *dialbuf; + int err = 0; - if (!ua) + (void)arg; + + if (!ua || !str_isset(uri)) return EINVAL; - err = sip_req_send(ua, "OPTIONS", uri, resph, arg, + dialbuf = mbuf_alloc(64); + if (!dialbuf) + return ENOMEM; + + err |= uri_complete(ua, dialbuf, uri); + + dialbuf->buf[dialbuf->end] = '\0'; + + err = sip_req_send(ua, "OPTIONS", (char *)dialbuf->buf, resph, NULL, "Accept: application/sdp\r\n" "Content-Length: 0\r\n" "\r\n"); @@ -784,6 +808,8 @@ int ua_options_send(struct ua *ua, const char *uri, warning("ua: send options: (%m)\n", err); } + mem_deref(dialbuf); + return err; } @@ -1542,7 +1568,8 @@ struct list *uag_list(void) */ const char *uag_allowed_methods(void) { - return "INVITE,ACK,BYE,CANCEL,OPTIONS,REFER,NOTIFY,SUBSCRIBE,INFO"; + return "INVITE,ACK,BYE,CANCEL,OPTIONS,REFER," + "NOTIFY,SUBSCRIBE,INFO,MESSAGE"; } |