Fix corruptin in app_cut (bug #1084)
[asterisk/asterisk.git] / apps / app_cut.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Cut application
5  *
6  * Copyright (c) 2003 Tilghman Lesher.  All rights reserved.
7  *
8  * Tilghman Lesher <app_cut__v002@the-tilghman.com>
9  *
10  * $Id$
11  *
12  * This code is released by the author with no restrictions on usage.
13  *
14  */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <string.h>
20 #include <asterisk/file.h>
21 #include <asterisk/logger.h>
22 #include <asterisk/options.h>
23 #include <asterisk/channel.h>
24 #include <asterisk/pbx.h>
25 #include <asterisk/module.h>
26
27 /* Maximum length of any variable */
28 #define MAXRESULT       1024
29
30 static char *tdesc = "Cuts up variables";
31
32 static char *app_cut = "Cut";
33
34 static char *cut_synopsis = "Cut(newvar=varname|delimiter|fieldspec)";
35
36 static char *cut_descrip =
37 "Cut(newvar=varname,delimiter,field)\n"
38 "  newvar    - new variable created from result string\n"
39 "  varname   - variable you want cut\n"
40 "  delimiter - defaults to '-'\n"
41 "  fieldspec - number of the field you want (1-based offset)\n"
42 "            may also be specified as a range (with -)\n"
43 "            or group of ranges and fields (with &)\n" 
44 "  Returns 0 or -1 on hangup or error.\n";
45
46 STANDARD_LOCAL_USER;
47
48 LOCAL_USER_DECL;
49
50 static int cut_exec(struct ast_channel *chan, void *data)
51 {
52         int res=0;
53         struct localuser *u;
54         char *s, *newvar=NULL, *varname=NULL, *delimiter=NULL, *field=NULL;
55         int args_okay = 0;
56
57         LOCAL_USER_ADD(u);
58
59         /* Check and parse arguments */
60         if (data) {
61                 s = ast_strdupa((char *)data);
62                 if (s) {
63                         newvar = strsep(&s, "=");
64                         if (newvar && (newvar[0] != '\0')) {
65                                 varname = strsep(&s, "|");
66                                 if (varname && (varname[0] != '\0')) {
67                                         delimiter = strsep(&s, "|");
68                                         if (delimiter) {
69                                                 field = strsep(&s, "|");
70                                                 if (field) {
71                                                         args_okay = 1;
72                                                 }
73                                         }
74                                 }
75                         }
76                 } else {
77                         ast_log(LOG_ERROR, "Out of memory\n");
78                         res = -1;
79                 }
80         }
81
82         if (args_okay) {
83                 char d, ds[2];
84                 char *tmp = alloca(strlen(varname) + 4);
85                 char *tmp2 = alloca(MAXRESULT);
86                 char retstring[MAXRESULT];
87
88                 if (tmp2)
89                         memset(tmp2, 0, MAXRESULT);
90                 memset(retstring, 0, MAXRESULT);
91
92                 if (tmp && tmp2) {
93                         snprintf(tmp, strlen(varname) + 4, "${%s}", varname);
94                         memset(tmp2, 0, sizeof(tmp2));
95                 } else {
96                         ast_log(LOG_ERROR, "Out of memory");
97                         return -1;
98                 }
99
100                 if (delimiter[0])
101                         d = delimiter[0];
102                 else
103                         d = '-';
104
105                 /* String form of the delimiter, for use with strsep(3) */
106                 sprintf(ds,"%c",d);
107
108                 pbx_substitute_variables_helper(chan, tmp, tmp2, MAXRESULT - 1);
109
110                 if (tmp2) {
111                         int curfieldnum = 1;
112                         while ((tmp2 != NULL) && (field != NULL)) {
113                                 char *nextgroup = strsep(&field, "&");
114                                 int num1 = 0, num2 = MAXRESULT;
115                                 char trashchar;
116
117                                 if (sscanf(nextgroup, "%d-%d", &num1, &num2) == 2) {
118                                         /* range with both start and end */
119                                 } else if (sscanf(nextgroup, "-%d", &num2) == 1) {
120                                         /* range with end */
121                                         num1 = 0;
122                                 } else if ((sscanf(nextgroup, "%d%c", &num1, &trashchar) == 2) && (trashchar == '-')) {
123                                         /* range with start */
124                                         num2 = MAXRESULT;
125                                 } else if (sscanf(nextgroup, "%d", &num1) == 1) {
126                                         /* single number */
127                                         num2 = num1;
128                                 } else {
129                                         ast_log(LOG_ERROR, "Cut(): Illegal range '%s'\n", nextgroup);
130                                         ast_log(LOG_ERROR, "Usage: %s\n", cut_synopsis);
131                                         return -1;
132                                 }
133
134                                 /* Get to start, if any */
135                                 if (num1 > 0) {
136                                         while ((tmp2 != (char *)NULL + 1) && (curfieldnum < num1)) {
137                                                 tmp2 = index(tmp2, d) + 1;
138                                                 curfieldnum++;
139                                         }
140                                 }
141
142                                 /* Most frequent problem is the expectation of reordering fields */
143                                 if ((num1 > 0) && (curfieldnum > num1)) {
144                                         ast_log(LOG_WARNING, "Cut(): we're already past the field you wanted?\n");
145                                 }
146
147                                 /* Re-null tmp2 if we added 1 to NULL */
148                                 if (tmp2 == (char *)NULL + 1)
149                                         tmp2 = NULL;
150
151                                 /* Output fields until we either run out of fields or num2 is reached */
152                                 while ((tmp2 != NULL) && (curfieldnum <= num2)) {
153                                         char *tmp3 = strsep(&tmp2, ds);
154                                         int curlen = strlen(retstring);
155
156                                         if (strlen(retstring)) {
157                                                 snprintf(retstring + curlen, MAXRESULT - curlen, "%c%s", d, tmp3);
158                                         } else {
159                                                 snprintf(retstring, MAXRESULT, "%s", tmp3);
160                                         }
161
162                                         curfieldnum++;
163                                 }
164                         }
165                 }
166
167                 pbx_builtin_setvar_helper(chan, newvar, retstring);
168         }
169
170         LOCAL_USER_REMOVE(u);
171         return res;
172 }
173
174 int unload_module(void)
175 {
176         STANDARD_HANGUP_LOCALUSERS;
177         return ast_unregister_application(app_cut);
178 }
179
180 int load_module(void)
181 {
182         return ast_register_application(app_cut, cut_exec, cut_synopsis, cut_descrip);
183 }
184
185 char *description(void)
186 {
187         return tdesc;
188 }
189
190 int usecount(void)
191 {
192         int res;
193         STANDARD_USECOUNT(res);
194         return res;
195 }
196
197 char *key()
198 {
199         return ASTERISK_GPL_KEY;
200 }