summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorAlfred E. Heggestad <aeh@db.org>2014-11-01 22:04:34 +0100
committerAlfred E. Heggestad <aeh@db.org>2014-11-01 22:04:34 +0100
commit0d11889082d7ce823a2965f75ac896c27d62ae25 (patch)
tree6cca9551c8d58b1fabd60c866fcc5c71334136ca /modules
parent0a240e73793d84423285930a682984359e725c75 (diff)
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
Diffstat (limited to 'modules')
-rw-r--r--modules/account/account.c1
-rw-r--r--modules/menu/menu.c65
-rw-r--r--modules/presence/module.mk2
-rw-r--r--modules/presence/notifier.c43
-rw-r--r--modules/presence/presence.c61
-rw-r--r--modules/presence/presence.h7
-rw-r--r--modules/presence/publisher.c277
-rw-r--r--modules/presence/subscriber.c4
8 files changed, 415 insertions, 45 deletions
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(&notifierl);
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;
}