fc72267619dc1de87b6362e3c16c8c74348cae4e
[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                 ast_sip_add_header(tdata, iter->name, iter->value);
91         };
92         tdata->mod_data[global_header_mod.id] = &handled_id;
93 }
94
95 static pj_status_t add_request_headers(pjsip_tx_data *tdata)
96 {
97         add_headers_to_message(&request_headers, tdata);
98
99         return PJ_SUCCESS;
100 }
101
102 static pj_status_t add_response_headers(pjsip_tx_data *tdata)
103 {
104         add_headers_to_message(&response_headers, tdata);
105
106         return PJ_SUCCESS;
107 }
108
109 static void remove_header(struct header_list *headers, const char *to_remove)
110 {
111         struct header *iter;
112         AST_LIST_TRAVERSE_SAFE_BEGIN(headers, iter, next) {
113                 if (!strcasecmp(iter->name, to_remove)) {
114                         AST_LIST_REMOVE_CURRENT(next);
115                         break;
116                 }
117         }
118         AST_LIST_TRAVERSE_SAFE_END;
119 }
120
121 static int add_header(struct header_list *headers, const char *name, const char *value, int replace)
122 {
123         struct header *to_add;
124
125         to_add = alloc_header(name, value);
126         if (!to_add) {
127                 return -1;
128         }
129
130         AST_RWLIST_WRLOCK(headers);
131         if (replace) { 
132                 remove_header(headers, name);
133         }
134         AST_LIST_INSERT_TAIL(headers, to_add, next);
135         AST_RWLIST_UNLOCK(headers);
136
137         return 0;
138 }
139
140 int ast_sip_add_global_request_header(const char *name, const char *value, int replace)
141 {
142         return add_header(&request_headers, name, value, replace);
143 }
144
145 int ast_sip_add_global_response_header(const char *name, const char *value, int replace)
146 {
147         return add_header(&response_headers, name, value, replace);
148 }
149
150 void ast_sip_initialize_global_headers(void)
151 {
152         AST_RWLIST_HEAD_INIT(&request_headers);
153         AST_RWLIST_HEAD_INIT(&response_headers);
154
155         internal_sip_register_service(&global_header_mod);
156 }
157
158 static void destroy_headers(struct header_list *headers)
159 {
160         struct header *iter;
161
162         while ((iter = AST_RWLIST_REMOVE_HEAD(headers, next))) {
163                 destroy_header(iter);
164         }
165         AST_RWLIST_HEAD_DESTROY(headers);
166 }
167
168 void ast_sip_destroy_global_headers(void)
169 {
170         destroy_headers(&request_headers);
171         destroy_headers(&response_headers);
172
173         internal_sip_unregister_service(&global_header_mod);
174 }