Extending test-client-custom-summary to try e_book_client_get_contacts_uids()
[platform/upstream/evolution-data-server.git] / camel / camel-url-scanner.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
4  *
5  *  Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU Lesser General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <ctype.h>
28 #include <stdio.h>
29 #include <string.h>
30
31 #include "camel-trie.h"
32 #include "camel-url-scanner.h"
33 #include "camel-utf8.h"
34
35 struct _CamelUrlScanner {
36         GPtrArray *patterns;
37         CamelTrie *trie;
38 };
39
40 CamelUrlScanner *
41 camel_url_scanner_new (void)
42 {
43         CamelUrlScanner *scanner;
44
45         scanner = g_new (CamelUrlScanner, 1);
46         scanner->patterns = g_ptr_array_new ();
47         scanner->trie = camel_trie_new (TRUE);
48
49         return scanner;
50 }
51
52 void
53 camel_url_scanner_free (CamelUrlScanner *scanner)
54 {
55         g_return_if_fail (scanner != NULL);
56
57         g_ptr_array_free (scanner->patterns, TRUE);
58         camel_trie_free (scanner->trie);
59         g_free (scanner);
60 }
61
62 void
63 camel_url_scanner_add (CamelUrlScanner *scanner,
64                        urlpattern_t *pattern)
65 {
66         g_return_if_fail (scanner != NULL);
67
68         camel_trie_add (scanner->trie, pattern->pattern, scanner->patterns->len);
69         g_ptr_array_add (scanner->patterns, pattern);
70 }
71
72 gboolean
73 camel_url_scanner_scan (CamelUrlScanner *scanner,
74                         const gchar *in,
75                         gsize inlen,
76                         urlmatch_t *match)
77 {
78         const gchar *pos;
79         const guchar *inptr, *inend;
80         urlpattern_t *pat;
81         gint pattern;
82
83         g_return_val_if_fail (scanner != NULL, FALSE);
84         g_return_val_if_fail (in != NULL, FALSE);
85
86         inptr = (const guchar *) in;
87         inend = inptr + inlen;
88
89         /* check validity of a string first */
90         if (!g_utf8_validate (in, inlen, NULL))
91                 return FALSE;
92
93         do {
94                 if (!(pos = camel_trie_search (scanner->trie, (const gchar *) inptr, inlen, &pattern)))
95                         return FALSE;
96
97                 pat = g_ptr_array_index (scanner->patterns, pattern);
98
99                 match->pattern = pat->pattern;
100                 match->prefix = pat->prefix;
101
102                 if (pat->start (in, pos, (const gchar *) inend, match) && pat->end (in, pos, (const gchar *) inend, match))
103                         return TRUE;
104
105                 inptr = (const guchar *) pos;
106                 if (camel_utf8_getc_limit (&inptr, inend) == 0xffff)
107                         break;
108
109                 inlen = inend - inptr;
110         } while (inptr < inend);
111
112         return FALSE;
113 }
114
115 static guchar url_scanner_table[256] = {
116           1,  1,  1,  1,  1,  1,  1,  1,  1,  9,  9,  1,  1,  9,  1,  1,
117           1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
118          24,128,160,128,128,128,128,128,160,160,128,128,160,192,160,160,
119          68, 68, 68, 68, 68, 68, 68, 68, 68, 68,160,160, 32,128, 32,128,
120         160, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
121          66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,160,160,160,128,128,
122         128, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
123          66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,128,128,128,128,  1,
124           1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
125           1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
126           1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
127           1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
128           1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
129           1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
130           1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
131           1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1
132 };
133
134 enum {
135         IS_CTRL         = (1 << 0),
136         IS_ALPHA        = (1 << 1),
137         IS_DIGIT        = (1 << 2),
138         IS_LWSP         = (1 << 3),
139         IS_SPACE        = (1 << 4),
140         IS_SPECIAL      = (1 << 5),
141         IS_DOMAIN       = (1 << 6),
142         IS_URLSAFE      = (1 << 7)
143 };
144
145 #define is_ctrl(x) ((url_scanner_table[(guchar)(x)] & IS_CTRL) != 0)
146 #define is_lwsp(x) ((url_scanner_table[(guchar)(x)] & IS_LWSP) != 0)
147 #define is_atom(x) ((url_scanner_table[(guchar)(x)] & (IS_SPECIAL|IS_SPACE|IS_CTRL)) == 0)
148 #define is_alpha(x) ((url_scanner_table[(guchar)(x)] & IS_ALPHA) != 0)
149 #define is_digit(x) ((url_scanner_table[(guchar)(x)] & IS_DIGIT) != 0)
150 #define is_domain(x) ((url_scanner_table[(guchar)(x)] & IS_DOMAIN) != 0)
151 #define is_urlsafe(x) ((url_scanner_table[(guchar)(x)] & (IS_ALPHA|IS_DIGIT|IS_URLSAFE)) != 0)
152
153 static const struct {
154         const gchar open;
155         const gchar close;
156 } url_braces[] = {
157         { '(', ')' },
158         { '{', '}' },
159         { '[', ']' },
160         { '<', '>' },
161         { '|', '|' },
162         { '\'', '\'' },
163 };
164
165 static gboolean
166 is_open_brace (gchar c)
167 {
168         gint i;
169
170         for (i = 0; i < G_N_ELEMENTS (url_braces); i++) {
171                 if (c == url_braces[i].open)
172                         return TRUE;
173         }
174
175         return FALSE;
176 }
177
178 static char
179 url_stop_at_brace (const gchar *in,
180                    gsize so,
181                    gchar *open_brace)
182 {
183         gint i;
184
185         if (open_brace != NULL)
186                 *open_brace = '\0';
187
188         if (so > 0) {
189                 for (i = 0; i < G_N_ELEMENTS (url_braces); i++) {
190                         if (in[so - 1] == url_braces[i].open) {
191                                 if (open_brace != NULL)
192                                         *open_brace = url_braces[i].open;
193                                 return url_braces[i].close;
194                         }
195                 }
196         }
197
198         return '\0';
199 }
200
201 gboolean
202 camel_url_addrspec_start (const gchar *in,
203                           const gchar *pos,
204                           const gchar *inend,
205                           urlmatch_t *match)
206 {
207         register const gchar *inptr = pos;
208
209         g_assert (*inptr == '@');
210
211         if (inptr > in)
212                 inptr--;
213
214         while (inptr > in) {
215                 if (is_atom (*inptr))
216                         inptr--;
217                 else
218                         break;
219
220                 while (inptr > in && is_atom (*inptr))
221                         inptr--;
222
223                 if (inptr > in && *inptr == '.')
224                         inptr--;
225         }
226
227         while (!is_atom (*inptr) || is_open_brace (*inptr))
228                 inptr++;
229
230         if (inptr >= pos)
231                 return FALSE;
232
233         match->um_so = (inptr - in);
234
235         return TRUE;
236 }
237
238 gboolean
239 camel_url_addrspec_end (const gchar *in,
240                         const gchar *pos,
241                         const gchar *inend,
242                         urlmatch_t *match)
243 {
244         const gchar *inptr = pos;
245         gint parts = 0, digits;
246         gboolean got_dot = FALSE;
247
248         g_assert (*inptr == '@');
249
250         inptr++;
251
252         if (*inptr == '[') {
253                 /* domain literal */
254                 do {
255                         inptr++;
256
257                         digits = 0;
258                         while (inptr < inend && is_digit (*inptr) && digits < 3) {
259                                 inptr++;
260                                 digits++;
261                         }
262
263                         parts++;
264
265                         if (*inptr != '.' && parts != 4)
266                                 return FALSE;
267                 } while (parts < 4);
268
269                 if (*inptr == ']')
270                         inptr++;
271                 else
272                         return FALSE;
273
274                 got_dot = TRUE;
275         } else {
276                 while (inptr < inend) {
277                         if (is_domain (*inptr))
278                                 inptr++;
279                         else
280                                 break;
281
282                         while (inptr < inend && is_domain (*inptr))
283                                 inptr++;
284
285                         if (inptr < inend && *inptr == '.' && is_domain (inptr[1])) {
286                                 if (*inptr == '.')
287                                         got_dot = TRUE;
288                                 inptr++;
289                         }
290                 }
291         }
292
293         /* don't allow toplevel domains */
294         if (inptr == pos + 1 || !got_dot)
295                 return FALSE;
296
297         match->um_eo = (inptr - in);
298
299         return TRUE;
300 }
301
302 gboolean
303 camel_url_file_start (const gchar *in,
304                       const gchar *pos,
305                       const gchar *inend,
306                       urlmatch_t *match)
307 {
308         match->um_so = (pos - in);
309
310         return TRUE;
311 }
312
313 gboolean
314 camel_url_file_end (const gchar *in,
315                     const gchar *pos,
316                     const gchar *inend,
317                     urlmatch_t *match)
318 {
319         register const gchar *inptr = pos;
320         gchar close_brace;
321
322         inptr += strlen (match->pattern);
323
324         if (*inptr == '/')
325                 inptr++;
326
327         close_brace = url_stop_at_brace (in, match->um_so, NULL);
328
329         while (inptr < inend && is_urlsafe (*inptr) && *inptr != close_brace)
330                 inptr++;
331
332         if (inptr == pos)
333                 return FALSE;
334
335         match->um_eo = (inptr - in);
336
337         return TRUE;
338 }
339
340 gboolean
341 camel_url_web_start (const gchar *in,
342                      const gchar *pos,
343                      const gchar *inend,
344                      urlmatch_t *match)
345 {
346         if (pos > in && !strncmp (pos, "www", 3)) {
347                 /* make sure we aren't actually part of another word */
348                 if (!is_open_brace (pos[-1]) && !isspace (pos[-1]))
349                         return FALSE;
350         }
351
352         match->um_so = (pos - in);
353
354         return TRUE;
355 }
356
357 gboolean
358 camel_url_web_end (const gchar *in,
359                    const gchar *pos,
360                    const gchar *inend,
361                    urlmatch_t *match)
362 {
363         register const gchar *inptr = pos;
364         gboolean passwd = FALSE;
365         const gchar *save;
366         gchar close_brace, open_brace;
367         gint brace_stack = 0;
368         gint port;
369
370         inptr += strlen (match->pattern);
371
372         close_brace = url_stop_at_brace (in, match->um_so, &open_brace);
373
374         /* find the end of the domain */
375         if (is_atom (*inptr)) {
376                 /* might be a domain or user@domain */
377                 save = inptr;
378                 while (inptr < inend) {
379                         if (!is_atom (*inptr))
380                                 break;
381
382                         inptr++;
383
384                         while (inptr < inend && is_atom (*inptr))
385                                 inptr++;
386
387                         if ((inptr + 1) < inend && *inptr == '.' && (is_atom (inptr[1]) || inptr[1] == '/'))
388                                         inptr++;
389                 }
390
391                 if (*inptr != '@')
392                         inptr = save;
393                 else
394                         inptr++;
395
396                 goto domain;
397         } else if (is_domain (*inptr)) {
398         domain:
399                 while (inptr < inend) {
400                         if (!is_domain (*inptr))
401                                 break;
402
403                         inptr++;
404
405                         while (inptr < inend && is_domain (*inptr))
406                                 inptr++;
407
408                         if ((inptr + 1) < inend && *inptr == '.' && (is_domain (inptr[1]) || inptr[1] == '/'))
409                                         inptr++;
410                 }
411         } else {
412                 return FALSE;
413         }
414
415         if (inptr < inend) {
416                 switch (*inptr) {
417                 case ':': /* we either have a port or a password */
418                         inptr++;
419
420                         if (is_digit (*inptr) || passwd) {
421                                 port = (*inptr++ - '0');
422
423                                 while (inptr < inend && is_digit (*inptr) && port < 65536)
424                                         port = (port * 10) + (*inptr++ - '0');
425
426                                 if (!passwd && (port >= 65536 || *inptr == '@')) {
427                                         if (inptr < inend) {
428                                                 /* this must be a password? */
429                                                 goto passwd;
430                                         }
431
432                                         inptr--;
433                                 }
434                         } else {
435                         passwd:
436                                 passwd = TRUE;
437                                 save = inptr;
438
439                                 while (inptr < inend && is_atom (*inptr))
440                                         inptr++;
441
442                                 if ((inptr + 2) < inend) {
443                                         if (*inptr == '@') {
444                                                 inptr++;
445                                                 if (is_domain (*inptr))
446                                                         goto domain;
447                                         }
448
449                                         return FALSE;
450                                 }
451                         }
452
453                         if (inptr >= inend || *inptr != '/')
454                                 break;
455
456                         /* we have a '/' so there could be a path - fall through */
457                 case '/': /* we've detected a path component to our url */
458                         inptr++;
459                 case '?':
460                         while (inptr < inend && is_urlsafe (*inptr)) {
461                                 if (*inptr == open_brace) {
462                                         brace_stack++;
463                                 } else if (*inptr == close_brace) {
464                                         brace_stack--;
465                                         if (brace_stack == -1)
466                                                 break;
467                                 }
468                                 inptr++;
469                         }
470
471                         break;
472                 default:
473                         break;
474                 }
475         }
476
477         /* urls are extremely unlikely to end with any
478          * punctuation, so strip any trailing
479          * punctuation off. Also strip off any closing
480          * double-quotes. */
481         while (inptr > pos && strchr (",.:;?!-|}])\"", inptr[-1]))
482                 inptr--;
483
484         match->um_eo = (inptr - in);
485
486         return TRUE;
487 }
488
489 #ifdef BUILD_TABLE
490
491 /* got these from rfc1738 */
492 #define CHARS_LWSP " \t\n\r"               /* linear whitespace chars */
493 #define CHARS_SPECIAL "()<>@,;:\\\".[]"
494
495 /* got these from rfc1738 */
496 #define CHARS_URLSAFE "$-_.+!*'(),{}|\\^~[]`#%\";/?:@&="
497
498 static void
499 table_init_bits (guint mask,
500                  const guchar *vals)
501 {
502         gint i;
503
504         for (i = 0; vals[i] != '\0'; i++)
505                 url_scanner_table[vals[i]] |= mask;
506 }
507
508 static void
509 url_scanner_table_init (void)
510 {
511         gint i;
512
513         for (i = 0; i < 256; i++) {
514                 url_scanner_table[i] = 0;
515                 if (i < 32)
516                         url_scanner_table[i] |= IS_CTRL;
517                 if ((i >= '0' && i <= '9'))
518                         url_scanner_table[i] |= IS_DIGIT | IS_DOMAIN;
519                 if ((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z'))
520                         url_scanner_table[i] |= IS_ALPHA | IS_DOMAIN;
521                 if (i >= 127)
522                         url_scanner_table[i] |= IS_CTRL;
523         }
524
525         url_scanner_table[' '] |= IS_SPACE;
526         url_scanner_table['-'] |= IS_DOMAIN;
527
528         /* not defined to be special in rfc0822, but when scanning
529          * backwards to find the beginning of the email address we do
530          * not want to include this gchar if we come accross it - so
531          * this is kind of a hack */
532         url_scanner_table['/'] |= IS_SPECIAL;
533
534         table_init_bits (IS_LWSP, CHARS_LWSP);
535         table_init_bits (IS_SPECIAL, CHARS_SPECIAL);
536         table_init_bits (IS_URLSAFE, CHARS_URLSAFE);
537 }
538
539 gint main (gint argc, gchar **argv)
540 {
541         gint i;
542
543         url_scanner_table_init ();
544
545         printf ("static guchar url_scanner_table[256] = {");
546         for (i = 0; i < 256; i++) {
547                 printf (
548                         "%s%3d%s", (i % 16) ? "" : "\n\t",
549                         url_scanner_table[i], i != 255 ? "," : "\n");
550         }
551         printf ("};\n\n");
552
553         return 0;
554 }
555
556 #endif /* BUILD_TABLE */