Add support for RFC 4662 resource list subscriptions.
[asterisk/asterisk.git] / res / res_pjsip_exten_state.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Kevin Harwell <kharwell@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*** MODULEINFO
20         <depend>pjproject</depend>
21         <depend>res_pjsip</depend>
22         <depend>res_pjsip_pubsub</depend>
23         <support_level>core</support_level>
24  ***/
25
26 #include "asterisk.h"
27
28 #include <pjsip.h>
29 #include <pjsip_simple.h>
30 #include <pjlib.h>
31
32 #include "asterisk/res_pjsip.h"
33 #include "asterisk/res_pjsip_pubsub.h"
34 #include "asterisk/res_pjsip_body_generator_types.h"
35 #include "asterisk/module.h"
36 #include "asterisk/logger.h"
37 #include "asterisk/astobj2.h"
38 #include "asterisk/sorcery.h"
39 #include "asterisk/app.h"
40
41 #define BODY_SIZE 1024
42 #define EVENT_TYPE_SIZE 50
43
44 /*!
45  * \brief A subscription for extension state
46  *
47  * This structure acts as the owner for the underlying SIP subscription. It
48  * also keeps a pointer to an associated "provider" so when a state changes
49  * a notify data creator is quickly accessible.
50  */
51 struct exten_state_subscription {
52         /*! Watcher id when registering for extension state changes */
53         int id;
54         /*! The SIP subscription */
55         struct ast_sip_subscription *sip_sub;
56         /*! Context in which subscription looks for updates */
57         char context[AST_MAX_CONTEXT];
58         /*! Extension within the context to receive updates from */
59         char exten[AST_MAX_EXTENSION];
60         /*! The subscription's user agent */
61         char *user_agent;
62         /*! The last known extension state */
63         enum ast_extension_states last_exten_state;
64         /*! The last known presence state */
65         enum ast_presence_state last_presence_state;
66 };
67
68 #define DEFAULT_PRESENCE_BODY "application/pidf+xml"
69 #define DEFAULT_DIALOG_BODY "application/dialog-info+xml"
70
71 static void subscription_shutdown(struct ast_sip_subscription *sub);
72 static int new_subscribe(struct ast_sip_endpoint *endpoint, const char *resource);
73 static int subscription_established(struct ast_sip_subscription *sub);
74 static void *get_notify_data(struct ast_sip_subscription *sub);
75 static void to_ami(struct ast_sip_subscription *sub,
76                    struct ast_str **buf);
77
78 struct ast_sip_notifier presence_notifier = {
79         .default_accept = DEFAULT_PRESENCE_BODY,
80         .new_subscribe = new_subscribe,
81         .subscription_established = subscription_established,
82         .get_notify_data = get_notify_data,
83 };
84
85 struct ast_sip_notifier dialog_notifier = {
86         .default_accept = DEFAULT_DIALOG_BODY,
87         .new_subscribe = new_subscribe,
88         .subscription_established = subscription_established,
89         .get_notify_data = get_notify_data,
90 };
91
92 struct ast_sip_subscription_handler presence_handler = {
93         .event_name = "presence",
94         .accept = { DEFAULT_PRESENCE_BODY, },
95         .subscription_shutdown = subscription_shutdown,
96         .to_ami = to_ami,
97         .notifier = &presence_notifier,
98 };
99
100 struct ast_sip_subscription_handler dialog_handler = {
101         .event_name = "dialog",
102         .accept = { DEFAULT_DIALOG_BODY, },
103         .subscription_shutdown = subscription_shutdown,
104         .to_ami = to_ami,
105         .notifier = &dialog_notifier,
106 };
107
108 static void exten_state_subscription_destructor(void *obj)
109 {
110         struct exten_state_subscription *sub = obj;
111
112         ast_free(sub->user_agent);
113         ao2_cleanup(sub->sip_sub);
114 }
115
116 static char *get_user_agent(const struct ast_sip_subscription *sip_sub)
117 {
118         size_t size;
119         char *user_agent = NULL;
120         pjsip_user_agent_hdr *user_agent_hdr = ast_sip_subscription_get_header(
121                         sip_sub, "User-Agent");
122
123         if (!user_agent_hdr) {
124                 return NULL;
125         }
126
127         size = pj_strlen(&user_agent_hdr->hvalue) + 1;
128         user_agent = ast_malloc(size);
129         ast_copy_pj_str(user_agent, &user_agent_hdr->hvalue, size);
130         return ast_str_to_lower(user_agent);
131 }
132
133 /*!
134  * \internal
135  * \brief Initialize the last extension state to something outside
136  * its usual states.
137  */
138 #define INITIAL_LAST_EXTEN_STATE -3
139
140 /*!
141  * \internal
142  * \brief Allocates an exten_state_subscription object.
143  *
144  * Creates the underlying SIP subscription for the given request. First makes
145  * sure that there are registered handler and provider objects available.
146  */
147 static struct exten_state_subscription *exten_state_subscription_alloc(
148                 struct ast_sip_subscription *sip_sub, struct ast_sip_endpoint *endpoint)
149 {
150         struct exten_state_subscription * exten_state_sub;
151
152         exten_state_sub = ao2_alloc(sizeof(*exten_state_sub), exten_state_subscription_destructor);
153         if (!exten_state_sub) {
154                 return NULL;
155         }
156
157         exten_state_sub->sip_sub = ao2_bump(sip_sub);
158         exten_state_sub->last_exten_state = INITIAL_LAST_EXTEN_STATE;
159         exten_state_sub->last_presence_state = AST_PRESENCE_NOT_SET;
160         exten_state_sub->user_agent = get_user_agent(sip_sub);
161         return exten_state_sub;
162 }
163
164 struct notify_task_data {
165         struct ast_sip_exten_state_data exten_state_data;
166         struct exten_state_subscription *exten_state_sub;
167         int terminate;
168 };
169
170 static void notify_task_data_destructor(void *obj)
171 {
172         struct notify_task_data *task_data = obj;
173
174         ao2_ref(task_data->exten_state_sub, -1);
175         ao2_cleanup(task_data->exten_state_data.device_state_info);
176         ast_free(task_data->exten_state_data.presence_subtype);
177         ast_free(task_data->exten_state_data.presence_message);
178         ast_free(task_data->exten_state_data.user_agent);
179 }
180
181 static struct notify_task_data *alloc_notify_task_data(char *exten, struct exten_state_subscription *exten_state_sub,
182                                                        struct ast_state_cb_info *info)
183 {
184         struct notify_task_data *task_data =
185                 ao2_alloc(sizeof(*task_data), notify_task_data_destructor);
186
187         if (!task_data) {
188                 ast_log(LOG_WARNING, "Unable to create notify task data\n");
189                 return NULL;
190         }
191
192         task_data->exten_state_sub = exten_state_sub;
193         task_data->exten_state_sub->last_exten_state = info->exten_state;
194         task_data->exten_state_sub->last_presence_state = info->presence_state;
195         ao2_ref(task_data->exten_state_sub, +1);
196
197         task_data->exten_state_data.exten = exten_state_sub->exten;
198         task_data->exten_state_data.exten_state = info->exten_state;
199         task_data->exten_state_data.presence_state = info->presence_state;
200         task_data->exten_state_data.presence_subtype = ast_strdup(info->presence_subtype);
201         task_data->exten_state_data.presence_message = ast_strdup(info->presence_message);
202         task_data->exten_state_data.user_agent = ast_strdup(exten_state_sub->user_agent);
203         task_data->exten_state_data.device_state_info = ao2_bump(info->device_state_info);
204         task_data->exten_state_data.sub = exten_state_sub->sip_sub;
205
206         ast_sip_subscription_get_local_uri(exten_state_sub->sip_sub,
207                         task_data->exten_state_data.local, sizeof(task_data->exten_state_data.local));
208         ast_sip_subscription_get_remote_uri(exten_state_sub->sip_sub,
209                         task_data->exten_state_data.remote, sizeof(task_data->exten_state_data.remote));
210
211         if ((info->exten_state == AST_EXTENSION_DEACTIVATED) ||
212             (info->exten_state == AST_EXTENSION_REMOVED)) {
213                 ast_log(LOG_WARNING, "Watcher for hint %s %s\n", exten, info->exten_state
214                          == AST_EXTENSION_REMOVED ? "removed" : "deactivated");
215                 task_data->terminate = 1;
216         }
217
218         return task_data;
219 }
220
221 static int notify_task(void *obj)
222 {
223         RAII_VAR(struct notify_task_data *, task_data, obj, ao2_cleanup);
224
225         /* Pool allocation has to happen here so that we allocate within a PJLIB thread */
226         task_data->exten_state_data.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
227                         "exten_state", 1024, 1024);
228         if (!task_data->exten_state_data.pool) {
229                 return -1;
230         }
231
232         task_data->exten_state_data.sub = task_data->exten_state_sub->sip_sub;
233
234         ast_sip_subscription_notify(task_data->exten_state_sub->sip_sub, &task_data->exten_state_data,
235                         task_data->terminate);
236
237         pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(),
238                         task_data->exten_state_data.pool);
239         return 0;
240 }
241
242 /*!
243  * \internal
244  * \brief Callback for exten/device state changes.
245  *
246  * Upon state change, send the appropriate notification to the subscriber.
247  */
248 static int state_changed(char *context, char *exten,
249                          struct ast_state_cb_info *info, void *data)
250 {
251         struct notify_task_data *task_data;
252         struct exten_state_subscription *exten_state_sub = data;
253
254         if (!(task_data = alloc_notify_task_data(exten, exten_state_sub, info))) {
255                 return -1;
256         }
257
258         /* safe to push this async since we copy the data from info and
259            add a ref for the device state info */
260         if (ast_sip_push_task(ast_sip_subscription_get_serializer(task_data->exten_state_sub->sip_sub),
261                               notify_task, task_data)) {
262                 ao2_cleanup(task_data);
263                 return -1;
264         }
265         return 0;
266 }
267
268 static void state_changed_destroy(int id, void *data)
269 {
270         struct exten_state_subscription *exten_state_sub = data;
271         ao2_cleanup(exten_state_sub);
272 }
273
274 static struct ast_datastore_info ds_info = { };
275 static const char ds_name[] = "exten state datastore";
276
277 /*!
278  * \internal
279  * \brief Add a datastore for exten exten_state_subscription.
280  *
281  * Adds the exten_state_subscription wrapper object to a datastore so it can be retrieved
282  * later based upon its association with the ast_sip_subscription.
283  */
284 static int add_datastore(struct exten_state_subscription *exten_state_sub)
285 {
286         RAII_VAR(struct ast_datastore *, datastore,
287                  ast_sip_subscription_alloc_datastore(&ds_info, ds_name), ao2_cleanup);
288
289         if (!datastore) {
290                 return -1;
291         }
292
293         datastore->data = exten_state_sub;
294         ast_sip_subscription_add_datastore(exten_state_sub->sip_sub, datastore);
295         ao2_ref(exten_state_sub, +1);
296         return 0;
297 }
298
299 /*!
300  * \internal
301  * \brief Get the exten_state_subscription object associated with the given
302  * ast_sip_subscription in the datastore.
303  */
304 static struct exten_state_subscription *get_exten_state_sub(
305         struct ast_sip_subscription *sub)
306 {
307         RAII_VAR(struct ast_datastore *, datastore,
308                  ast_sip_subscription_get_datastore(sub, ds_name), ao2_cleanup);
309
310         return datastore ? datastore->data : NULL;
311 }
312
313 static void subscription_shutdown(struct ast_sip_subscription *sub)
314 {
315         struct exten_state_subscription *exten_state_sub = get_exten_state_sub(sub);
316
317         if (!exten_state_sub) {
318                 return;
319         }
320
321         ast_extension_state_del(exten_state_sub->id, state_changed);
322         ast_sip_subscription_remove_datastore(exten_state_sub->sip_sub, ds_name);
323         /* remove data store reference */
324         ao2_cleanup(exten_state_sub);
325 }
326
327 static int new_subscribe(struct ast_sip_endpoint *endpoint,
328                 const char *resource)
329 {
330         if (!ast_exists_extension(NULL, endpoint->context, resource, PRIORITY_HINT, NULL)) {
331                 ast_log(LOG_WARNING, "Extension %s does not exist or has no associated hint\n", resource);
332                 return 404;
333         }
334
335         return 200;
336 }
337
338 static int subscription_established(struct ast_sip_subscription *sip_sub)
339 {
340         struct ast_sip_endpoint *endpoint = ast_sip_subscription_get_endpoint(sip_sub);
341         const char *resource = ast_sip_subscription_get_resource_name(sip_sub);
342         struct exten_state_subscription *exten_state_sub;
343
344         if (!(exten_state_sub = exten_state_subscription_alloc(sip_sub, endpoint))) {
345                 ao2_cleanup(endpoint);
346                 return -1;
347         }
348
349         ast_copy_string(exten_state_sub->context, endpoint->context, sizeof(exten_state_sub->context));
350         ast_copy_string(exten_state_sub->exten, resource, sizeof(exten_state_sub->exten));
351
352         if ((exten_state_sub->id = ast_extension_state_add_destroy_extended(
353                      exten_state_sub->context, exten_state_sub->exten,
354                      state_changed, state_changed_destroy, exten_state_sub)) < 0) {
355                 ast_log(LOG_WARNING, "Unable to subscribe endpoint '%s' to extension '%s@%s'\n",
356                         ast_sorcery_object_get_id(endpoint), exten_state_sub->exten,
357                         exten_state_sub->context);
358                 ao2_cleanup(endpoint);
359                 ao2_cleanup(exten_state_sub);
360                 return -1;
361         }
362
363         /* Go ahead and cleanup the endpoint since we don't need it anymore */
364         ao2_cleanup(endpoint);
365
366         /* bump the ref since ast_extension_state_add holds a reference */
367         ao2_ref(exten_state_sub, +1);
368
369         if (add_datastore(exten_state_sub)) {
370                 ast_log(LOG_WARNING, "Unable to add to subscription datastore.\n");
371                 ao2_cleanup(exten_state_sub);
372                 return -1;
373         }
374
375         ao2_cleanup(exten_state_sub);
376         return 0;
377 }
378
379 static void exten_state_data_destructor(void *obj)
380 {
381         struct ast_sip_exten_state_data *exten_state_data = obj;
382
383         ao2_cleanup(exten_state_data->device_state_info);
384         ast_free(exten_state_data->presence_subtype);
385         ast_free(exten_state_data->presence_message);
386         if (exten_state_data->pool) {
387                 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), exten_state_data->pool);
388         }
389 }
390
391 static struct ast_sip_exten_state_data *exten_state_data_alloc(struct ast_sip_subscription *sip_sub,
392                 struct exten_state_subscription *exten_state_sub)
393 {
394         struct ast_sip_exten_state_data *exten_state_data;
395         char *subtype = NULL;
396         char *message = NULL;
397
398         exten_state_data = ao2_alloc(sizeof(*exten_state_data), exten_state_data_destructor);
399         if (!exten_state_data) {
400                 return NULL;
401         }
402
403         exten_state_data->exten = exten_state_sub->exten;
404         if ((exten_state_data->presence_state = ast_hint_presence_state(NULL, exten_state_sub->context,
405                         exten_state_sub->exten, &subtype, &message)) == -1) {
406                 ao2_cleanup(exten_state_data);
407                 return NULL;
408         }
409         exten_state_data->presence_subtype = subtype;
410         exten_state_data->presence_message = message;
411         exten_state_data->user_agent = exten_state_sub->user_agent;
412         ast_sip_subscription_get_local_uri(sip_sub, exten_state_data->local,
413                         sizeof(exten_state_data->local));
414         ast_sip_subscription_get_remote_uri(sip_sub, exten_state_data->remote,
415                         sizeof(exten_state_data->remote));
416         exten_state_data->sub = sip_sub;
417
418         exten_state_data->exten_state = ast_extension_state_extended(
419                         NULL, exten_state_sub->context, exten_state_sub->exten,
420                         &exten_state_data->device_state_info);
421         if (exten_state_data->exten_state < 0) {
422                 ao2_cleanup(exten_state_data);
423                 return NULL;
424         }
425
426         exten_state_data->pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
427                         "exten_state", 1024, 1024);
428         if (!exten_state_data->pool) {
429                 ao2_cleanup(exten_state_data);
430                 return NULL;
431         }
432
433         return exten_state_data;
434 }
435
436 static void *get_notify_data(struct ast_sip_subscription *sub)
437 {
438         struct exten_state_subscription *exten_state_sub;
439
440         exten_state_sub = get_exten_state_sub(sub);
441         if (!exten_state_sub) {
442                 return NULL;
443         }
444
445         return exten_state_data_alloc(sub, exten_state_sub);
446 }
447
448 static void to_ami(struct ast_sip_subscription *sub,
449                    struct ast_str **buf)
450 {
451         struct exten_state_subscription *exten_state_sub =
452                 get_exten_state_sub(sub);
453
454         ast_str_append(buf, 0, "SubscriptionType: extension_state\r\n"
455                        "Extension: %s\r\nExtensionStates: %s\r\n",
456                        exten_state_sub->exten, ast_extension_state2str(
457                                exten_state_sub->last_exten_state));
458 }
459
460 static int load_module(void)
461 {
462         if (ast_sip_register_subscription_handler(&presence_handler)) {
463                 ast_log(LOG_WARNING, "Unable to register subscription handler %s\n",
464                         presence_handler.event_name);
465                 return AST_MODULE_LOAD_DECLINE;
466         }
467
468         if (ast_sip_register_subscription_handler(&dialog_handler)) {
469                 ast_log(LOG_WARNING, "Unable to register subscription handler %s\n",
470                         dialog_handler.event_name);
471                 ast_sip_unregister_subscription_handler(&presence_handler);
472                 return AST_MODULE_LOAD_DECLINE;
473         }
474
475         return AST_MODULE_LOAD_SUCCESS;
476 }
477
478 static int unload_module(void)
479 {
480         ast_sip_unregister_subscription_handler(&dialog_handler);
481         ast_sip_unregister_subscription_handler(&presence_handler);
482         return 0;
483 }
484
485 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Extension State Notifications",
486                 .support_level = AST_MODULE_SUPPORT_CORE,
487                 .load = load_module,
488                 .unload = unload_module,
489                 .load_pri = AST_MODPRI_CHANNEL_DEPEND,
490 );