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