SIP channel name uniqueness
[asterisk/asterisk.git] / funcs / func_redirecting.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2008 Digium, Inc.
5  *
6  * Richard Mudgett <rmudgett@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 /*!
20  * \file
21  * \brief Redirecting data dialplan function
22  * \ingroup functions
23  *
24  * \author Richard Mudgett <rmudgett@digium.com>
25  *
26  * See Also:
27  * \arg \ref AstCREDITS
28  */
29
30
31 #include "asterisk.h"
32
33 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
34
35 /* ------------------------------------------------------------------- */
36
37
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <sys/types.h>
42
43 #include "asterisk/module.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/pbx.h"
46 #include "asterisk/logger.h"
47 #include "asterisk/utils.h"
48 #include "asterisk/app.h"
49 #include "asterisk/options.h"
50 #include "asterisk/callerid.h"
51
52 /*
53  * Do not document the REDIRECTING(pres) datatype.
54  * It has turned out that the from-pres and to-pres values must be kept
55  * separate.  They represent two different parties and there is a case when
56  * they are active at the same time.  The plain pres option will simply
57  * live on as a historical relic.
58  */
59 /*** DOCUMENTATION
60         <function name="REDIRECTING" language="en_US">
61                 <synopsis>
62                         Gets or sets Redirecting data on the channel.
63                 </synopsis>
64                 <syntax>
65                         <parameter name="datatype" required="true">
66                                 <para>The allowable datatypes are:</para>
67                                 <enumlist>
68                                         <enum name = "from-all" />
69                                         <enum name = "from-num" />
70                                         <enum name = "from-name" />
71                                         <enum name = "from-ton" />
72                                         <enum name = "from-pres" />
73                                         <enum name = "to-all" />
74                                         <enum name = "to-num" />
75                                         <enum name = "to-name" />
76                                         <enum name = "to-ton" />
77                                         <enum name = "to-pres" />
78                                         <enum name = "reason" />
79                                         <enum name = "count" />
80                                 </enumlist>
81                         </parameter>
82                         <parameter name="i">
83                                 <para>If set, this will prevent the channel from sending out protocol
84                                 messages because of the value being set</para>
85                         </parameter>
86                 </syntax>
87                 <description>
88                         <para>Gets or sets Redirecting data on the channel. The allowable values
89                         for the <replaceable>reason</replaceable> field are the following:</para>
90                         <enumlist>
91                                 <enum name = "unknown"><para>Unknown</para></enum>
92                                 <enum name = "cfb"><para>Call Forwarding Busy</para></enum>
93                                 <enum name = "cfnr"><para>Call Forwarding No Reply</para></enum>
94                                 <enum name = "unavailable"><para>Callee is Unavailable</para></enum>
95                                 <enum name = "time_of_day"><para>Time of Day</para></enum>
96                                 <enum name = "dnd"><para>Do Not Disturb</para></enum>
97                                 <enum name = "deflection"><para>Call Deflection</para></enum>
98                                 <enum name = "follow_me"><para>Follow Me</para></enum>
99                                 <enum name = "out_of_order"><para>Called DTE Out-Of-Order</para></enum>
100                                 <enum name = "away"><para>Callee is Away</para></enum>
101                                 <enum name = "cf_dte"><para>Call Forwarding By The Called DTE</para></enum>
102                                 <enum name = "cfu"><para>Call Forwarding Unconditional</para></enum>
103                         </enumlist>
104                 </description>
105         </function>
106  ***/
107
108 enum ID_FIELD_STATUS {
109         ID_FIELD_VALID,
110         ID_FIELD_INVALID,
111         ID_FIELD_UNKNOWN
112 };
113
114
115
116
117 /* ******************************************************************* */
118 /*!
119  * \internal
120  * \brief Read values from the party id struct.
121  *
122  * \param buf Buffer to fill with read value.
123  * \param len Length of the buffer
124  * \param data Remaining function datatype string
125  *
126  * \retval ID_FIELD_VALID on success.
127  * \retval ID_FIELD_UNKNOWN on unknown field name.
128  */
129 static enum ID_FIELD_STATUS redirecting_id_read(char *buf, size_t len, char *data, const struct ast_party_id *id)
130 {
131         enum ID_FIELD_STATUS status;
132
133         status = ID_FIELD_VALID;
134
135         if (!strncasecmp("all", data, 3)) {
136                 snprintf(buf, len, "\"%s\" <%s>",
137                          S_OR(id->name, ""),
138                          S_OR(id->number, ""));
139         } else if (!strncasecmp("name", data, 4)) {
140                 if (id->name) {
141                         ast_copy_string(buf, id->name, len);
142                 }
143         } else if (!strncasecmp("num", data, 3)) {
144                 if (id->number) {
145                         ast_copy_string(buf, id->number, len);
146                 }
147         } else if (!strncasecmp("ton", data, 3)) {
148                 snprintf(buf, len, "%d", id->number_type);
149         } else if (!strncasecmp("pres", data, 4)) {
150                 ast_copy_string(buf, ast_named_caller_presentation(id->number_presentation), len);
151         } else {
152                 status = ID_FIELD_UNKNOWN;
153         }
154
155         return status;
156 }
157
158
159
160
161 /* ******************************************************************* */
162 /*!
163  * \internal
164  * \brief Read values from the redirecting information struct.
165  *
166  * \param chan Asterisk channel to read
167  * \param cmd Not used
168  * \param data Redirecting function datatype string
169  * \param buf Buffer to fill with read value.
170  * \param len Length of the buffer
171  *
172  * \retval 0 on success.
173  * \retval -1 on error.
174  */
175 static int redirecting_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
176 {
177         /* Ensure that the buffer is empty */
178         *buf = 0;
179
180         if (!chan)
181                 return -1;
182
183         ast_channel_lock(chan);
184
185         if (!strncasecmp("from-", data, 5)) {
186                 struct ast_party_id from_id;
187
188                 from_id = chan->redirecting.from;
189                 from_id.number = chan->cid.cid_rdnis;
190                 switch (redirecting_id_read(buf, len, data + 5, &from_id)) {
191                 case ID_FIELD_VALID:
192                 case ID_FIELD_INVALID:
193                         break;
194
195                 default:
196                         ast_log(LOG_ERROR, "Unknown redirecting data type '%s'.\n", data);
197                         break;
198                 }
199         } else if (!strncasecmp("to-", data, 3)) {
200                 switch (redirecting_id_read(buf, len, data + 3, &chan->redirecting.to)) {
201                 case ID_FIELD_VALID:
202                 case ID_FIELD_INVALID:
203                         break;
204
205                 default:
206                         ast_log(LOG_ERROR, "Unknown redirecting data type '%s'.\n", data);
207                         break;
208                 }
209         } else if (!strncasecmp("pres", data, 4)) {
210                 ast_copy_string(buf, ast_named_caller_presentation(chan->redirecting.from.number_presentation), len);
211         } else if (!strncasecmp("reason", data, 6)) {
212                 ast_copy_string(buf, ast_redirecting_reason_name(chan->redirecting.reason), len);
213         } else if (!strncasecmp("count", data, 5)) {
214                 snprintf(buf, len, "%d", chan->redirecting.count);
215         } else {
216                 ast_log(LOG_ERROR, "Unknown redirecting data type '%s'.\n", data);
217         }
218
219         ast_channel_unlock(chan);
220
221         return 0;
222 }
223
224
225
226
227 /* ******************************************************************* */
228 /*!
229  * \internal
230  * \brief Write new values to the party id struct
231  *
232  * \param id Party ID struct to write values
233  * \param data Remaining function datatype string
234  * \param value Value to assign to the party id.
235  *
236  * \retval ID_FIELD_VALID on success.
237  * \retval ID_FIELD_INVALID on error with field value.
238  * \retval ID_FIELD_UNKNOWN on unknown field name.
239  */
240 static enum ID_FIELD_STATUS redirecting_id_write(struct ast_party_id *id, char *data, const char *value)
241 {
242         char *val;
243         enum ID_FIELD_STATUS status;
244
245         status = ID_FIELD_VALID;
246
247         if (!strncasecmp("all", data, 3)) {
248                 char name[256];
249                 char num[256];
250
251                 ast_callerid_split(value, name, sizeof(name), num, sizeof(num));
252                 if (!(id->name = ast_strdup(name))) {
253                         return ID_FIELD_INVALID;
254                 }
255                 if (!(id->number = ast_strdup(num))) {
256                         return ID_FIELD_INVALID;
257                 }
258         } else if (!strncasecmp("name", data, 4)) {
259                 id->name = ast_strdup(value);
260                 ast_trim_blanks(id->name);
261         } else if (!strncasecmp("num", data, 3)) {
262                 id->number = ast_strdup(value);
263                 ast_trim_blanks(id->number);
264         } else if (!strncasecmp("ton", data, 3)) {
265                 val = ast_strdupa(value);
266                 ast_trim_blanks(val);
267
268                 if (('0' <= val[0]) && (val[0] <= '9')) {
269                         id->number_type = atoi(val);
270                 } else {
271                         ast_log(LOG_ERROR, "Unknown redirecting type of number '%s', value unchanged\n", val);
272                         status = ID_FIELD_INVALID;
273                 }
274         } else if (!strncasecmp("pres", data, 4)) {
275                 int pres;
276
277                 val = ast_strdupa(value);
278                 ast_trim_blanks(val);
279
280                 if (('0' <= val[0]) && (val[0] <= '9')) {
281                         pres = atoi(val);
282                 } else {
283                         pres = ast_parse_caller_presentation(val);
284                 }
285
286                 if (pres < 0) {
287                         ast_log(LOG_ERROR, "Unknown redirecting number presentation '%s', value unchanged\n", val);
288                         status = ID_FIELD_INVALID;
289                 } else {
290                         id->number_presentation = pres;
291                 }
292         } else {
293                 status = ID_FIELD_UNKNOWN;
294         }
295
296         return status;
297 }
298
299
300
301
302 /* ******************************************************************* */
303 /*!
304  * \internal
305  * \brief Write new values to the redirecting information struct.
306  *
307  * \param chan Asterisk channel to update
308  * \param cmd Not used
309  * \param data Redirecting function datatype string
310  * \param value Value to assign to the redirecting information struct.
311  *
312  * \retval 0 on success.
313  * \retval -1 on error.
314  */
315 static int redirecting_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
316 {
317         struct ast_party_redirecting redirecting;
318         char *val;
319         char *option;
320         void (*set_it)(struct ast_channel *chan, const struct ast_party_redirecting *redirecting);
321
322         if (!value || !chan) {
323                 return -1;
324         }
325
326         /* Determine if the update indication inhibit option is present */
327         option = strchr(data, ',');
328         if (option) {
329                 option = ast_skip_blanks(option + 1);
330                 switch (*option) {
331                 case 'i':
332                         set_it = ast_channel_set_redirecting;
333                         break;
334
335                 default:
336                         ast_log(LOG_ERROR, "Unknown redirecting option '%s'.\n", option);
337                         return 0;
338                 }
339         }
340         else {
341                 set_it = ast_channel_update_redirecting;
342         }
343
344         ast_channel_lock(chan);
345         ast_party_redirecting_set_init(&redirecting, &chan->redirecting);
346         ast_channel_unlock(chan);
347
348         value = ast_skip_blanks(value);
349
350         if (!strncasecmp("from-", data, 5)) {
351                 switch (redirecting_id_write(&redirecting.from, data + 5, value)) {
352                 case ID_FIELD_VALID:
353                         set_it(chan, &redirecting);
354                         ast_party_redirecting_free(&redirecting);
355                         break;
356
357                 case ID_FIELD_INVALID:
358                         break;
359
360                 default:
361                         ast_log(LOG_ERROR, "Unknown redirecting data type '%s'.\n", data);
362                         break;
363                 }
364         } else if (!strncasecmp("to-", data, 3)) {
365                 switch (redirecting_id_write(&redirecting.to, data + 3, value)) {
366                 case ID_FIELD_VALID:
367                         set_it(chan, &redirecting);
368                         ast_party_redirecting_free(&redirecting);
369                         break;
370
371                 case ID_FIELD_INVALID:
372                         break;
373
374                 default:
375                         ast_log(LOG_ERROR, "Unknown redirecting data type '%s'.\n", data);
376                         break;
377                 }
378         } else if (!strncasecmp("pres", data, 4)) {
379                 int pres;
380
381                 val = ast_strdupa(value);
382                 ast_trim_blanks(val);
383
384                 if (('0' <= val[0]) && (val[0] <= '9')) {
385                         pres = atoi(val);
386                 } else {
387                         pres = ast_parse_caller_presentation(val);
388                 }
389
390                 if (pres < 0) {
391                         ast_log(LOG_ERROR, "Unknown redirecting number presentation '%s', value unchanged\n", val);
392                 } else {
393                         redirecting.from.number_presentation = pres;
394                         redirecting.to.number_presentation = pres;
395                         set_it(chan, &redirecting);
396                 }
397         } else if (!strncasecmp("reason", data, 6)) {
398                 int reason;
399
400                 val = ast_strdupa(value);
401                 ast_trim_blanks(val);
402
403                 if (('0' <= val[0]) && (val[0] <= '9')) {
404                         reason = atoi(val);
405                 } else {
406                         reason = ast_redirecting_reason_parse(val);
407                 }
408
409                 if (reason < 0) {
410                         ast_log(LOG_ERROR, "Unknown redirecting reason '%s', value unchanged\n", val);
411                 } else {
412                         redirecting.reason = reason;
413                         set_it(chan, &redirecting);
414                 }
415         } else if (!strncasecmp("count", data, 5)) {
416                 val = ast_strdupa(value);
417                 ast_trim_blanks(val);
418
419                 if (('0' <= val[0]) && (val[0] <= '9')) {
420                         redirecting.count = atoi(val);
421                         set_it(chan, &redirecting);
422                 } else {
423                         ast_log(LOG_ERROR, "Unknown redirecting count '%s', value unchanged\n", val);
424                 }
425         } else {
426                 ast_log(LOG_ERROR, "Unknown redirecting data type '%s'.\n", data);
427         }
428
429         return 0;
430 }
431
432
433
434
435 static struct ast_custom_function redirecting_function = {
436         .name = "REDIRECTING",
437         .read = redirecting_read,
438         .write = redirecting_write,
439 };
440
441
442
443
444 /* ******************************************************************* */
445 /*!
446  * \internal
447  * \brief Unload the function module
448  *
449  * \retval 0 on success.
450  * \retval -1 on error.
451  */
452 static int unload_module(void)
453 {
454         return ast_custom_function_unregister(&redirecting_function);
455 }
456
457
458
459
460 /* ******************************************************************* */
461 /*!
462  * \internal
463  * \brief Load and initialize the function module.
464  *
465  * \retval 0 on success.
466  * \retval -1 on error.
467  */
468 static int load_module(void)
469 {
470         return ast_custom_function_register(&redirecting_function)
471                 ? AST_MODULE_LOAD_DECLINE
472                 : AST_MODULE_LOAD_SUCCESS;
473 }
474
475
476
477
478 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Redirecting data dialplan function");
479
480
481 /* ------------------------------------------------------------------- */
482 /* end func_redirecting.c */