1 /*
2  * This file contains string parsing functions.
3  *
4  * All functions advance the input after the successfully parsed token, or
5  * to the next non-delimiter if no token could be successfully parsed.
6  * All functions return pointer to static data which may not be freed.
7  *
8  * climm Copyright (C) © 2001-2010 Rüdiger Kuhlmann
9  *
10  * climm is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; version 2 dated June, 1991.
13  *
14  * climm is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
17  * License for more details.
18  *
19  * In addition, as a special exception permission is granted to link the
20  * code of this release of climm with the OpenSSL project's "OpenSSL"
21  * library, and distribute the linked executables.  You must obey the GNU
22  * General Public License in all respects for all of the code used other
23  * than "OpenSSL".  If you modify this file, you may extend this exception
24  * to your version of the file, but you are not obligated to do so.  If you
25  * do not wish to do so, delete this exception statement from your version
26  * of this file.
27  *
28  * You should have received a copy of the GNU General Public License
29  * along with this package; if not, write to the Free Software
30  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
31  * 02111-1307, USA.
32  *
33  * $Id: util_parse.c 2864 2010-03-16 22:48:25Z kuhlmann $
34  */
35 
36 #include "climm.h"
37 #include "util_str.h"
38 #include "util_parse.h"
39 #include "contact.h"
40 #include "connection.h"
41 
42 /*
43  * Parses the next word, respecting double quotes, and interpreting
44  * \x##, \# and comments (#).
45  */
s_parse_s(const char ** input,const char * sep)46 strc_t s_parse_s (const char **input, const char *sep)
47 {
48     static str_s str;
49     strc_t parsed;
50     const char *p = *input;
51     char *q;
52     int s = 0;
53 
54     while (*p && strchr (sep, *p))
55         p++;
56     if (*p == '#')
57     {
58         while (*p)
59             p++;
60     }
61     *input = p;
62     if (!*p)
63         return NULL;
64 
65     s_init (&str, p, 0);
66 
67     q = str.txt;
68     parsed = &str;
69     if (!q)
70         return NULL;
71 
72     if (*p == '"')
73     {
74         s = 1;
75         p++;
76     }
77     while (*p)
78     {
79         if (*p == '\\')
80         {
81             p++;
82             if (*p == 'x' && p[1] && p[2])
83             {
84                 p++;
85                 *q = (*p >= '0' && *p <= '9' ? *p - '0' : *p >= 'a' && *p <= 'f' ? *p - 'a' + 10 : *p - 'A' + 10) << 4;
86                 p++;
87                 *q |= *p >= '0' && *p <= '9' ? *p - '0' : *p >= 'a' && *p <= 'f' ? *p - 'a' + 10 : *p - 'A' + 10;
88                 p++, q++;
89                 continue;
90             }
91             else if (*p)
92             {
93                 *(q++) = *(p++);
94                 continue;
95             }
96         }
97         if (*p == '"' && s)
98         {
99             *q = '\0';
100             *input = p + 1;
101             str.len = q - str.txt;
102             return parsed;
103         }
104         if (!s && strchr (sep, *p))
105             break;
106         *(q++) = *(p++);
107     }
108     *q = '\0';
109     *input = p;
110     str.len = q - str.txt;
111     return parsed;
112 }
113 
is_valid_icq_name(char * t)114 int is_valid_icq_name (char *t)
115 {
116     if (!*t)
117         return 0;
118     for ( ; *t; t++)
119         if (*t < '0' || *t > '9')
120             return 0;
121     return 1;
122 }
123 
is_valid_aim_name(char * t)124 int is_valid_aim_name (char *t)
125 {
126     if (!*t)
127         return 0;
128     if (*t >= '0' && *t <= '9')
129         return 0;
130     for ( ; *t; t++)
131     {
132         if ((*t < '0' || *t > '9') && (*t < 'a' || *t > 'z') && (*t < 'A' || *t > 'Z'))
133             return 0;
134         if (*t >= 'A' && *t <= 'Z')
135             t += 'a' - 'A';
136     }
137     return 1;
138 }
139 
is_valid_xmpp_name(char * txt)140 int is_valid_xmpp_name (char *txt)
141 {
142     char *at, *slash;
143 
144     if (!*txt)
145         return 0;
146     if (!(at = strchr (txt, '@')))
147         return 0;
148     if (strchr (at + 1, '@'))
149         return 0;
150     slash = strchr (txt, '/');
151     if (slash && slash != strchr (at, '/'))
152         return 0;
153     return 1;
154 }
155 
is_valid_msn_name(char * txt)156 int is_valid_msn_name (char *txt)
157 {
158     char *at;
159 
160     if (!*txt)
161         return 0;
162     if (!(at = strchr (txt, '@')))
163         return 0;
164     if (strchr (at + 1, '@'))
165         return 0;
166     return 1;
167 }
168 
169 /*
170  * Parses a nick, UIN or screen name.
171  */
s_parsenick_s(const char ** input,const char * sep,BOOL any,Server * serv,const char ** alias)172 Contact *s_parsenick_s (const char **input, const char *sep, BOOL any, Server *serv, const char **alias)
173 {
174     ContactGroup *cg;
175     Contact *r, *parsed;
176     const char *p = *input;
177     strc_t t;
178     UDWORD max, l, ll, i;
179 
180     while (*p && strchr (sep, *p))
181         p++;
182     *input = p;
183     if (!*p)
184         return NULL;
185 
186     if ((t = s_parse_s (&p, sep)) && strncmp (t->txt, *input, t->len))
187     {
188         if ((parsed = ContactFind (serv, t->txt)))
189         {
190             *input = p;
191             if (alias)
192                 *alias = t->txt;
193             return parsed;
194         }
195     }
196     p = *input;
197 
198     if (serv->type == TYPE_SERVER && !strncasecmp (p, "AIM:", 4))
199     {
200         const char *pp;
201         p += 4;
202         pp = p;
203         t = s_parse (&p);
204         if (t && (*pp == '"' || is_valid_aim_name (t->txt)))
205         {
206             *input = p;
207             if (alias)
208                 *alias = t->txt;
209             return ContactScreen (serv, t->txt);
210         }
211         p = *input;
212     }
213 
214     if (serv->type == TYPE_MSN_SERVER && !strncasecmp (p, "MSN:", 4))
215     {
216         p += 4;
217         t = s_parse (&p);
218         if (t && is_valid_msn_name (t->txt))
219         {
220             *input = p;
221             if (alias)
222                 *alias = t->txt;
223             return ContactScreen (serv, t->txt);
224         }
225         p = *input;
226     }
227 
228     if (serv->type == TYPE_XMPP_SERVER &&
229         (!strncasecmp (p, "JABBER:", 7) || !strncasecmp (p, "XMPP:", 5)))
230     {
231         p += (*p == 'J') ? 7 : 5;
232         t = s_parse (&p);
233         if (t && is_valid_xmpp_name (t->txt))
234         {
235             *input = p;
236             if (alias)
237                 *alias = t->txt;
238             return ContactScreen (serv, t->txt);
239         }
240         p = *input;
241     }
242 
243     if (serv->type == TYPE_SERVER && !strncasecmp (p, "ICQ:", 4))
244     {
245         p += 4;
246         t = s_parse (&p);
247         if (t && is_valid_icq_name (t->txt))
248         {
249             *input = p;
250             if (alias)
251                 *alias = t->txt;
252             return ContactScreen (serv, t->txt);
253         }
254         p = *input;
255     }
256 
257     max = 0;
258     parsed = NULL;
259     ll = strlen (p);
260     cg = serv->contacts;
261     for (i = 0; (r = ContactIndex (cg, i)); i++)
262     {
263         ContactAlias *ca;
264 
265         l = strlen (r->nick);
266         if (l > max && l <= ll && (!p[l] || strchr (sep, p[l])) && !strncasecmp (p, r->nick, l))
267         {
268             parsed = r;
269             max = strlen (r->nick);
270             if (alias)
271                 *alias = r->nick;
272         }
273 
274         for (ca = r->alias; ca; ca = ca->more)
275         {
276             l = strlen (ca->alias);
277             if (l > max && l <= ll && (!p[l] || strchr (sep, p[l])) && !strncasecmp (p, ca->alias, l))
278             {
279                 parsed = r;
280                 max = strlen (ca->alias);
281                 if (alias)
282                     *alias = ca->alias;
283             }
284         }
285     }
286     if (max)
287     {
288         *input = p + max;
289         return parsed;
290     }
291 
292     t = s_parse (&p);
293     if (!t)
294         return NULL;
295 
296     if (serv->type == TYPE_MSN_SERVER && is_valid_msn_name (t->txt))
297     {
298         *input = p;
299         if (alias)
300             *alias = t->txt;
301         return ContactScreen (serv, t->txt);
302     }
303 
304     if (serv->type == TYPE_XMPP_SERVER && is_valid_xmpp_name (t->txt))
305     {
306         *input = p;
307         if (alias)
308             *alias = t->txt;
309         return ContactScreen (serv, t->txt);
310     }
311 
312     if (serv->type == TYPE_SERVER && is_valid_icq_name (t->txt))
313     {
314         *input = p;
315         if (alias)
316             *alias = t->txt;
317         return ContactScreen (serv, t->txt);
318     }
319 
320     if (!any)
321         return NULL;
322 
323     for (i = 0; (serv = ServerNr (i)); i++)
324         if ((r = s_parsenick_s (input, sep, 0, serv, alias)))
325             return r;
326 
327     return NULL;
328 }
329 
330 /*
331  * Parses a contact group by name.
332  */
s_parsecg_s(const char ** input,const char * sep,BOOL any,Server * serv)333 ContactGroup *s_parsecg_s (const char **input, const char *sep, BOOL any, Server *serv)
334 {
335     ContactGroup *cg;
336     const char *p = *input;
337     strc_t t;
338     UDWORD l, i;
339 
340     while (*p && strchr (sep, *p))
341         p++;
342     *input = p;
343 
344     if (!*p)
345         return NULL;
346 
347     if ((t = s_parse_s (&p, sep)))
348     {
349         for (i = 0; (cg = ContactGroupIndex (i)); i++)
350         {
351             if (cg->serv == serv && cg->name && !strcmp (cg->name, t->txt))
352             {
353                 *input = p;
354                 return cg;
355             }
356         }
357     }
358     p = *input;
359 
360     for (i = 0; (cg = ContactGroupIndex (i)); i++)
361     {
362         if (!cg->name || !*cg->name)
363             continue;
364         l = strlen (cg->name);
365         if (cg->serv == serv && !strncmp (cg->name, p, l)
366             && (strchr (sep, p[l]) || !p[l]))
367         {
368             *input = p + l + (p[l] ? 1 : 0);
369             return cg;
370         }
371     }
372     if (!any)
373         return NULL;
374 
375     for (i = 0; (serv = ServerNr (i)); i++)
376         if ((cg = s_parsecg_s (input, sep, 0, serv)))
377             return cg;
378 
379     return NULL;
380 }
381 
382 /*
383  * Parses nicks and contact groups.
384  */
s_parselist_s(const char ** input,BOOL rem,BOOL any,Server * serv)385 ContactGroup *s_parselist_s (const char **input, BOOL rem, BOOL any, Server *serv)
386 {
387     static ContactGroup *scg = NULL;
388     ContactGroup *cg;
389     Contact *cont;
390     const char *p = *input;
391     strc_t par;
392     UDWORD i, one = 0;
393 
394     if (scg)
395         ContactGroupD (scg);
396     scg = ContactGroupC (NULL, 0, "");
397     scg->temp = 1; /* ContactD will not compact contacts on temporary lists! */
398     while (*p)
399     {
400         while (*p && strchr (DEFAULT_SEP, *p))
401             p++;
402         if ((cg = s_parsecg_s (&p, MULTI_SEP, 0, serv)))
403         {
404             for (i = 0; (cont = ContactIndex (cg, i)); i++)
405                 if (!ContactHas (scg, cont))
406                     ContactAdd (scg, cont);
407         }
408         else if ((cont = s_parsenick_s (&p, MULTI_SEP, 0, serv, NULL)))
409         {
410             if (!ContactHas (scg, cont))
411                 ContactAdd (scg, cont);
412         }
413         else if (any && (cg = s_parsecg_s (&p, MULTI_SEP, 1, serv)))
414         {
415             for (i = 0; (cont = ContactIndex (cg, i)); i++)
416                 if (!ContactHas (scg, cont))
417                     ContactAdd (scg, cont);
418         }
419         else if (any && (cont = s_parsenick_s (&p, MULTI_SEP, 1, serv, NULL)))
420         {
421             if (!ContactHas (scg, cont))
422                 ContactAdd (scg, cont);
423         }
424         else if ((par = s_parse (&p)))
425         {
426             rl_printf (i18n (2523, "%s not recognized as a nick name.\n"), s_wordquote (par->txt));
427             if (!rem)
428                 break;
429             one = 1;
430             continue;
431         }
432         else
433             break;
434         if (!rem && *p != ',')
435             break;
436         if (*p)
437             p++;
438     }
439 
440     if ((one || ContactIndex (scg, 0)) && (rem || strchr (DEFAULT_SEP, *p)))
441     {
442         *input = p;
443         return scg;
444     }
445     return NULL;
446 }
447 
448 /*
449  * Parses the remainder of the input, translates \x## and \#.
450  */
s_parserem_s(const char ** input,const char * sep)451 char *s_parserem_s (const char **input, const char *sep)
452 {
453     static char *t = NULL;
454     const char *p = *input;
455 
456     while (*p && strchr (sep, *p))
457         p++;
458     *input = p;
459     if (!*p)
460         return NULL;
461 
462     s_repl (&t, p);
463     while (*p)
464         p++;
465     return t;
466 }
467 
468 /*
469  * Parses a number.
470  */
s_parseint_s(const char ** input,UDWORD * parsed,const char * sep,int flags)471 const char *s_parseint_s (const char **input, UDWORD *parsed, const char *sep, int flags)
472 {
473     static str_s str;
474     const char *p = *input;
475     const char *st;
476     UDWORD nr, sig;
477 
478     while (*p && strchr (sep, *p))
479         p++;
480     *input = p;
481     if (*p == '#' && flags & 4)
482     {
483         while (*p)
484             p++;
485     }
486     if (!*p)
487     {
488         *parsed = 0;
489         return NULL;
490     }
491 
492     nr = 0;
493     sig = 1;
494 
495     st = p;
496     if (*p == '+')
497     {
498         p++;
499         while (*p == '0')
500             p++;
501         st = p;
502     }
503     else if (*p == '-' && flags & 1)
504     {
505         sig = -1;
506         p++;
507     }
508     s_init (&str, st, 0);
509 
510     while (*p && *p >= '0' && *p <= '9')
511     {
512         nr = nr * 10 + (*p - '0');
513         p++;
514     }
515     if (!nr && *p == 'x' && flags & 2)
516     {
517         p++;
518         while (*p && ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F')))
519         {
520             nr = nr * 16 + (*p >= '0' && *p <= '9' ? *p - '0' : *p >= 'a' && *p <= 'f' ? *p - 'a' + 10 : *p - 'A' + 10);
521             p++;
522         }
523     }
524     if (*p && !strchr (sep, *p))
525     {
526         *parsed = 0;
527         return NULL;
528     }
529     *input = p;
530     *parsed = nr * sig;
531     str.txt[p - st] = 0;
532     return str.txt;
533 }
534 
535 /*
536  * Parses a keyword.
537  */
s_parsekey_s(const char ** input,const char * keyword,const char * sep)538 BOOL s_parsekey_s (const char **input, const char *keyword, const char *sep)
539 {
540     const char *p = *input;
541 
542     while (*p && strchr (sep, *p))
543         p++;
544     *input = p;
545 
546     while (*keyword == *p && *p)
547         keyword++, p++;
548 
549     if (*keyword)
550         return FALSE;
551 
552     if (*p && !strchr (sep, *p))
553         return FALSE;
554 
555     *input = p;
556     return TRUE;
557 }
558 
559