Stasis: Allow message types to be blocked
[asterisk/asterisk.git] / main / stasis_message.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 Message 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_FILE_VERSION(__FILE__, "$Revision$")
33
34 #include "asterisk/astobj2.h"
35 #include "asterisk/stasis.h"
36 #include "asterisk/utils.h"
37
38 /*! \internal */
39 struct stasis_message_type {
40         struct stasis_message_vtable *vtable;
41         char *name;
42 };
43
44 static struct stasis_message_vtable null_vtable = {};
45
46 static void message_type_dtor(void *obj)
47 {
48         struct stasis_message_type *type = obj;
49         ast_free(type->name);
50         type->name = NULL;
51 }
52
53 int stasis_message_type_create(const char *name,
54         struct stasis_message_vtable *vtable,
55         struct stasis_message_type **result)
56 {
57         struct stasis_message_type *type;
58
59         /* Check for declination */
60         if (name && stasis_message_type_declined(name)) {
61                 return STASIS_MESSAGE_TYPE_DECLINED;
62         }
63
64         type = ao2_t_alloc(sizeof(*type), message_type_dtor, name);
65         if (!type) {
66                 return STASIS_MESSAGE_TYPE_ERROR;
67         }
68         if (!vtable) {
69                 /* Null object pattern, FTW! */
70                 vtable = &null_vtable;
71         }
72
73         type->name = ast_strdup(name);
74         if (!type->name) {
75                 ao2_cleanup(type);
76                 return STASIS_MESSAGE_TYPE_ERROR;
77         }
78         type->vtable = vtable;
79         *result = type;
80
81         return STASIS_MESSAGE_TYPE_SUCCESS;
82 }
83
84 const char *stasis_message_type_name(const struct stasis_message_type *type)
85 {
86         return type->name;
87 }
88
89 /*! \internal */
90 struct stasis_message {
91         /*! Time the message was created */
92         struct timeval timestamp;
93         /*! Type of the message */
94         struct stasis_message_type *type;
95         /*! Where this message originated.  NULL if aggregate message. */
96         const struct ast_eid *eid_ptr;
97         /*! Message content */
98         void *data;
99         /*! Where this message originated. */
100         struct ast_eid eid;
101 };
102
103 static void stasis_message_dtor(void *obj)
104 {
105         struct stasis_message *message = obj;
106         ao2_cleanup(message->type);
107         ao2_cleanup(message->data);
108 }
109
110 struct stasis_message *stasis_message_create_full(struct stasis_message_type *type, void *data, const struct ast_eid *eid)
111 {
112         struct stasis_message *message;
113
114         if (type == NULL || data == NULL) {
115                 return NULL;
116         }
117
118         message = ao2_t_alloc(sizeof(*message), stasis_message_dtor, type->name);
119         if (message == NULL) {
120                 return NULL;
121         }
122
123         message->timestamp = ast_tvnow();
124         ao2_ref(type, +1);
125         message->type = type;
126         ao2_ref(data, +1);
127         message->data = data;
128         if (eid) {
129                 message->eid_ptr = &message->eid;
130                 message->eid = *eid;
131         }
132
133         return message;
134 }
135
136 struct stasis_message *stasis_message_create(struct stasis_message_type *type, void *data)
137 {
138         return stasis_message_create_full(type, data, &ast_eid_default);
139 }
140
141 const struct ast_eid *stasis_message_eid(const struct stasis_message *msg)
142 {
143         if (msg == NULL) {
144                 return NULL;
145         }
146         return msg->eid_ptr;
147 }
148
149 struct stasis_message_type *stasis_message_type(const struct stasis_message *msg)
150 {
151         if (msg == NULL) {
152                 return NULL;
153         }
154         return msg->type;
155 }
156
157 void *stasis_message_data(const struct stasis_message *msg)
158 {
159         if (msg == NULL) {
160                 return NULL;
161         }
162         return msg->data;
163 }
164
165 const struct timeval *stasis_message_timestamp(const struct stasis_message *msg)
166 {
167         if (msg == NULL) {
168                 return NULL;
169         }
170         return &msg->timestamp;
171 }
172
173 #define INVOKE_VIRTUAL(fn, ...)                         \
174         ({                                              \
175                 if (msg == NULL) {                      \
176                         return NULL;                    \
177                 }                                       \
178                 ast_assert(msg->type != NULL);          \
179                 ast_assert(msg->type->vtable != NULL);  \
180                 if (msg->type->vtable->fn == NULL) {    \
181                         return NULL;                    \
182                 }                                       \
183                 msg->type->vtable->fn(__VA_ARGS__);     \
184         })
185
186 struct ast_manager_event_blob *stasis_message_to_ami(struct stasis_message *msg)
187 {
188         return INVOKE_VIRTUAL(to_ami, msg);
189 }
190
191 struct ast_json *stasis_message_to_json(
192         struct stasis_message *msg,
193         struct stasis_message_sanitizer *sanitize)
194 {
195         return INVOKE_VIRTUAL(to_json, msg, sanitize);
196 }
197
198 struct ast_event *stasis_message_to_event(struct stasis_message *msg)
199 {
200         return INVOKE_VIRTUAL(to_event, msg);
201 }