summaryrefslogtreecommitdiff
path: root/modules/presence
diff options
context:
space:
mode:
authorAlfred E. Heggestad <aeh@db.org>2014-02-09 11:50:07 +0100
committerAlfred E. Heggestad <aeh@db.org>2014-02-09 11:50:07 +0100
commit98bf08bdcf2edd9d397f32650a8bfe62186fbecf (patch)
treeebc6ec71f44bff8c42e4eefced61948623df02fc /modules/presence
parente6ad5cf4401b860ba402d4b7b3c7c254bc87a019 (diff)
baresip 0.4.10
Diffstat (limited to 'modules/presence')
-rw-r--r--modules/presence/module.mk11
-rw-r--r--modules/presence/notifier.c270
-rw-r--r--modules/presence/presence.c41
-rw-r--r--modules/presence/presence.h13
-rw-r--r--modules/presence/subscriber.c273
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(&not->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->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(&notifierl, &not->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(&not, 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(&notifierl);
+ 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);
+}