res_pjsip: add "via_addr", "via_port", "call_id" to contact
[asterisk/asterisk.git] / main / stasis_endpoints.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * David M. Lee, II <dlee@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 /*! \file
20  *
21  * \brief Stasis endpoint API.
22  *
23  * \author David M. Lee, II <dlee@digium.com>
24  */
25
26 /*** MODULEINFO
27         <support_level>core</support_level>
28  ***/
29
30 #include "asterisk.h"
31
32 ASTERISK_REGISTER_FILE()
33
34 #include "asterisk/astobj2.h"
35 #include "asterisk/stasis.h"
36 #include "asterisk/stasis_endpoints.h"
37
38 /*** DOCUMENTATION
39         <managerEvent language="en_US" name="PeerStatus">
40                 <managerEventInstance class="EVENT_FLAG_SYSTEM">
41                         <synopsis>Raised when the state of a peer changes.</synopsis>
42                         <syntax>
43                                 <parameter name="ChannelType">
44                                         <para>The channel technology of the peer.</para>
45                                 </parameter>
46                                 <parameter name="Peer">
47                                         <para>The name of the peer (including channel technology).</para>
48                                 </parameter>
49                                 <parameter name="PeerStatus">
50                                         <para>New status of the peer.</para>
51                                         <enumlist>
52                                                 <enum name="Unknown"/>
53                                                 <enum name="Registered"/>
54                                                 <enum name="Unregistered"/>
55                                                 <enum name="Rejected"/>
56                                                 <enum name="Lagged"/>
57                                         </enumlist>
58                                 </parameter>
59                                 <parameter name="Cause">
60                                         <para>The reason the status has changed.</para>
61                                 </parameter>
62                                 <parameter name="Address">
63                                         <para>New address of the peer.</para>
64                                 </parameter>
65                                 <parameter name="Port">
66                                         <para>New port for the peer.</para>
67                                 </parameter>
68                                 <parameter name="Time">
69                                         <para>Time it takes to reach the peer and receive a response.</para>
70                                 </parameter>
71                         </syntax>
72                 </managerEventInstance>
73         </managerEvent>
74         <managerEvent language="en_US" name="ContactStatus">
75                 <managerEventInstance class="EVENT_FLAG_SYSTEM">
76                         <synopsis>Raised when the state of a contact changes.</synopsis>
77                         <syntax>
78                                 <parameter name="URI">
79                                         <para>This contact's URI.</para>
80                                 </parameter>
81                                 <parameter name="ContactStatus">
82                                         <para>New status of the contact.</para>
83                                         <enumlist>
84                                                 <enum name="Unknown"/>
85                                                 <enum name="Unreachable"/>
86                                                 <enum name="Reachable"/>
87                                                 <enum name="Created"/>
88                                                 <enum name="Removed"/>
89                                                 <enum name="Updated"/>
90                                         </enumlist>
91                                 </parameter>
92                                 <parameter name="AOR">
93                                         <para>The name of the associated aor.</para>
94                                 </parameter>
95                                 <parameter name="EndpointName">
96                                         <para>The name of the associated endpoint.</para>
97                                 </parameter>
98                                 <parameter name="RoundtripUsec">
99                                         <para>The RTT measured during the last qualify.</para>
100                                 </parameter>
101                                 <parameter name="UserAgent">
102                                         <para>Content of the User-Agent header in REGISTER request</para>
103                                 </parameter>
104                                 <parameter name="RegExpire">
105                                         <para>Absolute time that this contact is no longer valid after</para>
106                                 </parameter>
107                                 <parameter name="ViaAddress">
108                                         <para>IP address:port of the last Via header in REGISTER request</para>
109                                 </parameter>
110                                 <parameter name="CallID">
111                                         <para>Content of the Call-ID header in REGISTER request</para>
112                                 </parameter>
113                         </syntax>
114                 </managerEventInstance>
115         </managerEvent>
116 ***/
117
118 static struct stasis_cp_all *endpoint_cache_all;
119
120 struct stasis_cp_all *ast_endpoint_cache_all(void)
121 {
122         return endpoint_cache_all;
123 }
124
125 struct stasis_cache *ast_endpoint_cache(void)
126 {
127         return stasis_cp_all_cache(endpoint_cache_all);
128 }
129
130 struct stasis_topic *ast_endpoint_topic_all(void)
131 {
132         return stasis_cp_all_topic(endpoint_cache_all);
133 }
134
135 struct stasis_topic *ast_endpoint_topic_all_cached(void)
136 {
137         return stasis_cp_all_topic_cached(endpoint_cache_all);
138 }
139
140 STASIS_MESSAGE_TYPE_DEFN(ast_endpoint_snapshot_type);
141
142 static struct ast_manager_event_blob *peerstatus_to_ami(struct stasis_message *msg)
143 {
144         struct ast_endpoint_blob *obj = stasis_message_data(msg);
145         RAII_VAR(struct ast_str *, peerstatus_event_string, ast_str_create(64), ast_free);
146         const char *value;
147
148         /* peer_status is the only *required* thing */
149         if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "peer_status")))) {
150                 return NULL;
151         }
152         ast_str_append(&peerstatus_event_string, 0, "PeerStatus: %s\r\n", value);
153
154         if ((value = ast_json_string_get(ast_json_object_get(obj->blob, "cause")))) {
155                 ast_str_append(&peerstatus_event_string, 0, "Cause: %s\r\n", value);
156         }
157         if ((value = ast_json_string_get(ast_json_object_get(obj->blob, "address")))) {
158                 ast_str_append(&peerstatus_event_string, 0, "Address: %s\r\n", value);
159         }
160         if ((value = ast_json_string_get(ast_json_object_get(obj->blob, "port")))) {
161                 ast_str_append(&peerstatus_event_string, 0, "Port: %s\r\n", value);
162         }
163         if ((value = ast_json_string_get(ast_json_object_get(obj->blob, "time")))) {
164                 ast_str_append(&peerstatus_event_string, 0, "Time: %s\r\n", value);
165         }
166
167         return ast_manager_event_blob_create(EVENT_FLAG_SYSTEM, "PeerStatus",
168                 "ChannelType: %s\r\n"
169                 "Peer: %s/%s\r\n"
170                 "%s",
171                 obj->snapshot->tech,
172                 obj->snapshot->tech,
173                 obj->snapshot->resource,
174                 ast_str_buffer(peerstatus_event_string));
175 }
176
177 static struct ast_json *peerstatus_to_json(struct stasis_message *msg, const struct stasis_message_sanitizer *sanitize)
178 {
179         struct ast_endpoint_blob *obj = stasis_message_data(msg);
180         struct ast_json *json_endpoint;
181         struct ast_json *json_peer;
182         struct ast_json *json_final;
183         const struct timeval *tv = stasis_message_timestamp(msg);
184
185         json_endpoint = ast_endpoint_snapshot_to_json(obj->snapshot, NULL);
186         if (!json_endpoint) {
187                 return NULL;
188         }
189
190         json_peer = ast_json_object_create();
191         if (!json_peer) {
192                 ast_json_unref(json_endpoint);
193                 return NULL;
194         }
195
196         /* Copy all fields from the blob */
197         ast_json_object_update(json_peer, obj->blob);
198
199         json_final = ast_json_pack("{s: s, s: o, s: o, s: o }",
200                 "type", "PeerStatusChange",
201                 "timestamp", ast_json_timeval(*tv, NULL),
202                 "endpoint", json_endpoint,
203                 "peer", json_peer);
204         if (!json_final) {
205                 ast_json_unref(json_endpoint);
206                 ast_json_unref(json_peer);
207         }
208
209         return json_final;
210 }
211
212 STASIS_MESSAGE_TYPE_DEFN(ast_endpoint_state_type,
213         .to_ami = peerstatus_to_ami,
214         .to_json = peerstatus_to_json,
215 );
216
217 static struct ast_manager_event_blob *contactstatus_to_ami(struct stasis_message *msg)
218 {
219         struct ast_endpoint_blob *obj = stasis_message_data(msg);
220         RAII_VAR(struct ast_str *, contactstatus_event_string, ast_str_create(64), ast_free);
221         const char *value;
222
223         if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "uri")))) {
224                 return NULL;
225         }
226         ast_str_append(&contactstatus_event_string, 0, "URI: %s\r\n", value);
227
228         if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "contact_status")))) {
229                 return NULL;
230         }
231         ast_str_append(&contactstatus_event_string, 0, "ContactStatus: %s\r\n", value);
232
233         if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "aor")))) {
234                 return NULL;
235         }
236         ast_str_append(&contactstatus_event_string, 0, "AOR: %s\r\n", value);
237
238         if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "endpoint_name")))) {
239                 return NULL;
240         }
241         ast_str_append(&contactstatus_event_string, 0, "EndpointName: %s\r\n", value);
242
243         if ((value = ast_json_string_get(ast_json_object_get(obj->blob, "roundtrip_usec")))) {
244                 ast_str_append(&contactstatus_event_string, 0, "RoundtripUsec: %s\r\n", value);
245         }
246
247         return ast_manager_event_blob_create(EVENT_FLAG_SYSTEM, "ContactStatus",
248                 "%s", ast_str_buffer(contactstatus_event_string));
249 }
250
251 static struct ast_json *contactstatus_to_json(struct stasis_message *msg, const struct stasis_message_sanitizer *sanitize)
252 {
253         struct ast_endpoint_blob *obj = stasis_message_data(msg);
254         struct ast_json *json_endpoint;
255         struct ast_json *json_final;
256         const struct timeval *tv = stasis_message_timestamp(msg);
257
258         json_endpoint = ast_endpoint_snapshot_to_json(obj->snapshot, NULL);
259         if (!json_endpoint) {
260                 return NULL;
261         }
262
263         json_final = ast_json_pack("{s: s, s: o, s: o, s: { s: s, s: s, s: s, s: s } } ",
264                 "type", "ContactStatusChange",
265                 "timestamp", ast_json_timeval(*tv, NULL),
266                 "endpoint", json_endpoint,
267                 "contact_info",
268                 "uri", ast_json_string_get(ast_json_object_get(obj->blob, "uri")),
269                 "contact_status", ast_json_string_get(ast_json_object_get(obj->blob, "contact_status")),
270                 "aor", ast_json_string_get(ast_json_object_get(obj->blob, "aor")),
271                 "roundtrip_usec", ast_json_string_get(ast_json_object_get(obj->blob, "roundtrip_usec")));
272         if (!json_final) {
273                 ast_json_unref(json_endpoint);
274         }
275
276         return json_final;
277 }
278
279 STASIS_MESSAGE_TYPE_DEFN(ast_endpoint_contact_state_type,
280         .to_ami = contactstatus_to_ami,
281         .to_json = contactstatus_to_json
282 );
283
284 static void endpoint_blob_dtor(void *obj)
285 {
286         struct ast_endpoint_blob *event = obj;
287         ao2_cleanup(event->snapshot);
288         ast_json_unref(event->blob);
289 }
290
291 struct stasis_message *ast_endpoint_blob_create(struct ast_endpoint *endpoint,
292         struct stasis_message_type *type, struct ast_json *blob)
293 {
294         RAII_VAR(struct ast_endpoint_blob *, obj, NULL, ao2_cleanup);
295         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
296
297         if (!type) {
298                 return NULL;
299         }
300         if (!blob) {
301                 blob = ast_json_null();
302         }
303
304         if (!(obj = ao2_alloc(sizeof(*obj), endpoint_blob_dtor))) {
305                 return NULL;
306         }
307
308         if (endpoint) {
309                 if (!(obj->snapshot = ast_endpoint_snapshot_create(endpoint))) {
310                         return NULL;
311                 }
312         }
313
314         obj->blob = ast_json_ref(blob);
315
316         if (!(msg = stasis_message_create(type, obj))) {
317                 return NULL;
318         }
319
320         ao2_ref(msg, +1);
321         return msg;
322 }
323
324 void ast_endpoint_blob_publish(struct ast_endpoint *endpoint, struct stasis_message_type *type,
325         struct ast_json *blob)
326 {
327         RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
328         if (blob) {
329                 message = ast_endpoint_blob_create(endpoint, type, blob);
330         }
331         if (message) {
332                 stasis_publish(ast_endpoint_topic(endpoint), message);
333         }
334 }
335
336 struct ast_endpoint_snapshot *ast_endpoint_latest_snapshot(const char *tech,
337         const char *name)
338 {
339         RAII_VAR(char *, id, NULL, ast_free);
340         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
341         struct ast_endpoint_snapshot *snapshot;
342
343         if (ast_strlen_zero(name)) {
344                 ast_asprintf(&id, "%s", tech);
345         } else {
346                 ast_asprintf(&id, "%s/%s", tech, name);
347         }
348         if (!id) {
349                 return NULL;
350         }
351         ast_tech_to_upper(id);
352
353         msg = stasis_cache_get(ast_endpoint_cache(),
354                 ast_endpoint_snapshot_type(), id);
355         if (!msg) {
356                 return NULL;
357         }
358
359         snapshot = stasis_message_data(msg);
360         ast_assert(snapshot != NULL);
361
362         ao2_ref(snapshot, +1);
363         return snapshot;
364 }
365
366 /*!
367  * \brief Callback extract a unique identity from a snapshot message.
368  *
369  * This identity is unique to the underlying object of the snapshot, such as the
370  * UniqueId field of a channel.
371  *
372  * \param message Message to extract id from.
373  * \return String representing the snapshot's id.
374  * \return \c NULL if the message_type of the message isn't a handled snapshot.
375  * \since 12
376  */
377 static const char *endpoint_snapshot_get_id(struct stasis_message *message)
378 {
379         struct ast_endpoint_snapshot *snapshot;
380
381         if (ast_endpoint_snapshot_type() != stasis_message_type(message)) {
382                 return NULL;
383         }
384
385         snapshot = stasis_message_data(message);
386
387         return snapshot->id;
388 }
389
390
391 struct ast_json *ast_endpoint_snapshot_to_json(
392         const struct ast_endpoint_snapshot *snapshot,
393         const struct stasis_message_sanitizer *sanitize)
394 {
395         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
396         struct ast_json *channel_array;
397         int i;
398
399         json = ast_json_pack("{s: s, s: s, s: s, s: []}",
400                 "technology", snapshot->tech,
401                 "resource", snapshot->resource,
402                 "state", ast_endpoint_state_to_string(snapshot->state),
403                 "channel_ids");
404
405         if (json == NULL) {
406                 return NULL;
407         }
408
409         if (snapshot->max_channels != -1) {
410                 int res = ast_json_object_set(json, "max_channels",
411                         ast_json_integer_create(snapshot->max_channels));
412                 if (res != 0) {
413                         return NULL;
414                 }
415         }
416
417         channel_array = ast_json_object_get(json, "channel_ids");
418         ast_assert(channel_array != NULL);
419         for (i = 0; i < snapshot->num_channels; ++i) {
420                 int res;
421
422                 if (sanitize && sanitize->channel_id
423                         && sanitize->channel_id(snapshot->channel_ids[i])) {
424                         continue;
425                 }
426
427                 res = ast_json_array_append(channel_array,
428                         ast_json_string_create(snapshot->channel_ids[i]));
429                 if (res != 0) {
430                         return NULL;
431                 }
432         }
433
434         return ast_json_ref(json);
435 }
436
437 static void endpoints_stasis_cleanup(void)
438 {
439         STASIS_MESSAGE_TYPE_CLEANUP(ast_endpoint_snapshot_type);
440         STASIS_MESSAGE_TYPE_CLEANUP(ast_endpoint_state_type);
441         STASIS_MESSAGE_TYPE_CLEANUP(ast_endpoint_contact_state_type);
442
443         ao2_cleanup(endpoint_cache_all);
444         endpoint_cache_all = NULL;
445 }
446
447 int ast_endpoint_stasis_init(void)
448 {
449         int res = 0;
450         ast_register_cleanup(endpoints_stasis_cleanup);
451
452         endpoint_cache_all = stasis_cp_all_create("endpoint_topic_all",
453                 endpoint_snapshot_get_id);
454         if (!endpoint_cache_all) {
455                 return -1;
456         }
457
458         res |= STASIS_MESSAGE_TYPE_INIT(ast_endpoint_snapshot_type);
459         res |= STASIS_MESSAGE_TYPE_INIT(ast_endpoint_state_type);
460         res |= STASIS_MESSAGE_TYPE_INIT(ast_endpoint_contact_state_type);
461
462         return res;
463 }