diff options
author | Alfred E. Heggestad <aeh@db.org> | 2014-02-09 11:50:07 +0100 |
---|---|---|
committer | Alfred E. Heggestad <aeh@db.org> | 2014-02-09 11:50:07 +0100 |
commit | 98bf08bdcf2edd9d397f32650a8bfe62186fbecf (patch) | |
tree | ebc6ec71f44bff8c42e4eefced61948623df02fc /modules/presence | |
parent | e6ad5cf4401b860ba402d4b7b3c7c254bc87a019 (diff) |
baresip 0.4.10
Diffstat (limited to 'modules/presence')
-rw-r--r-- | modules/presence/module.mk | 11 | ||||
-rw-r--r-- | modules/presence/notifier.c | 270 | ||||
-rw-r--r-- | modules/presence/presence.c | 41 | ||||
-rw-r--r-- | modules/presence/presence.h | 13 | ||||
-rw-r--r-- | modules/presence/subscriber.c | 273 |
5 files changed, 608 insertions, 0 deletions
diff --git a/modules/presence/module.mk b/modules/presence/module.mk new file mode 100644 index 0000000..6ec5d17 --- /dev/null +++ b/modules/presence/module.mk @@ -0,0 +1,11 @@ +# +# module.mk +# +# Copyright (C) 2010 Creytiv.com +# + +MOD := presence +$(MOD)_SRCS += presence.c subscriber.c notifier.c +$(MOD)_LFLAGS += + +include mk/mod.mk diff --git a/modules/presence/notifier.c b/modules/presence/notifier.c new file mode 100644 index 0000000..ced7b75 --- /dev/null +++ b/modules/presence/notifier.c @@ -0,0 +1,270 @@ +/** + * @file notifier.c Presence notifier + * + * Copyright (C) 2010 Creytiv.com + */ +#include <re.h> +#include <baresip.h> +#include "presence.h" + + +/* + * Notifier - other people are subscribing to the status of our AOR. + * we must maintain a list of active notifications. we receive a SUBSCRIBE + * message from peer, and send NOTIFY to all peers when the Status changes + */ + + +struct notifier { + struct le le; + struct sipevent_sock *sock; + struct sipnot *not; + struct ua *ua; +}; + +static enum presence_status my_status = PRESENCE_OPEN; +static struct list notifierl; +static struct sipevent_sock *evsock; + + +static const char *presence_status_str(enum presence_status st) +{ + switch (st) { + + case PRESENCE_OPEN: return "open"; + case PRESENCE_CLOSED: return "closed"; + default: return "?"; + } +} + + +static int notify(struct notifier *not, enum presence_status status) +{ + const char *aor = ua_aor(not->ua); + struct mbuf *mb; + int err; + + mb = mbuf_alloc(1024); + if (!mb) + return ENOMEM; + + 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(status), aor); + if (err) + goto out; + + mb->pos = 0; + + err = sipevent_notify(not->not, mb, SIPEVENT_ACTIVE, 0, 0); + if (err) { + warning("presence: notify to %s failed (%m)\n", aor, err); + } + + out: + mem_deref(mb); + return err; +} + + +static void sipnot_close_handler(int err, const struct sip_msg *msg, + void *arg) +{ + struct notifier *not = arg; + + if (err) { + info("presence: notifier closed (%m)\n", err); + } + else if (msg) { + info("presence: notifier closed (%u %r)\n", + msg->scode, &msg->reason); + } + + not = mem_deref(not); +} + + +static void destructor(void *arg) +{ + struct notifier *not = arg; + + list_unlink(¬->le); + mem_deref(not->not); + mem_deref(not->sock); + mem_deref(not->ua); +} + + +static int auth_handler(char **username, char **password, + const char *realm, void *arg) +{ + return account_auth(arg, username, password, realm); +} + + +static int notifier_alloc(struct notifier **notp, struct sipevent_sock *sock, + const struct sip_msg *msg, + const struct sipevent_event *se, struct ua *ua) +{ + struct notifier *not; + int err; + + if (!sock || !msg || !se) + return EINVAL; + + not = mem_zalloc(sizeof(*not), destructor); + if (!not) + return ENOMEM; + + not->sock = mem_ref(sock); + not->ua = mem_ref(ua); + + err = sipevent_accept(¬->not, sock, msg, NULL, se, 200, "OK", + 600, 600, 600, + ua_cuser(not->ua), "application/pidf+xml", + auth_handler, ua_prm(not->ua), true, + sipnot_close_handler, not, NULL); + if (err) { + warning("presence: sipevent_accept failed: %m\n", err); + goto out; + } + + list_append(¬ifierl, ¬->le, not); + + out: + if (err) + mem_deref(not); + else if (notp) + *notp = not; + + return err; +} + + +static int notifier_add(struct sipevent_sock *sock, const struct sip_msg *msg, + struct ua *ua) +{ + const struct sip_hdr *hdr; + struct sipevent_event se; + struct notifier *not; + int err; + + hdr = sip_msg_hdr(msg, SIP_HDR_EVENT); + if (!hdr) + return EPROTO; + + err = sipevent_event_decode(&se, &hdr->val); + if (err) + return err; + + if (pl_strcasecmp(&se.event, "presence")) { + info("presence: unexpected event '%r'\n", &se.event); + return EPROTO; + } + + err = notifier_alloc(¬, sock, msg, &se, ua); + if (err) + return err; + + (void)notify(not, my_status); + + return 0; +} + + +static void notifier_update_status(enum presence_status status) +{ + 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); + } +} + + +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; + + (void)arg; + + ua = uag_find(&msg->uri.user); + if (!ua) { + warning("presence: no UA found for %r\n", &msg->uri.user); + (void)sip_treply(NULL, uag_sip(), msg, 404, "Not Found"); + return true; + } + + if (notifier_add(evsock, msg, ua)) + (void)sip_treply(NULL, uag_sip(), msg, 400, "Bad Presence"); + + return true; +} + + +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)); +} + + +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 new file mode 100644 index 0000000..7ed4908 --- /dev/null +++ b/modules/presence/presence.c @@ -0,0 +1,41 @@ +/** + * @file presence.c Presence module + * + * Copyright (C) 2010 Creytiv.com + */ +#include <re.h> +#include <baresip.h> +#include "presence.h" + + +static int module_init(void) +{ + int err; + + err = subscriber_init(); + if (err) + return err; + + err = notifier_init(); + if (err) + return err; + + return 0; +} + + +static int module_close(void) +{ + notifier_close(); + subscriber_close(); + + return 0; +} + + +const struct mod_export DECL_EXPORTS(presence) = { + "presence", + "application", + module_init, + module_close +}; diff --git a/modules/presence/presence.h b/modules/presence/presence.h new file mode 100644 index 0000000..a1c5e31 --- /dev/null +++ b/modules/presence/presence.h @@ -0,0 +1,13 @@ +/** + * @file presence.h Presence module interface + * + * Copyright (C) 2010 Creytiv.com + */ + + +int subscriber_init(void); +void subscriber_close(void); + + +int notifier_init(void); +void notifier_close(void); diff --git a/modules/presence/subscriber.c b/modules/presence/subscriber.c new file mode 100644 index 0000000..22361d5 --- /dev/null +++ b/modules/presence/subscriber.c @@ -0,0 +1,273 @@ +/** + * @file subscriber.c Presence subscriber + * + * Copyright (C) 2010 Creytiv.com + */ +#include <re.h> +#include <baresip.h> +#include "presence.h" + + +/* + * Subscriber - we subscribe to the status information of N resources. + * + * For each entry in the address book marked with ;presence=p2p, + * we send a SUBSCRIBE to that person, and expect to receive + * a NOTIFY when her status changes. + */ + + +struct presence { + struct le le; + struct sipsub *sub; + struct tmr tmr; + enum presence_status status; + unsigned failc; + struct contact *contact; +}; + +static struct list presencel; + + +static void tmr_handler(void *arg); + + +static uint32_t wait_term(const struct sipevent_substate *substate) +{ + uint32_t wait; + + switch (substate->reason) { + + case SIPEVENT_DEACTIVATED: + case SIPEVENT_TIMEOUT: + wait = 5; + break; + + case SIPEVENT_REJECTED: + case SIPEVENT_NORESOURCE: + wait = 3600; + break; + + case SIPEVENT_PROBATION: + case SIPEVENT_GIVEUP: + default: + wait = 300; + if (pl_isset(&substate->retry_after)) + wait = max(wait, pl_u32(&substate->retry_after)); + break; + } + + return wait; +} + + +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 notify_handler(struct sip *sip, const struct sip_msg *msg, + void *arg) +{ + enum presence_status status = PRESENCE_CLOSED; + struct presence *pres = arg; + const struct sip_hdr *hdr; + struct pl pl; + + pres->failc = 0; + + hdr = sip_msg_hdr(msg, SIP_HDR_CONTENT_TYPE); + if (!hdr || 0 != pl_strcasecmp(&hdr->val, "application/pidf+xml")) { + + if (hdr) + warning("presence: unsupported content-type: '%r'\n", + &hdr->val); + + sip_treplyf(NULL, NULL, sip, msg, false, + 415, "Unsupported Media Type", + "Accept: application/pidf+xml\r\n" + "Content-Length: 0\r\n" + "\r\n"); + return; + } + + if (!re_regex((const char *)mbuf_buf(msg->mb), mbuf_get_left(msg->mb), + "<status>[^<]*<basic>[^<]*</basic>[^<]*</status>", + NULL, &pl, NULL)) { + + if (!pl_strcasecmp(&pl, "open")) + status = PRESENCE_OPEN; + } + + if (!re_regex((const char *)mbuf_buf(msg->mb), mbuf_get_left(msg->mb), + "<rpid:away/>")) { + + status = PRESENCE_CLOSED; + } + else if (!re_regex((const char *)mbuf_buf(msg->mb), + mbuf_get_left(msg->mb), + "<rpid:busy/>")) { + + status = PRESENCE_BUSY; + } + else if (!re_regex((const char *)mbuf_buf(msg->mb), + mbuf_get_left(msg->mb), + "<rpid:on-the-phone/>")) { + + status = PRESENCE_BUSY; + } + + (void)sip_treply(NULL, sip, msg, 200, "OK"); + + contact_set_presence(pres->contact, status); +} + + +static void close_handler(int err, const struct sip_msg *msg, + const struct sipevent_substate *substate, void *arg) +{ + struct presence *pres = arg; + uint32_t wait; + + pres->sub = mem_deref(pres->sub); + + info("presence: subscriber closed <%r>: ", + &contact_addr(pres->contact)->auri); + + if (substate) { + info("%s", sipevent_reason_name(substate->reason)); + wait = wait_term(substate); + } + else if (msg) { + info("%u %r", msg->scode, &msg->reason); + wait = wait_fail(++pres->failc); + } + else { + info("%m", err); + wait = wait_fail(++pres->failc); + } + + info("; will retry in %u secs (failc=%u)\n", wait, pres->failc); + + tmr_start(&pres->tmr, wait * 1000, tmr_handler, pres); + + contact_set_presence(pres->contact, PRESENCE_UNKNOWN); +} + + +static void destructor(void *arg) +{ + struct presence *pres = arg; + + list_unlink(&pres->le); + tmr_cancel(&pres->tmr); + mem_deref(pres->contact); + mem_deref(pres->sub); +} + + +static int auth_handler(char **username, char **password, + const char *realm, void *arg) +{ + return account_auth(arg, username, password, realm); +} + + +static int subscribe(struct presence *pres) +{ + const char *routev[1]; + struct ua *ua; + char uri[256]; + int err; + + /* We use the first UA */ + ua = uag_find_aor(NULL); + if (!ua) { + warning("presence: no UA found\n"); + return ENOENT; + } + + pl_strcpy(&contact_addr(pres->contact)->auri, uri, sizeof(uri)); + + routev[0] = ua_outbound(ua); + + err = sipevent_subscribe(&pres->sub, uag_sipevent_sock(), uri, NULL, + ua_aor(ua), "presence", NULL, 600, + ua_cuser(ua), routev, routev[0] ? 1 : 0, + auth_handler, ua_prm(ua), true, NULL, + notify_handler, close_handler, pres, + "%H", ua_print_supported, ua); + if (err) { + warning("presence: sipevent_subscribe failed: %m\n", err); + } + + return err; +} + + +static void tmr_handler(void *arg) +{ + struct presence *pres = arg; + + if (subscribe(pres)) { + tmr_start(&pres->tmr, wait_fail(++pres->failc) * 1000, + tmr_handler, pres); + } +} + + +static int presence_alloc(struct contact *contact) +{ + struct presence *pres; + + pres = mem_zalloc(sizeof(*pres), destructor); + if (!pres) + return ENOMEM; + + pres->status = PRESENCE_UNKNOWN; + pres->contact = mem_ref(contact); + + tmr_init(&pres->tmr); + tmr_start(&pres->tmr, 1000, tmr_handler, pres); + + list_append(&presencel, &pres->le, pres); + + return 0; +} + + +int subscriber_init(void) +{ + struct le *le; + int err = 0; + + for (le = list_head(contact_list()); le; le = le->next) { + + struct contact *c = le->data; + struct sip_addr *addr = contact_addr(c); + struct pl val; + + if (0 == sip_param_decode(&addr->params, "presence", &val) && + 0 == pl_strcasecmp(&val, "p2p")) { + + err |= presence_alloc(le->data); + } + } + + info("Subscribing to %u contacts\n", list_count(&presencel)); + + return err; +} + + +void subscriber_close(void) +{ + list_flush(&presencel); +} |