res_pjsip: Fix warning by deferring implicit type cast.
[asterisk/asterisk.git] / res / res_pjsip / pjsip_global_headers.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Mark Michelson <mmichelson@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 #include "asterisk.h"
20
21 #include <pjsip.h>
22 #include <pjlib.h>
23
24 #include "asterisk/res_pjsip.h"
25 #include "asterisk/linkedlists.h"
26 #include "include/res_pjsip_private.h"
27
28 static pj_status_t add_request_headers(pjsip_tx_data *tdata);
29 static pj_status_t add_response_headers(pjsip_tx_data *tdata);
30
31 /*!
32  * \brief Indicator we've already handled a specific request/response
33  *
34  * PJSIP tends to reuse requests and responses. If we already have added
35  * headers to a request or response, we mark the message with this value
36  * so that we know not to re-add the headers again.
37  */
38 static unsigned int handled_id = 0xCA115785;
39
40 static pjsip_module global_header_mod = {
41         .name = {"Global headers", 13},
42         .priority = PJSIP_MOD_PRIORITY_APPLICATION,
43         .on_tx_request = add_request_headers,
44         .on_tx_response = add_response_headers,
45 };
46
47 struct header {
48         AST_DECLARE_STRING_FIELDS(
49                 AST_STRING_FIELD(name);
50                 AST_STRING_FIELD(value);
51         );
52         AST_LIST_ENTRY(header) next;
53 };
54
55 static struct header *alloc_header(const char *name, const char *value)
56 {
57         struct header *alloc;
58         
59         alloc = ast_calloc_with_stringfields(1, struct header, 32);
60
61         if (!alloc) {
62                 return NULL;
63         }
64
65         ast_string_field_set(alloc, name, name);
66         ast_string_field_set(alloc, value, value);
67
68         return alloc;
69 }
70
71 static void destroy_header(struct header *to_destroy)
72 {
73         ast_string_field_free_memory(to_destroy);
74         ast_free(to_destroy);
75 }
76
77 AST_RWLIST_HEAD(header_list, header);
78
79 static struct header_list request_headers;
80 static struct header_list response_headers;
81
82 static void add_headers_to_message(struct header_list *headers, pjsip_tx_data *tdata)
83 {
84         struct header *iter;
85         SCOPED_LOCK(lock, headers, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK);
86         if (tdata->mod_data[global_header_mod.id] == &handled_id) {
87                 return;
88         }
89         AST_LIST_TRAVERSE(headers, iter, next) {
90                 pj_str_t name;
91                 pjsip_generic_string_hdr *hdr;
92
93                 hdr = pjsip_msg_find_hdr_by_name(tdata->msg, pj_cstr(&name, iter->name), NULL);
94                 if (hdr) {
95                         continue;
96                 }
97
98                 ast_sip_add_header(tdata, iter->name, iter->value);
99         };
100         tdata->mod_data[global_header_mod.id] = &handled_id;
101 }
102
103 static pj_status_t add_request_headers(pjsip_tx_data *tdata)
104 {
105         add_headers_to_message(&request_headers, tdata);
106
107         return PJ_SUCCESS;
108 }
109
110 static pj_status_t add_response_headers(pjsip_tx_data *tdata)
111 {
112         add_headers_to_message(&response_headers, tdata);
113
114         return PJ_SUCCESS;
115 }
116
117 static void remove_header(struct header_list *headers, const char *to_remove)
118 {
119         struct header *iter;
120         AST_LIST_TRAVERSE_SAFE_BEGIN(headers, iter, next) {
121                 if (!strcasecmp(iter->name, to_remove)) {
122                         AST_LIST_REMOVE_CURRENT(next);
123                         destroy_header(iter);
124                         break;
125                 }
126         }
127         AST_LIST_TRAVERSE_SAFE_END;
128 }
129
130 static int add_header(struct header_list *headers, const char *name, const char *value, int replace)
131 {
132         struct header *to_add = NULL;
133
134         if (!ast_strlen_zero(value)) {
135                 to_add = alloc_header(name, value);
136                 if (!to_add) {
137                         return -1;
138                 }
139         }
140
141         AST_RWLIST_WRLOCK(headers);
142         if (replace) { 
143                 remove_header(headers, name);
144         }
145         if (to_add) {
146                 AST_LIST_INSERT_TAIL(headers, to_add, next);
147         }
148         AST_RWLIST_UNLOCK(headers);
149
150         return 0;
151 }
152
153 int ast_sip_add_global_request_header(const char *name, const char *value, int replace)
154 {
155         return add_header(&request_headers, name, value, replace);
156 }
157
158 int ast_sip_add_global_response_header(const char *name, const char *value, int replace)
159 {
160         return add_header(&response_headers, name, value, replace);
161 }
162
163 void ast_sip_initialize_global_headers(void)
164 {
165         AST_RWLIST_HEAD_INIT(&request_headers);
166         AST_RWLIST_HEAD_INIT(&response_headers);
167
168         internal_sip_register_service(&global_header_mod);
169 }
170
171 static void destroy_headers(struct header_list *headers)
172 {
173         struct header *iter;
174
175         while ((iter = AST_RWLIST_REMOVE_HEAD(headers, next))) {
176                 destroy_header(iter);
177         }
178         AST_RWLIST_HEAD_DESTROY(headers);
179 }
180
181 void ast_sip_destroy_global_headers(void)
182 {
183         destroy_headers(&request_headers);
184         destroy_headers(&response_headers);
185
186         internal_sip_unregister_service(&global_header_mod);
187 }