core, lib, modules: restructured source code tree
[sip-router] / src / core / parser / parse_methods.c
1 /*
2  * Copyright (c) 2004 Juha Heinanen
3  *
4  * This file is part of Kamailio, a free SIP server.
5  *
6  * Kamailio is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version
10  *
11  * Kamailio is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License 
17  * along with this program; if not, write to the Free Software 
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20
21 /*! \file
22  * \brief Parser :: Parse Methods
23  *
24  * \ingroup parser
25  */
26
27 #include <strings.h>
28 #include "../dprint.h"
29 #include "../trim.h"
30 #include "parse_methods.h"
31
32
33 /*! \brief
34  * Check if argument is valid RFC3261 token character.
35  */
36 static int token_char(char _c)
37 {
38         return  (_c >= 65 && _c <= 90) ||        /* upper alpha */
39                 (_c >= 97 && _c <= 122) ||       /* lower aplha */
40                 (_c >= 48 && _c <= 57) ||        /* digits */
41                 (_c == '-') || (_c == '.') || (_c == '!') || (_c == '%') ||
42                 (_c == '*') || (_c == '_') || (_c == '+') || (_c == '`') ||
43                 (_c == '\'') || (_c == '~');
44  }
45
46
47
48 /*! \brief Parse a string containing a method.
49  *
50  * Parse a method pointed by s & assign its enum bit to method. The string
51  * _must_ contain _only_ the method (without trailing or heading whitespace).
52  * \return 0 on success, -1 on error
53  */
54 int parse_method_name(const str* const s, enum request_method* const method)
55  {
56         if (unlikely(!s || !method)) {
57                 LOG(L_ERR, "Invalid parameter value\n");
58                 return -1;
59         }
60         
61         if (unlikely(!s->len || (s->s==0))) {
62                 DBG("No input\n");
63                 *method = METHOD_OTHER;
64                 return 0;
65         }
66         
67         switch ((s->s)[0]) {
68                 /* ordered after probability of aparition on a normal proxy */
69                 case 'R':
70                 case 'r':
71                         if (likely((s->len == 8) &&
72                                         !strncasecmp(s->s + 1, "egister", 7))) {
73                                 *method = METHOD_REGISTER;
74                                 return 0;
75                         }
76                         if (likely((s->len==5) && !strncasecmp(s->s + 1, "efer", 4))) {
77                                 *method = METHOD_REFER;
78                                 return 0;
79                         }
80                         break;
81                 case 'A':
82                 case 'a':
83                         if (likely((s->len==3) && !strncasecmp(s->s + 1, "ck", 2))) {
84                                 *method = METHOD_ACK;
85                                 return 0;
86                         }
87                         break;
88                 case 'I':
89                 case 'i':
90                         if (likely((s->len==6) && !strncasecmp(s->s + 1, "nvite", 5))){
91                                 *method = METHOD_INVITE;
92                                 return 0;
93                         }
94                         if (likely((s->len==4) && !strncasecmp(s->s + 1, "nfo", 3))) {
95                                 *method = METHOD_INFO;
96                                 return 0;
97                         }
98                         break;
99                 case 'P':
100                 case 'p':
101                         if (likely((s->len==5) && !strncasecmp(s->s + 1, "rack", 4))) {
102                                 *method = METHOD_PRACK;
103                                 return 0;
104                         }
105                         if (likely((s->len==7) && !strncasecmp(s->s + 1, "ublish", 6))) {
106                                 *method = METHOD_PUBLISH;
107                                 return 0;
108                         }
109                         break;
110                 case 'C':
111                 case 'c':
112                         if (likely((s->len==6) && !strncasecmp(s->s + 1, "ancel", 5))) {
113                                 *method = METHOD_CANCEL;
114                                 return 0;
115                         }
116                         break;
117                 case 'B':
118                 case 'b':
119                         if (likely((s->len==3) && !strncasecmp(s->s + 1, "ye", 2))) {
120                                 *method = METHOD_BYE;
121                                 return 0;
122                         }
123                         break;
124                 case 'M':
125                 case 'm':
126                         if (likely((s->len==7) && !strncasecmp(s->s + 1, "essage", 6))) {
127                                 *method = METHOD_MESSAGE;
128                                 return 0;
129                         }
130                         break;
131                 case 'O':
132                 case 'o':
133                         if (likely((s->len==7) && !strncasecmp(s->s + 1, "ptions", 6))) {
134                                 *method = METHOD_OPTIONS;
135                                 return 0;
136                         }
137                         break;
138                 case 'S':
139                 case 's':
140                         if (likely((s->len==9) && !strncasecmp(s->s + 1, "ubscribe", 8))) {
141                                 *method = METHOD_SUBSCRIBE;
142                                 return 0;
143                         }
144                         break;
145                 case 'N':
146                 case 'n':
147                         if (likely((s->len==6) && !strncasecmp(s->s + 1, "otify", 5))){
148                                 *method = METHOD_NOTIFY;
149                                 return 0;
150                         }
151                         break;
152                 case 'U':
153                 case 'u':
154                         if (likely((s->len==6) && !strncasecmp(s->s + 1, "pdate", 5))){
155                                 *method = METHOD_UPDATE;
156                                 return 0;
157                         }
158                         break;
159                 default:
160                         break;
161         }
162         /* unknown method */
163         *method = METHOD_OTHER;
164         return 0;
165 }
166
167
168
169  /*! \brief
170   * Parse a method pointed by _next, assign its enum bit to _method, and update
171   * _next past the method. Returns 1 if parse succeeded and 0 otherwise.
172   */
173 static int parse_method_advance(str* const _next, enum request_method* const _method)
174  {
175         char* end;
176         
177          if (unlikely(!_next || !_method)) {
178                  LOG(L_ERR, "Invalid parameter value\n");
179                  return 0;
180          }
181          
182          if (unlikely(!_next->len || !_next->s)) {
183                  DBG("No input\n");
184                 *_method = METHOD_OTHER;
185                  return 1;
186          }
187         end=_next->s+_next->len;
188         
189          switch ((_next->s)[0]) {
190          case 'A':
191          case 'a':
192                  if ((_next->len > 2) && !strncasecmp(_next->s + 1, "ck", 2)) {
193                         *_method = METHOD_ACK;
194                         _next->len -= 3;
195                         _next->s += 3;
196                         goto found;
197                 } else {
198                         goto unknown;
199                 }
200
201         case 'B':
202         case 'b':
203                 if ((_next->len > 2) && !strncasecmp(_next->s + 1, "ye", 2)) {
204                         *_method = METHOD_BYE;
205                         _next->len -= 3;
206                         _next->s += 3;
207                         goto found;
208                 } else {
209                         goto unknown;
210                 }
211
212         case 'C':
213         case 'c':
214                 if ((_next->len > 5) && !strncasecmp(_next->s + 1, "ancel", 5)) {
215                         *_method = METHOD_CANCEL;
216                         _next->len -= 6;
217                         _next->s += 6;
218                         goto found;
219                 } else {
220                         goto unknown;
221                 }
222
223         case 'I':
224         case 'i':
225                 if ((_next->len > 3) &&
226                     ((*(_next->s + 1) == 'N') || (*(_next->s + 1) == 'n'))) {
227                         if (!strncasecmp(_next->s + 2, "fo", 2)) {
228                                 *_method = METHOD_INFO;
229                                 _next->len -= 4;
230                                 _next->s += 4;
231                                 goto found;
232                         }
233
234                         if ((_next->len > 5) && !strncasecmp(_next->s + 2, "vite", 4)) {
235                                 *_method = METHOD_INVITE;
236                                 _next->len -= 6;
237                                 _next->s += 6;
238                                 goto found;
239                         }
240                 }
241                 goto unknown;
242
243         case 'M':
244         case 'm':
245                 if ((_next->len > 6) && !strncasecmp(_next->s + 1, "essage", 6)) {
246                         *_method = METHOD_MESSAGE;
247                         _next->len -= 7;
248                         _next->s += 7;
249                         goto found;
250                 } else {
251                         goto unknown;
252                 }
253
254         case 'N':
255         case 'n':
256                 if ((_next->len > 5) && !strncasecmp(_next->s + 1, "otify", 5)) {
257                         *_method = METHOD_NOTIFY;
258                         _next->len -= 6;
259                         _next->s += 6;
260                         goto found;
261                 } else {
262                         goto unknown;
263                 }
264
265         case 'O':
266         case 'o':
267                 if ((_next->len > 6) && !strncasecmp(_next->s + 1, "ptions", 6)) {
268                         *_method = METHOD_OPTIONS;
269                         _next->len -= 7;
270                         _next->s += 7;
271                         goto found;
272                 } else {
273                         goto unknown;
274                 }
275
276         case 'P':
277         case 'p':
278                 if ((_next->len > 4) && !strncasecmp(_next->s + 1, "rack", 4)) {
279                         *_method = METHOD_PRACK;
280                         _next->len -= 5;
281                         _next->s += 5;
282                         goto found;
283                 }
284                 if ((_next->len > 6) && !strncasecmp(_next->s + 1, "ublish", 6)) {
285                         *_method = METHOD_PUBLISH;
286                         _next->len -= 7;
287                         _next->s += 7;
288                         goto found;
289                 }
290                 goto unknown;
291
292         case 'R':
293         case 'r':
294                 if ((_next->len > 4) &&
295                     ((*(_next->s + 1) == 'E') || (*(_next->s + 1) == 'e'))) {
296                         if (!strncasecmp(_next->s + 2, "fer", 3)) {
297                                 *_method = METHOD_REFER;
298                                 _next->len -= 5;
299                                 _next->s += 5;
300                                 goto found;
301                         }
302
303                         if ((_next->len > 7) && !strncasecmp(_next->s + 2, "gister", 6)) {
304                                 *_method = METHOD_REGISTER;
305                                 _next->len -= 8;
306                                 _next->s += 8;
307                                 goto found;
308                         }
309                 }
310                 goto unknown;
311
312         case 'S':
313         case 's':
314                 if ((_next->len > 8) && !strncasecmp(_next->s + 1, "ubscribe", 8)) {
315                         *_method = METHOD_SUBSCRIBE;
316                         _next->len -= 9;
317                         _next->s += 9;
318                         goto found;
319                 } else {
320                         goto unknown;
321                 }
322
323         case 'U':
324         case 'u':
325                 if ((_next->len > 5) && !strncasecmp(_next->s + 1, "pdate", 5)) {
326                         *_method = METHOD_UPDATE;
327                         _next->len -= 6;
328                         _next->s += 6;
329                         goto found;
330                 } else {
331                         goto unknown;
332                 }
333
334         default:
335                 goto unknown;
336         }
337
338  unknown:
339         if (token_char(*(_next->s))) {
340                 do { 
341                         _next->s++;
342                         _next->len--;
343                 } while (_next->len && token_char(*(_next->s)));
344                 *_method = METHOD_OTHER;
345                 return 1;
346         } else {
347                 return 0;
348         }
349 found:
350         /* check if the method really ends here (if not return 0) */
351         return (_next->s>=end) || (!token_char(*(_next->s)));
352  }
353  
354  
355  /*! \brief
356   * Parse comma separated list of methods pointed by _body and assign their
357   * enum bits to _methods.  Returns 0 on success and -1 on failure.
358   */
359 int parse_methods(const str* const _body, unsigned int* const _methods)
360  {
361         str next;
362         unsigned int method;
363         
364         method=0; /* fixes silly gcc 4.x warning */
365  
366         if (!_body || !_methods) {
367                 LOG(L_ERR, "parse_methods: Invalid parameter value\n");
368                 return -1;
369         }
370
371         next.len = _body->len;
372         next.s = _body->s;
373  
374         trim_leading(&next);
375  
376         *_methods = 0;
377  
378         if (next.len == 0) {
379                 return 0;
380         }
381
382         while (1) {
383                 if (parse_method_advance(&next, &method)) {
384                         *_methods |= method;
385                 } else {
386                         LOG(L_ERR, "ERROR: parse_methods: Invalid method\n");
387                         return -1;
388                 }
389                 
390                 trim_leading(&next);
391                 if (next.len) {
392                         if (next.s[0] == ',') {
393                                 next.len--;
394                                 next.s++;
395                                 trim_leading(&next);
396                                 if (next.len == 0) {
397                                         LOG(L_ERR, "ERROR: parse_methods: Method expected\n");
398                                         return 0;
399                                 }
400                         } else {
401                                 LOG(L_ERR, "ERROR: parse_methods: Comma expected\n");
402                                 return -1;
403                         }
404                 } else {
405                         break;
406                 }
407         }
408
409         return 0;
410  }