Asterisk - The Open Source Telephony Project  GIT-master-8beac82
res_pjsip_header_funcs.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Fairview 5 Engineering, LLC
5  *
6  * George Joseph <george.joseph@fairview5.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_session</depend>
23  <support_level>core</support_level>
24  ***/
25 
26 #include "asterisk.h"
27 
28 #include <pjsip.h>
29 #include <pjsip_ua.h>
30 
31 #include "asterisk/res_pjsip.h"
33 #include "asterisk/channel.h"
34 #include "asterisk/pbx.h"
35 #include "asterisk/app.h"
36 #include "asterisk/module.h"
37 #include "asterisk/utils.h"
38 
39 /*** DOCUMENTATION
40  <function name="PJSIP_HEADER" language="en_US">
41  <synopsis>
42  Gets headers from an inbound PJSIP channel. Adds, updates or removes the
43  specified SIP header from an outbound PJSIP channel.
44  </synopsis>
45  <syntax>
46  <parameter name="action" required="true">
47  <enumlist>
48  <enum name="read"><para>Returns instance <replaceable>number</replaceable>
49  of header <replaceable>name</replaceable>. A <literal>*</literal>
50  may be appended to <replaceable>name</replaceable> to iterate over all
51  headers <emphasis>beginning with</emphasis> <replaceable>name</replaceable>.
52  </para></enum>
53 
54  <enum name="add"><para>Adds a new header <replaceable>name</replaceable>
55  to this session.</para></enum>
56 
57  <enum name="update"><para>Updates instance <replaceable>number</replaceable>
58  of header <replaceable>name</replaceable> to a new value.
59  The header must already exist.</para></enum>
60 
61  <enum name="remove"><para>Removes all instances of previously added headers
62  whose names match <replaceable>name</replaceable>. A <literal>*</literal>
63  may be appended to <replaceable>name</replaceable> to remove all headers
64  <emphasis>beginning with</emphasis> <replaceable>name</replaceable>.
65  <replaceable>name</replaceable> may be set to a single <literal>*</literal>
66  to clear <emphasis>all</emphasis> previously added headers. In all cases,
67  the number of headers actually removed is returned.</para></enum>
68  </enumlist>
69  </parameter>
70 
71  <parameter name="name" required="true"><para>The name of the header.</para></parameter>
72 
73  <parameter name="number" required="false">
74  <para>If there's more than 1 header with the same name, this specifies which header
75  to read or update. If not specified, defaults to <literal>1</literal> meaning
76  the first matching header. Not valid for <literal>add</literal> or
77  <literal>remove</literal>.</para>
78  </parameter>
79 
80  </syntax>
81  <description>
82  <para>PJSIP_HEADER allows you to read specific SIP headers from the inbound
83  PJSIP channel as well as write(add, update, remove) headers on the outbound
84  channel. One exception is that you can read headers that you have already
85  added on the outbound channel.</para>
86  <para>Examples:</para>
87  <para>;</para>
88  <para>; Set 'somevar' to the value of the 'From' header.</para>
89  <para>exten => 1,1,Set(somevar=${PJSIP_HEADER(read,From)})</para>
90  <para>;</para>
91  <para>; Set 'via2' to the value of the 2nd 'Via' header.</para>
92  <para>exten => 1,1,Set(via2=${PJSIP_HEADER(read,Via,2)})</para>
93  <para>;</para>
94  <para>; Set 'xhdr' to the value of the 1sx X-header.</para>
95  <para>exten => 1,1,Set(xhdr=${PJSIP_HEADER(read,X-*,1)})</para>
96  <para>;</para>
97  <para>; Add an 'X-Myheader' header with the value of 'myvalue'.</para>
98  <para>exten => 1,1,Set(PJSIP_HEADER(add,X-MyHeader)=myvalue)</para>
99  <para>;</para>
100  <para>; Add an 'X-Myheader' header with an empty value.</para>
101  <para>exten => 1,1,Set(PJSIP_HEADER(add,X-MyHeader)=)</para>
102  <para>;</para>
103  <para>; Update the value of the header named 'X-Myheader' to 'newvalue'.</para>
104  <para>; 'X-Myheader' must already exist or the call will fail.</para>
105  <para>exten => 1,1,Set(PJSIP_HEADER(update,X-MyHeader)=newvalue)</para>
106  <para>;</para>
107  <para>; Remove all headers whose names exactly match 'X-MyHeader'.</para>
108  <para>exten => 1,1,Set(PJSIP_HEADER(remove,X-MyHeader)=)</para>
109  <para>;</para>
110  <para>; Remove all headers that begin with 'X-My'.</para>
111  <para>exten => 1,1,Set(PJSIP_HEADER(remove,X-My*)=)</para>
112  <para>;</para>
113  <para>; Remove all previously added headers.</para>
114  <para>exten => 1,1,Set(PJSIP_HEADER(remove,*)=)</para>
115  <para>;</para>
116 
117  <note><para>The <literal>remove</literal> action can be called by reading
118  <emphasis>or</emphasis> writing PJSIP_HEADER.</para>
119  <para>;</para>
120  <para>; Display the number of headers removed</para>
121  <para>exten => 1,1,Verbose( Removed ${PJSIP_HEADER(remove,X-MyHeader)} headers)</para>
122  <para>;</para>
123  <para>; Set a variable to the number of headers removed</para>
124  <para>exten => 1,1,Set(count=${PJSIP_HEADER(remove,X-MyHeader)})</para>
125  <para>;</para>
126  <para>; Just remove them ignoring any count</para>
127  <para>exten => 1,1,Set(=${PJSIP_HEADER(remove,X-MyHeader)})</para>
128  <para>exten => 1,1,Set(PJSIP_HEADER(remove,X-MyHeader)=)</para>
129  <para>;</para>
130  </note>
131 
132  <note><para>If you call PJSIP_HEADER in a normal dialplan context you'll be
133  operating on the <emphasis>caller's (incoming)</emphasis> channel which
134  may not be what you want. To operate on the <emphasis>callee's (outgoing)</emphasis>
135  channel call PJSIP_HEADER in a pre-dial handler. </para>
136  <para>Example:</para>
137  <para>;</para>
138  <para>[handler]</para>
139  <para>exten => addheader,1,Set(PJSIP_HEADER(add,X-MyHeader)=myvalue)</para>
140  <para>exten => addheader,2,Set(PJSIP_HEADER(add,X-MyHeader2)=myvalue2)</para>
141  <para>;</para>
142  <para>[somecontext]</para>
143  <para>exten => 1,1,Dial(PJSIP/${EXTEN},,b(handler^addheader^1))</para>
144  <para>;</para>
145  </note>
146  </description>
147  </function>
148  <function name="PJSIP_HEADERS" language="en_US">
149  <synopsis>
150  Gets the list of SIP header names from an INVITE message.
151  </synopsis>
152  <syntax>
153  <parameter name="prefix">
154  <para>If specified, only the headers matching the given prefix are returned.</para>
155  </parameter>
156  </syntax>
157  <description>
158  <para>Returns a comma-separated list of header names (without values) from the
159  INVITE message. Multiple headers with the same name are included in the list only once.
160  </para>
161  <para>For example, <literal>${PJSIP_HEADERS(Co)}</literal> might return
162  <literal>Contact,Content-Length,Content-Type</literal>. As a practical example,
163  you may use <literal>${PJSIP_HEADERS(X-)}</literal> to enumerate optional extended
164  headers.</para>
165  </description>
166  <see-also>
167  <ref type="function">PJSIP_HEADER</ref>
168  </see-also>
169  </function>
170 
171  ***/
172 
173 /*! \brief Linked list for accumulating headers */
175  pjsip_hdr *hdr;
177 };
179 
180 /*! \brief Datastore for saving headers */
181 static const struct ast_datastore_info header_datastore = {
182  .type = "header_datastore",
183 };
184 
185 /*! \brief Data structure used for ast_sip_push_task_wait_serializer */
186 struct header_data {
188  char *header_name;
189  const char *header_value;
190  char *buf;
192  size_t len;
193 };
194 
195 /*!
196  * \internal
197  * \brief Insert the header pointers into the linked list.
198  *
199  * For each header in the message, allocate a list entry,
200  * clone the header, then insert the entry.
201  */
202 static int insert_headers(pj_pool_t * pool, struct hdr_list *list, pjsip_msg * msg)
203 {
204  pjsip_hdr *hdr = msg->hdr.next;
205  struct hdr_list_entry *le;
206 
207  while (hdr && hdr != &msg->hdr) {
208  le = pj_pool_zalloc(pool, sizeof(struct hdr_list_entry));
209  le->hdr = pjsip_hdr_clone(pool, hdr);
210  AST_LIST_INSERT_TAIL(list, le, nextptr);
211  hdr = hdr->next;
212  }
213 
214  return 0;
215 }
216 
217 /*!
218  * \internal
219  * \brief Session supplement callback on an incoming INVITE request
220  *
221  * Retrieve the header_datastore from the session or create one if it doesn't exist.
222  * Create and initialize the list if needed.
223  * Insert the headers.
224  */
225 static int incoming_request(struct ast_sip_session *session, pjsip_rx_data * rdata)
226 {
227  pj_pool_t *pool = session->inv_session->dlg->pool;
228  RAII_VAR(struct ast_datastore *, datastore,
229  ast_sip_session_get_datastore(session, header_datastore.type), ao2_cleanup);
230 
231  if (!datastore) {
232  if (!(datastore =
233  ast_sip_session_alloc_datastore(&header_datastore, header_datastore.type))
234  ||
235  !(datastore->data = pj_pool_alloc(pool, sizeof(struct hdr_list))) ||
236  ast_sip_session_add_datastore(session, datastore)) {
237  ast_log(AST_LOG_ERROR, "Unable to create datastore for header functions.\n");
238  return 0;
239  }
240  AST_LIST_HEAD_INIT_NOLOCK((struct hdr_list *) datastore->data);
241  }
242  insert_headers(pool, (struct hdr_list *) datastore->data, rdata->msg_info.msg);
243 
244  return 0;
245 }
246 
247 /*!
248  * \internal
249  * \brief Search list for nth occurrence of specific header.
250  */
251 static pjsip_hdr *find_header(struct hdr_list *list, const char *header_name,
252  int header_number)
253 {
254  struct hdr_list_entry *le;
255  pjsip_hdr *hdr = NULL;
256  int i = 1;
257 
258  if (!list || ast_strlen_zero(header_name) || header_number < 1) {
259  return NULL;
260  }
261 
262  AST_LIST_TRAVERSE(list, le, nextptr) {
263  if (pj_stricmp2(&le->hdr->name, header_name) == 0 && i++ == header_number) {
264  hdr = le->hdr;
265  break;
266  }
267  }
268 
269  return hdr;
270 }
271 
272 /*!
273  * \internal
274  * \brief Implements PJSIP_HEADERS by searching for the requested header prefix.
275  *
276  * Retrieve the header_datastore.
277  * Search for the all matching headers.
278  * Validate the pjsip_hdr found.
279  * Parse pjsip_hdr into a name and copy to the buffer.
280  * Return the value.
281  */
282 static int read_headers(void *obj)
283 {
284  struct header_data *data = obj;
285  size_t len = strlen(data->header_name);
286  pjsip_hdr *hdr = NULL;
287  char *pj_hdr_string;
288  int pj_hdr_string_len;
289  char *p;
290  char *pos;
291  size_t plen, wlen = 0;
292  struct hdr_list_entry *le;
293  struct hdr_list *list;
294 
295  RAII_VAR(struct ast_datastore *, datastore,
296  ast_sip_session_get_datastore(data->channel->session, header_datastore.type),
297  ao2_cleanup);
298 
299  if (!datastore || !datastore->data) {
300  ast_debug(1, "There was no datastore from which to read headers.\n");
301  return -1;
302  }
303 
304  list = datastore->data;
305  pj_hdr_string = ast_alloca(data->len);
306  AST_LIST_TRAVERSE(list, le, nextptr) {
307  if (pj_strnicmp2(&le->hdr->name, data->header_name, len) == 0) {
308  /* Found matched header, append to buf */
309  hdr = le->hdr;
310 
311  pj_hdr_string_len = pjsip_hdr_print_on(hdr, pj_hdr_string, data->len - 1);
312  if (pj_hdr_string_len == -1) {
314  "Not enought buffer space in pjsip_hdr_print_on\n");
315  return -1;
316  }
317  pj_hdr_string[pj_hdr_string_len] = '\0';
318  p = strchr(pj_hdr_string, ':');
319  if (!p) {
321  "A malformed header was returned from pjsip_hdr_print_on\n");
322  continue;
323  }
324 
325  pj_hdr_string[p - pj_hdr_string] = '\0';
326  p = ast_strip(pj_hdr_string);
327  plen = strlen(p);
328  if (wlen + plen + 1 > data->len) {
330  "Buffer isn't big enough to hold header value. %zu > %zu\n", plen + 1,
331  data->len);
332  return -1;
333  }
334  pos = strstr(data->buf, p);
335  if (pos && pos[1] == ',') {
336  if (pos == data->buf) {
337  continue;
338  } else if (pos[-1] == ',') {
339  continue;
340  }
341  }
342  ast_copy_string(data->buf + wlen, p, data->len - wlen);
343  wlen += plen;
344  ast_copy_string(data->buf + wlen, ",", data->len - wlen);
345  wlen++;
346  }
347  }
348 
349  if (wlen == 0) {
350  ast_debug(1, "There was no header named %s.\n", data->header_name);
351  return -1;
352  } else {
353  data->buf[wlen-1] = '\0';
354  }
355  return 0;
356 }
357 
358 
359 /*!
360  * \internal
361  * \brief Implements PJSIP_HEADER 'read' by searching the for the requested header.
362  *
363  * Retrieve the header_datastore.
364  * Search for the nth matching header.
365  * Validate the pjsip_hdr found.
366  * Parse pjsip_hdr into a name and value.
367  * Return the value.
368  */
369 static int read_header(void *obj)
370 {
371  struct header_data *data = obj;
372  size_t len = strlen(data->header_name);
373  pjsip_hdr *hdr = NULL;
374  char *pj_hdr_string;
375  int pj_hdr_string_len;
376  char *p;
377  size_t plen;
378  struct hdr_list_entry *le;
379  struct hdr_list *list;
380  int i = 1;
381  RAII_VAR(struct ast_datastore *, datastore,
382  ast_sip_session_get_datastore(data->channel->session, header_datastore.type),
383  ao2_cleanup);
384 
385  if (!datastore || !datastore->data) {
386  ast_debug(1, "There was no datastore from which to read headers.\n");
387  return -1;
388  }
389 
390  list = datastore->data;
391  AST_LIST_TRAVERSE(list, le, nextptr) {
392  if (data->header_name[len - 1] == '*') {
393  if (pj_strnicmp2(&le->hdr->name, data->header_name, len - 1) == 0 && i++ == data->header_number) {
394  hdr = le->hdr;
395  break;
396  }
397  } else {
398  if (pj_stricmp2(&le->hdr->name, data->header_name) == 0 && i++ == data->header_number) {
399  hdr = le->hdr;
400  break;
401  }
402  }
403  }
404 
405  if (!hdr) {
406  ast_debug(1, "There was no header named %s.\n", data->header_name);
407  return -1;
408  }
409 
410  pj_hdr_string = ast_alloca(data->len);
411  pj_hdr_string_len = pjsip_hdr_print_on(hdr, pj_hdr_string, data->len - 1);
412  if (pj_hdr_string_len == -1) {
414  "Not enought buffer space in pjsip_hdr_print_on\n");
415  return -1;
416  }
417 
418  pj_hdr_string[pj_hdr_string_len] = '\0';
419 
420  p = strchr(pj_hdr_string, ':');
421  if (!p) {
423  "A malformed header was returned from pjsip_hdr_print_on.\n");
424  return -1;
425  }
426 
427  ++p;
428  p = ast_strip(p);
429  plen = strlen(p);
430  if (plen + 1 > data->len) {
432  "Buffer isn't big enough to hold header value. %zu > %zu\n", plen + 1,
433  data->len);
434  return -1;
435  }
436 
437  ast_copy_string(data->buf, p, data->len);
438 
439  return 0;
440 }
441 
442 /*!
443  * \internal
444  * \brief Implements PJSIP_HEADER 'add' by inserting the specified header into thge list.
445  *
446  * Retrieve the header_datastore from the session or create one if it doesn't exist.
447  * Create and initialize the list if needed.
448  * Create the pj_strs for name and value.
449  * Create pjsip_msg and hdr_list_entry.
450  * Add the entry to the list.
451  */
452 static int add_header(void *obj)
453 {
454  struct header_data *data = obj;
455  struct ast_sip_session *session = data->channel->session;
456  pj_pool_t *pool = session->inv_session->dlg->pool;
457  pj_str_t pj_header_name;
458  pj_str_t pj_header_value;
459  struct hdr_list_entry *le;
460  struct hdr_list *list;
461 
462  RAII_VAR(struct ast_datastore *, datastore,
463  ast_sip_session_get_datastore(session, header_datastore.type), ao2_cleanup);
464 
465  if (!datastore) {
466  if (!(datastore = ast_sip_session_alloc_datastore(&header_datastore,
467  header_datastore.type))
468  || !(datastore->data = pj_pool_alloc(pool, sizeof(struct hdr_list)))
469  || ast_sip_session_add_datastore(session, datastore)) {
470  ast_log(AST_LOG_ERROR, "Unable to create datastore for header functions.\n");
471  return -1;
472  }
473  AST_LIST_HEAD_INIT_NOLOCK((struct hdr_list *) datastore->data);
474  }
475 
476  ast_debug(1, "Adding header %s with value %s\n", data->header_name,
477  data->header_value);
478 
479  pj_cstr(&pj_header_name, data->header_name);
480  pj_cstr(&pj_header_value, data->header_value);
481  le = pj_pool_zalloc(pool, sizeof(struct hdr_list_entry));
482  le->hdr = (pjsip_hdr *) pjsip_generic_string_hdr_create(pool, &pj_header_name,
483  &pj_header_value);
484  list = datastore->data;
485 
486  AST_LIST_INSERT_TAIL(list, le, nextptr);
487 
488  return 0;
489 }
490 
491 /*!
492  * \internal
493  * \brief Implements PJSIP_HEADER 'update' by finding the specified header and updating it.
494  *
495  * Retrieve the header_datastore from the session or create one if it doesn't exist.
496  * Create and initialize the list if needed.
497  * Create the pj_strs for name and value.
498  * Create pjsip_msg and hdr_list_entry.
499  * Add the entry to the list.
500  */
501 static int update_header(void *obj)
502 {
503  struct header_data *data = obj;
504  pjsip_hdr *hdr = NULL;
505  RAII_VAR(struct ast_datastore *, datastore,
506  ast_sip_session_get_datastore(data->channel->session, header_datastore.type),
507  ao2_cleanup);
508 
509  if (!datastore || !datastore->data) {
510  ast_log(AST_LOG_ERROR, "No headers had been previously added to this session.\n");
511  return -1;
512  }
513 
514  hdr = find_header((struct hdr_list *) datastore->data, data->header_name,
515  data->header_number);
516 
517  if (!hdr) {
518  ast_log(AST_LOG_ERROR, "There was no header named %s.\n", data->header_name);
519  return -1;
520  }
521 
522  pj_strcpy2(&((pjsip_generic_string_hdr *) hdr)->hvalue, data->header_value);
523 
524  return 0;
525 }
526 
527 /*!
528  * \internal
529  * \brief Implements PJSIP_HEADER 'remove' by finding the specified header and removing it.
530  *
531  * Retrieve the header_datastore from the session. Fail if it doesn't exist.
532  * If the header_name is exactly '*', the entire list is simply destroyed.
533  * Otherwise search the list for the matching header name which may be a partial name.
534  */
535 static int remove_header(void *obj)
536 {
537  struct header_data *data = obj;
538  size_t len = strlen(data->header_name);
539  struct hdr_list *list;
540  struct hdr_list_entry *le;
541  int removed_count = 0;
542  RAII_VAR(struct ast_datastore *, datastore,
543  ast_sip_session_get_datastore(data->channel->session, header_datastore.type),
544  ao2_cleanup);
545 
546  if (!datastore || !datastore->data) {
547  ast_log(AST_LOG_ERROR, "No headers had been previously added to this session.\n");
548  return -1;
549  }
550 
551  list = datastore->data;
553  if (data->header_name[len - 1] == '*') {
554  if (pj_strnicmp2(&le->hdr->name, data->header_name, len - 1) == 0) {
556  removed_count++;
557  }
558  } else {
559  if (pj_stricmp2(&le->hdr->name, data->header_name) == 0) {
561  removed_count++;
562  }
563  }
564  }
566 
567  if (data->buf && data->len) {
568  snprintf(data->buf, data->len, "%d", removed_count);
569  }
570 
571  return 0;
572 }
573 
574 /*!
575  * \brief Read list of unique SIP headers
576  */
577 static int func_read_headers(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len)
578 {
579  struct ast_sip_channel_pvt *channel = chan ? ast_channel_tech_pvt(chan) : NULL;
580  struct header_data header_data;
582  AST_APP_ARG(header_pattern);
583  );
585 
586  if (!chan || strncmp(ast_channel_name(chan), "PJSIP/", 6)) {
587  ast_log(LOG_ERROR, "This function requires a PJSIP channel.\n");
588  return -1;
589  }
590 
591  if (ast_strlen_zero(args.header_pattern)) {
592  ast_log(AST_LOG_ERROR, "This function requires a pattern.\n");
593  return -1;
594  }
595 
596  header_data.channel = channel;
597  header_data.header_name = args.header_pattern;
598  header_data.header_value = NULL;
599  header_data.buf = buf;
600  header_data.len = len;
601 
602  return ast_sip_push_task_wait_serializer(channel->session->serializer, read_headers, &header_data);
603 
604 }
605 
606 /*!
607  * \brief Implements function 'read' callback.
608  *
609  * Valid actions are 'read' and 'remove'.
610  */
611 static int func_read_header(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len)
612 {
613  struct ast_sip_channel_pvt *channel = chan ? ast_channel_tech_pvt(chan) : NULL;
614  struct header_data header_data;
615  int number;
617  AST_APP_ARG(action);
620 
621  if (!channel || strncmp(ast_channel_name(chan), "PJSIP/", 6)) {
622  ast_log(LOG_ERROR, "This function requires a PJSIP channel.\n");
623  return -1;
624  }
625 
626  if (ast_strlen_zero(args.action)) {
627  ast_log(AST_LOG_ERROR, "This function requires an action.\n");
628  return -1;
629  }
630  if (ast_strlen_zero(args.header_name)) {
631  ast_log(AST_LOG_ERROR, "This function requires a header name.\n");
632  return -1;
633  }
634  if (!args.header_number) {
635  number = 1;
636  } else {
637  sscanf(args.header_number, "%30d", &number);
638  if (number < 1) {
639  number = 1;
640  }
641  }
642 
643  header_data.channel = channel;
644  header_data.header_name = args.header_name;
645  header_data.header_number = number;
646  header_data.header_value = NULL;
647  header_data.buf = buf;
648  header_data.len = len;
649 
650  if (!strcasecmp(args.action, "read")) {
651  return ast_sip_push_task_wait_serializer(channel->session->serializer, read_header, &header_data);
652  } else if (!strcasecmp(args.action, "remove")) {
654  remove_header, &header_data);
655  } else {
657  "Unknown action '%s' is not valid, must be 'read' or 'remove'.\n",
658  args.action);
659  return -1;
660  }
661 }
662 
663 /*!
664  * \brief Implements function 'write' callback.
665  *
666  * Valid actions are 'add', 'update' and 'remove'.
667  */
668 static int func_write_header(struct ast_channel *chan, const char *cmd, char *data,
669  const char *value)
670 {
671  struct ast_sip_channel_pvt *channel = chan ? ast_channel_tech_pvt(chan) : NULL;
672  struct header_data header_data;
673  int header_number;
675  AST_APP_ARG(action);
676  AST_APP_ARG(header_name); AST_APP_ARG(header_number););
678 
679  if (!channel || strncmp(ast_channel_name(chan), "PJSIP/", 6)) {
680  ast_log(LOG_ERROR, "This function requires a PJSIP channel.\n");
681  return -1;
682  }
683 
684  if (ast_strlen_zero(args.action)) {
685  ast_log(AST_LOG_ERROR, "This function requires an action.\n");
686  return -1;
687  }
688  if (ast_strlen_zero(args.header_name)) {
689  ast_log(AST_LOG_ERROR, "This function requires a header name.\n");
690  return -1;
691  }
692  if (!args.header_number) {
693  header_number = 1;
694  } else {
695  sscanf(args.header_number, "%30d", &header_number);
696  if (header_number < 1) {
697  header_number = 1;
698  }
699  }
700 
701  header_data.channel = channel;
702  header_data.header_name = args.header_name;
703  header_data.header_number = header_number;
704  header_data.header_value = value;
705  header_data.buf = NULL;
706  header_data.len = 0;
707 
708  if (!strcasecmp(args.action, "add")) {
710  add_header, &header_data);
711  } else if (!strcasecmp(args.action, "update")) {
713  update_header, &header_data);
714  } else if (!strcasecmp(args.action, "remove")) {
716  remove_header, &header_data);
717  } else {
719  "Unknown action '%s' is not valid, must be 'add', 'update', or 'remove'.\n",
720  args.action);
721  return -1;
722  }
723 }
724 
726  .name = "PJSIP_HEADER",
727  .read = func_read_header,
728  .write = func_write_header,
729 };
730 
732  .name = "PJSIP_HEADERS",
733  .read = func_read_headers
734 };
735 
736 /*!
737  * \internal
738  * \brief Session supplement callback for outgoing INVITE requests
739  *
740  * Retrieve the header_datastore from the session.
741  * Add each header in the list to the outgoing message.
742  *
743  * These pjsip_hdr structures will have been created by add_header.
744  * Because outgoing_request may be called more than once with the same header
745  * list (as in the case of an authentication exchange), each pjsip_hdr structure
746  * MUST be newly cloned for each outgoing message.
747  */
748 static void outgoing_request(struct ast_sip_session *session, pjsip_tx_data * tdata)
749 {
750  pj_pool_t *pool = session->inv_session->dlg->pool;
751  struct hdr_list *list;
752  struct hdr_list_entry *le;
753  RAII_VAR(struct ast_datastore *, datastore,
754  ast_sip_session_get_datastore(session, header_datastore.type), ao2_cleanup);
755 
756  if (!datastore || !datastore->data ||
757  (session->inv_session->state >= PJSIP_INV_STATE_CONFIRMED)) {
758  return;
759  }
760 
761  list = datastore->data;
762  AST_LIST_TRAVERSE(list, le, nextptr) {
763  pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) pjsip_hdr_clone(pool, le->hdr));
764  }
765  ast_sip_session_remove_datastore(session, datastore->uid);
766 }
767 
769  .method = "INVITE",
770  .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL - 1000,
771  .incoming_request = incoming_request,
772  .outgoing_request = outgoing_request,
773 };
774 
775 static int load_module(void)
776 {
777  ast_sip_session_register_supplement(&header_funcs_supplement);
778  ast_custom_function_register(&pjsip_header_function);
779  ast_custom_function_register(&pjsip_headers_function);
780 
782 }
783 
784 static int unload_module(void)
785 {
786  ast_custom_function_unregister(&pjsip_header_function);
787  ast_custom_function_unregister(&pjsip_headers_function);
788  ast_sip_session_unregister_supplement(&header_funcs_supplement);
789  return 0;
790 }
791 
792 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Header Functions",
793  .support_level = AST_MODULE_SUPPORT_CORE,
794  .load = load_module,
795  .unload = unload_module,
796  .load_pri = AST_MODPRI_APP_DEPEND,
797  .requires = "res_pjsip,res_pjsip_session",
798 );
const char * name
Definition: pbx.h:119
const char * type
Definition: datastore.h:32
static int unload_module(void)
static int func_read_headers(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len)
Read list of unique SIP headers.
Main Channel structure associated with a channel.
static int incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata)
Asterisk main include file. File version handling, generic pbx functions.
void * ast_channel_tech_pvt(const struct ast_channel *chan)
struct ast_sip_channel_pvt * channel
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
#define AST_LOG_WARNING
Definition: logger.h:279
static int insert_headers(pj_pool_t *pool, struct hdr_list *list, pjsip_msg *msg)
Structure for a data store type.
Definition: datastore.h:31
A structure which contains a channel implementation and session.
struct ast_sip_session * session
Pointer to session.
Data structure used for ast_sip_push_task_wait_serializer.
static int func_write_header(struct ast_channel *chan, const char *cmd, char *data, const char *value)
Implements function &#39;write&#39; callback.
Structure for a data store object.
Definition: datastore.h:68
void ast_sip_session_unregister_supplement(struct ast_sip_session_supplement *supplement)
Unregister a an supplement to SIP session processing.
Definition: pjsip_session.c:63
static struct ast_custom_function pjsip_headers_function
const char * args
#define NULL
Definition: resample.c:96
int value
Definition: syslog.c:37
struct pjsip_inv_session * inv_session
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:614
A structure describing a SIP session.
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
static const struct ast_datastore_info header_datastore
Datastore for saving headers.
Utility functions.
Number structure.
Definition: app_followme.c:154
void ast_sip_session_remove_datastore(struct ast_sip_session *session, const char *name)
Remove a session datastore from the session.
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#define AST_LOG_ERROR
Definition: logger.h:290
General Asterisk PBX channel definitions.
static int read_header(void *obj)
struct ast_datastore * ast_sip_session_alloc_datastore(const struct ast_datastore_info *info, const char *uid)
Alternative for ast_datastore_alloc()
struct ast_datastore * ast_sip_session_get_datastore(struct ast_sip_session *session, const char *name)
Retrieve a session datastore.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:911
static struct ast_mansession session
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
int ast_sip_push_task_wait_serializer(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Push a task to the serializer and wait for it to complete.
Definition: res_pjsip.c:5255
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
static int remove_header(void *obj)
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:219
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:556
struct hdr_list_entry::@465 nextptr
static int read_headers(void *obj)
AST_LIST_HEAD_NOLOCK(contactliststruct, contact)
Core PBX routines and definitions.
struct ast_taskprocessor * serializer
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:290
static void outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
#define LOG_ERROR
Definition: logger.h:285
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
static pjsip_hdr * find_header(struct hdr_list *list, const char *header_name, int header_number)
static struct ast_custom_function pjsip_header_function
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
A supplement to SIP message processing.
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:680
Linked list for accumulating headers.
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
pjsip_hdr * hdr
const char * ast_channel_name(const struct ast_channel *chan)
static int update_header(void *obj)
static int func_read_header(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len)
Implements function &#39;read&#39; callback.
static struct ast_sip_session_supplement header_funcs_supplement
static int add_header(void *obj)
const char * header_value
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528
static int load_module(void)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1508
int ast_sip_session_add_datastore(struct ast_sip_session *session, struct ast_datastore *datastore)
Add a datastore to a SIP session.
#define AST_APP_ARG(name)
Define an application argument.
#define ast_sip_session_register_supplement(supplement)