1 /**
2  * @file publisher.c Presence Publisher (RFC 3903)
3  *
4  * Copyright (C) 2010 Creytiv.com
5  * Copyright (C) 2014 Juha Heinanen
6  */
7 
8 #include <string.h>
9 #include <re.h>
10 #include <baresip.h>
11 #include "presence.h"
12 
13 
14 struct publisher {
15 	struct le le;
16 	struct tmr tmr;
17 	unsigned failc;
18 	char *etag;
19 	unsigned int expires;
20 	unsigned int refresh;
21 	struct ua *ua;
22 };
23 
24 static struct list publ = LIST_INIT;
25 
26 static void tmr_handler(void *arg);
27 static int publish(struct publisher *pub);
28 
29 
response_handler(int err,const struct sip_msg * msg,void * arg)30 static void response_handler(int err, const struct sip_msg *msg, void *arg)
31 {
32 	struct publisher *pub = arg;
33 	const struct sip_hdr *etag_hdr;
34 
35 	if (err)
36 		return;
37 
38 	if (msg->scode < 200) {
39 		return;
40 	}
41 
42 	if (msg->scode < 300) {
43 
44 		if (pub->expires == 0)
45 			return;
46 
47 		etag_hdr = sip_msg_xhdr(msg, "SIP-ETag");
48 		if (etag_hdr) {
49 			mem_deref(pub->etag);
50 			pl_strdup(&(pub->etag), &(etag_hdr->val));
51 			pub->refresh = 1;
52 			tmr_start(&pub->tmr, pub->expires * 900,
53 				  tmr_handler, pub);
54 		}
55 		else {
56 			warning("%s: publisher got 200 OK without etag\n",
57 				ua_aor(pub->ua));
58 		}
59 	}
60 	else if (msg->scode == 412) {
61 
62 		mem_deref(pub->etag);
63 		pub->etag = NULL;
64 		pub->refresh = 0;
65 		publish(pub);
66 
67 	}
68 	else {
69 		warning("%s: publisher got error response %u %r\n",
70 			ua_aor(pub->ua), msg->scode, &msg->reason);
71 	}
72 
73 	return;
74 }
75 
76 
77 /* move this to presence.c */
presence_status_str(enum presence_status st)78 static const char *presence_status_str(enum presence_status st)
79 {
80 	switch (st) {
81 
82 	case PRESENCE_OPEN:   return "open";
83 	case PRESENCE_CLOSED: return "closed";
84 	case PRESENCE_UNKNOWN: return "unknown";
85 	default: return "?";
86 	}
87 }
88 
89 
publish(struct publisher * pub)90 static int publish(struct publisher *pub)
91 {
92 	int err;
93 	const char *aor = ua_aor(pub->ua);
94 	struct mbuf *mb;
95 
96 	mb = mbuf_alloc(1024);
97 	if (!mb)
98 		return ENOMEM;
99 
100 	if (pub->expires && !pub->refresh)
101 		err = mbuf_printf(mb,
102 	"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\r\n"
103 	"<presence xmlns=\"urn:ietf:params:xml:ns:pidf\"\r\n"
104 	"    xmlns:dm=\"urn:ietf:params:xml:ns:pidf:data-model\"\r\n"
105 	"    xmlns:rpid=\"urn:ietf:params:xml:ns:pidf:rpid\"\r\n"
106 	"    entity=\"%s\">\r\n"
107 	"  <dm:person id=\"p4159\"><rpid:activities/></dm:person>\r\n"
108 	"  <tuple id=\"t4109\">\r\n"
109 	"    <status>\r\n"
110 	"      <basic>%s</basic>\r\n"
111 	"    </status>\r\n"
112 	"    <contact>%s</contact>\r\n"
113 	"  </tuple>\r\n"
114 	"</presence>\r\n"
115 		  ,aor,
116 		  presence_status_str(ua_presence_status(pub->ua)), aor);
117 	else
118 		err = mbuf_printf(mb, "");
119 	if (err)
120 		goto out;
121 
122 	mb->pos = 0;
123 
124 	/* XXX: can be simplified with 1 function call, by adding a
125 	   print-handler that prints "SIP-If-Match: ETAG" */
126 	if (pub->etag)
127 		err = sip_req_send(pub->ua, "PUBLISH", aor,
128 				   pub->expires ? response_handler : NULL,
129 				   pub,
130 			   "%s"
131 			   "Event: presence\r\n"
132 			   "Expires: %u\r\n"
133 			   "SIP-If-Match: %s\r\n"
134 			   "Content-Length: %zu\r\n"
135 			   "\r\n"
136 			   "%b",
137 			   pub->expires
138 				   ? "Content-Type: application/pidf+xml\r\n"
139 				   : "",
140 
141 			   pub->expires,
142 			   pub->etag,
143 			   mbuf_get_left(mb),
144 			   mbuf_buf(mb),
145 			   mbuf_get_left(mb));
146 	else
147 		err = sip_req_send(pub->ua, "PUBLISH", aor,
148 				   pub->expires ? response_handler : NULL,
149 				   pub,
150 			   "%s"
151 			   "Event: presence\r\n"
152 			   "Expires: %u\r\n"
153 			   "Content-Length: %zu\r\n"
154 			   "\r\n"
155 			   "%b",
156 			   pub->expires
157 				   ? "Content-Type: application/pidf+xml\r\n"
158 				   : "",
159 			   pub->expires,
160 			   mbuf_get_left(mb),
161 			   mbuf_buf(mb),
162 			   mbuf_get_left(mb));
163 	if (err) {
164 		warning("publisher: send PUBLISH: (%m)\n", err);
165 	}
166 
167 out:
168 	mem_deref(mb);
169 
170 	return err;
171 }
172 
173 
174 /* move to presence.c */
wait_fail(unsigned failc)175 static uint32_t wait_fail(unsigned failc)
176 {
177 	switch (failc) {
178 
179 	case 1:  return 30;
180 	case 2:  return 300;
181 	case 3:  return 3600;
182 	default: return 86400;
183 	}
184 }
185 
186 
tmr_handler(void * arg)187 static void tmr_handler(void *arg)
188 {
189 	struct publisher *pub = arg;
190 
191 	if (publish(pub))
192 		tmr_start(&pub->tmr, wait_fail(++pub->failc) * 1000,
193 			  tmr_handler, pub);
194 	else
195 		pub->failc = 0;
196 }
197 
198 
destructor(void * arg)199 static void destructor(void *arg)
200 {
201 	struct publisher *pub = arg;
202 
203 	list_unlink(&pub->le);
204 	tmr_cancel(&pub->tmr);
205 	mem_deref(pub->ua);
206 	mem_deref(pub->etag);
207 }
208 
209 
publisher_update_status(struct ua * ua)210 void publisher_update_status(struct ua *ua)
211 {
212 	struct le *le;
213 
214 	for (le = publ.head; le; le = le->next) {
215 
216 		struct publisher *pub = le->data;
217 
218 		if (pub->ua == ua) {
219 			pub->refresh = 0;
220 			publish(pub);
221 		}
222 	}
223 }
224 
225 
publisher_alloc(struct ua * ua)226 static int publisher_alloc(struct ua *ua)
227 {
228 	struct publisher *pub;
229 
230 	pub = mem_zalloc(sizeof(*pub), destructor);
231 	if (!pub)
232 		return ENOMEM;
233 
234 	pub->ua = mem_ref(ua);
235 	pub->expires = account_pubint(ua_account(ua));
236 
237 	tmr_init(&pub->tmr);
238 	tmr_start(&pub->tmr, 10, tmr_handler, pub);
239 
240 	list_append(&publ, &pub->le, pub);
241 
242 	return 0;
243 }
244 
245 
pub_ua_event_handler(struct ua * ua,enum ua_event ev,struct call * call,const char * prm,void * arg)246 static void pub_ua_event_handler(struct ua *ua,
247 				 enum ua_event ev,
248 				 struct call *call,
249 				 const char *prm,
250 				 void *arg )
251 {
252 	(void)call;
253 	(void)prm;
254 	(void)arg;
255 
256 	if (account_pubint(ua_account(ua)) == 0)
257 		return;
258 
259 	if (ev == UA_EVENT_REGISTER_OK) {
260 		if (ua_presence_status(ua) == PRESENCE_UNKNOWN) {
261 			ua_presence_status_set(ua, PRESENCE_OPEN);
262 			publisher_update_status(ua);
263 		}
264 	}
265 }
266 
267 
publisher_init(void)268 int publisher_init(void)
269 {
270 	struct le *le;
271 	int err = 0;
272 
273 	uag_event_register(pub_ua_event_handler, NULL);
274 
275 	for (le = list_head(uag_list()); le; le = le->next) {
276 
277 		struct ua *ua = le->data;
278 		struct account *acc = ua_account(ua);
279 
280 		if (account_pubint(acc) == 0)
281 			continue;
282 
283 		err |= publisher_alloc(ua);
284 	}
285 
286 	if (err)
287 		return err;
288 
289 	return 0;
290 }
291 
292 
publisher_close(void)293 void publisher_close(void)
294 {
295 	struct le *le;
296 
297 	uag_event_unregister(pub_ua_event_handler);
298 
299 	for (le = list_head(&publ); le; le = le->next) {
300 
301 		struct publisher *pub = le->data;
302 
303 		ua_presence_status_set(pub->ua, PRESENCE_CLOSED);
304 		pub->expires = 0;
305 		publish(pub);
306 	}
307 
308 	list_flush(&publ);
309 }
310