1 /*
2  * Copyright (c) 2004-2006 Maxim Sobolev <sobomax@FreeBSD.org>
3  * Copyright (c) 2006-2014 Sippy Software, Inc., http://www.sippysoft.com
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28 
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <stdint.h>
32 #include <stdlib.h>
33 
34 #include "rtpp_log.h"
35 #include "rtpp_cfg_stable.h"
36 #include "rtpp_defines.h"
37 #include "rtpp_command.h"
38 #include "rtpp_command_parse.h"
39 #include "rtpp_command_private.h"
40 #include "rtpp_command_query.h"
41 #include "rtpp_types.h"
42 #include "rtpp_stats.h"
43 #include "rtpp_log_obj.h"
44 
45 struct cmd_props {
46     int max_argc;
47     int min_argc;
48     int has_cmods;
49     int has_call_id;
50     int fpos;
51     int tpos;
52     char *cmods;
53 };
54 
55 static int
56 fill_cmd_props(struct cfg *cf, struct rtpp_command *cmd,
57   struct cmd_props *cpp)
58 {
59 
60     cpp->has_call_id = 1;
61     cpp->fpos = -1;
62     cpp->tpos = -1;
63     cpp->cmods = &(cmd->argv[0][1]);
64     switch (cmd->argv[0][0]) {
65     case 'u':
66     case 'U':
67         cmd->cca.op = UPDATE;
68         cmd->cca.rname = "update/create";
69         cmd->cca.hint = "U[opts] callid remote_ip remote_port from_tag [to_tag] [notify_socket notify_tag]";
70         cpp->max_argc = 8;
71         cpp->min_argc = 5;
72         cpp->has_cmods = 1;
73         cpp->fpos = 4;
74         cpp->tpos = 5;
75         break;
76 
77     case 'l':
78     case 'L':
79         cmd->cca.op = LOOKUP;
80         cmd->cca.rname = "lookup";
81         cmd->cca.hint = "L[opts] callid remote_ip remote_port from_tag [to_tag]";
82         cpp->max_argc = 6;
83         cpp->min_argc = 5;
84         cpp->has_cmods = 1;
85         cpp->fpos = 4;
86         cpp->tpos = 5;
87         break;
88 
89     case 'd':
90     case 'D':
91         cmd->cca.op = DELETE;
92         cmd->cca.rname = "delete";
93         cmd->cca.hint = "D[w] callid from_tag [to_tag]";
94         cpp->max_argc = 4;
95         cpp->min_argc = 3;
96         cpp->has_cmods = 1;
97         cpp->fpos = 2;
98         cpp->tpos = 3;
99         break;
100 
101     case 'p':
102     case 'P':
103         cmd->cca.op = PLAY;
104         cmd->cca.rname = "play";
105         cmd->cca.hint = "P[n] callid pname codecs from_tag [to_tag]";
106         cpp->max_argc = 6;
107         cpp->min_argc = 5;
108         cpp->has_cmods = 1;
109         cpp->fpos = 4;
110         cpp->tpos = 5;
111         break;
112 
113     case 'r':
114     case 'R':
115         cmd->cca.op = RECORD;
116         cmd->cca.rname = "record";
117         if (cf->stable->record_pcap != 0) {
118             cmd->cca.hint = "R[s] call_id from_tag [to_tag]";
119         } else {
120             cmd->cca.hint = "R call_id from_tag [to_tag]";
121         }
122         cpp->max_argc = 4;
123         cpp->min_argc = 3;
124         cpp->has_cmods = 1;
125         cpp->fpos = 2;
126         cpp->tpos = 3;
127         break;
128 
129     case 'c':
130     case 'C':
131         cmd->cca.op = COPY;
132         cmd->cca.rname = "copy";
133         cmd->cca.hint = "C[-xxx-] call_id -XXX- from_tag [to_tag]";
134         cpp->max_argc = 5;
135         cpp->min_argc = 4;
136         cpp->has_cmods = 1;
137         cpp->fpos = 3;
138         cpp->tpos = 4;
139         break;
140 
141     case 's':
142     case 'S':
143         cmd->cca.op = NOPLAY;
144         cmd->cca.rname = "noplay";
145         cmd->cca.hint = "S call_id from_tag [to_tag]";
146         cpp->max_argc = 4;
147         cpp->min_argc = 3;
148         cpp->has_cmods = 0;
149         cpp->fpos = 2;
150         cpp->tpos = 3;
151         break;
152 
153     case 'v':
154     case 'V':
155         if (cpp->cmods[0] == 'F' || cpp->cmods[0] == 'f') {
156             cpp->cmods += 1;
157             cmd->cca.op = VER_FEATURE;
158             cmd->cca.rname = "feature_check";
159             cmd->cca.hint = "VF feature_num";
160             cmd->no_glock = 1;
161             cpp->max_argc = 2;
162             cpp->min_argc = 2;
163             cpp->has_cmods = 0;
164             cpp->has_call_id = 0;
165             break;
166         }
167         cmd->cca.op = GET_VER;
168         cmd->cca.rname = "get_version";
169         cmd->cca.hint = "V";
170         cmd->no_glock = 1;
171         cpp->max_argc = 1;
172         cpp->min_argc = 1;
173         cpp->has_cmods = 0;
174         cpp->has_call_id = 0;
175         break;
176 
177     case 'i':
178     case 'I':
179         cmd->cca.op = INFO;
180         cmd->cca.rname = "get_info";
181         cmd->cca.hint = "I[b]";
182         cpp->max_argc = 1;
183         cpp->min_argc = 1;
184         cpp->has_cmods = 1;
185         cpp->has_call_id = 0;
186         break;
187 
188     case 'q':
189     case 'Q':
190         cmd->cca.op = QUERY;
191         cmd->cca.rname = "query";
192         cmd->cca.hint = "Q[v] call_id from_tag [to_tag [stat_name1 ...[stat_nameN]]]";
193         cpp->max_argc = 4 + RTPP_QUERY_NSTATS;
194         cpp->min_argc = 3;
195         cpp->has_cmods = 1;
196         cpp->fpos = 2;
197         cpp->tpos = 3;
198         break;
199 
200     case 'x':
201     case 'X':
202         cmd->cca.op = DELETE_ALL;
203         cmd->cca.rname = "delete_all";
204         cmd->cca.hint = "X";
205         cpp->max_argc = 1;
206         cpp->min_argc = 1;
207         cpp->has_cmods = 0;
208         cpp->has_call_id = 0;
209         break;
210 
211     case 'g':
212     case 'G':
213         cmd->cca.op = GET_STATS;
214         cmd->cca.rname = "get_stats";
215         cmd->cca.hint = "G[v] [stat_name1 [stat_name2 [stat_name3 ...[stat_nameN]]]]";
216         cmd->no_glock = 1;
217         cpp->max_argc = CALL_METHOD(cf->stable->rtpp_stats, getnstats) + 1;
218         cpp->min_argc = 1;
219         cpp->has_cmods = 1;
220         cpp->has_call_id = 0;
221         break;
222 
223     default:
224         return (-1);
225     }
226     return (0);
227 }
228 
229 int
230 rtpp_command_pre_parse(struct cfg *cf, struct rtpp_command *cmd)
231 {
232     struct cmd_props cprops;
233 
234     if (fill_cmd_props(cf, cmd, &cprops) != 0) {
235         RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "unknown command \"%c\"",
236           cmd->argv[0][0]);
237         reply_error(cmd, ECODE_CMDUNKN);
238         return (-1);
239     }
240     if (cmd->argc < cprops.min_argc || cmd->argc > cprops.max_argc) {
241         RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "%s command syntax error"
242           ": invalid number of arguments (%d)", cmd->cca.rname, cmd->argc);
243         reply_error(cmd, ECODE_PARSE_NARGS);
244         return (-1);
245     }
246     if (cprops.has_cmods == 0 && cprops.cmods[0] != '\0') {
247         RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "%s command syntax error"
248           ": modifiers are not supported by the command", cmd->cca.rname);
249         reply_error(cmd, ECODE_PARSE_MODS);
250         return (-1);
251     }
252     cmd->cca.call_id = cprops.has_call_id ? cmd->argv[1] : NULL;
253     cmd->cca.from_tag = cprops.fpos > 0 ? cmd->argv[cprops.fpos] : NULL;
254     cmd->cca.to_tag = cprops.tpos > 0 ? cmd->argv[cprops.tpos] : NULL;
255     return (0);
256 }
257