1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Authors: Jeffrey Stedfast <fejj@ximian.com>
5 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
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.
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.
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.
31 #include "camel-trie.h"
32 #include "camel-url-scanner.h"
33 #include "camel-utf8.h"
35 struct _CamelUrlScanner {
41 camel_url_scanner_new (void)
43 CamelUrlScanner *scanner;
45 scanner = g_new (CamelUrlScanner, 1);
46 scanner->patterns = g_ptr_array_new ();
47 scanner->trie = camel_trie_new (TRUE);
53 camel_url_scanner_free (CamelUrlScanner *scanner)
55 g_return_if_fail (scanner != NULL);
57 g_ptr_array_free (scanner->patterns, TRUE);
58 camel_trie_free (scanner->trie);
63 camel_url_scanner_add (CamelUrlScanner *scanner,
64 urlpattern_t *pattern)
66 g_return_if_fail (scanner != NULL);
68 camel_trie_add (scanner->trie, pattern->pattern, scanner->patterns->len);
69 g_ptr_array_add (scanner->patterns, pattern);
73 camel_url_scanner_scan (CamelUrlScanner *scanner,
79 const guchar *inptr, *inend;
83 g_return_val_if_fail (scanner != NULL, FALSE);
84 g_return_val_if_fail (in != NULL, FALSE);
86 inptr = (const guchar *) in;
87 inend = inptr + inlen;
89 /* check validity of a string first */
90 if (!g_utf8_validate (in, inlen, NULL))
94 if (!(pos = camel_trie_search (scanner->trie, (const gchar *) inptr, inlen, &pattern)))
97 pat = g_ptr_array_index (scanner->patterns, pattern);
99 match->pattern = pat->pattern;
100 match->prefix = pat->prefix;
102 if (pat->start (in, pos, (const gchar *) inend, match) && pat->end (in, pos, (const gchar *) inend, match))
105 inptr = (const guchar *) pos;
106 if (camel_utf8_getc_limit (&inptr, inend) == 0xffff)
109 inlen = inend - inptr;
110 } while (inptr < inend);
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
140 IS_SPECIAL = (1 << 5),
141 IS_DOMAIN = (1 << 6),
142 IS_URLSAFE = (1 << 7)
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)
153 static const struct {
166 is_open_brace (gchar c)
170 for (i = 0; i < G_N_ELEMENTS (url_braces); i++) {
171 if (c == url_braces[i].open)
179 url_stop_at_brace (const gchar *in,
185 if (open_brace != NULL)
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;
202 camel_url_addrspec_start (const gchar *in,
207 register const gchar *inptr = pos;
209 g_assert (*inptr == '@');
215 if (is_atom (*inptr))
220 while (inptr > in && is_atom (*inptr))
223 if (inptr > in && *inptr == '.')
227 while (!is_atom (*inptr) || is_open_brace (*inptr))
233 match->um_so = (inptr - in);
239 camel_url_addrspec_end (const gchar *in,
244 const gchar *inptr = pos;
245 gint parts = 0, digits;
246 gboolean got_dot = FALSE;
248 g_assert (*inptr == '@');
258 while (inptr < inend && is_digit (*inptr) && digits < 3) {
265 if (*inptr != '.' && parts != 4)
276 while (inptr < inend) {
277 if (is_domain (*inptr))
282 while (inptr < inend && is_domain (*inptr))
285 if (inptr < inend && *inptr == '.' && is_domain (inptr[1])) {
293 /* don't allow toplevel domains */
294 if (inptr == pos + 1 || !got_dot)
297 match->um_eo = (inptr - in);
303 camel_url_file_start (const gchar *in,
308 match->um_so = (pos - in);
314 camel_url_file_end (const gchar *in,
319 register const gchar *inptr = pos;
322 inptr += strlen (match->pattern);
327 close_brace = url_stop_at_brace (in, match->um_so, NULL);
329 while (inptr < inend && is_urlsafe (*inptr) && *inptr != close_brace)
335 match->um_eo = (inptr - in);
341 camel_url_web_start (const gchar *in,
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]))
352 match->um_so = (pos - in);
358 camel_url_web_end (const gchar *in,
363 register const gchar *inptr = pos;
364 gboolean passwd = FALSE;
366 gchar close_brace, open_brace;
367 gint brace_stack = 0;
370 inptr += strlen (match->pattern);
372 close_brace = url_stop_at_brace (in, match->um_so, &open_brace);
374 /* find the end of the domain */
375 if (is_atom (*inptr)) {
376 /* might be a domain or user@domain */
378 while (inptr < inend) {
379 if (!is_atom (*inptr))
384 while (inptr < inend && is_atom (*inptr))
387 if ((inptr + 1) < inend && *inptr == '.' && (is_atom (inptr[1]) || inptr[1] == '/'))
397 } else if (is_domain (*inptr)) {
399 while (inptr < inend) {
400 if (!is_domain (*inptr))
405 while (inptr < inend && is_domain (*inptr))
408 if ((inptr + 1) < inend && *inptr == '.' && (is_domain (inptr[1]) || inptr[1] == '/'))
417 case ':': /* we either have a port or a password */
420 if (is_digit (*inptr) || passwd) {
421 port = (*inptr++ - '0');
423 while (inptr < inend && is_digit (*inptr) && port < 65536)
424 port = (port * 10) + (*inptr++ - '0');
426 if (!passwd && (port >= 65536 || *inptr == '@')) {
428 /* this must be a password? */
439 while (inptr < inend && is_atom (*inptr))
442 if ((inptr + 2) < inend) {
445 if (is_domain (*inptr))
453 if (inptr >= inend || *inptr != '/')
456 /* we have a '/' so there could be a path - fall through */
457 case '/': /* we've detected a path component to our url */
460 while (inptr < inend && is_urlsafe (*inptr)) {
461 if (*inptr == open_brace) {
463 } else if (*inptr == close_brace) {
465 if (brace_stack == -1)
477 /* urls are extremely unlikely to end with any
478 * punctuation, so strip any trailing
479 * punctuation off. Also strip off any closing
481 while (inptr > pos && strchr (",.:;?!-|}])\"", inptr[-1]))
484 match->um_eo = (inptr - in);
491 /* got these from rfc1738 */
492 #define CHARS_LWSP " \t\n\r" /* linear whitespace chars */
493 #define CHARS_SPECIAL "()<>@,;:\\\".[]"
495 /* got these from rfc1738 */
496 #define CHARS_URLSAFE "$-_.+!*'(),{}|\\^~[]`#%\";/?:@&="
499 table_init_bits (guint mask,
504 for (i = 0; vals[i] != '\0'; i++)
505 url_scanner_table[vals[i]] |= mask;
509 url_scanner_table_init (void)
513 for (i = 0; i < 256; i++) {
514 url_scanner_table[i] = 0;
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;
522 url_scanner_table[i] |= IS_CTRL;
525 url_scanner_table[' '] |= IS_SPACE;
526 url_scanner_table['-'] |= IS_DOMAIN;
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;
534 table_init_bits (IS_LWSP, CHARS_LWSP);
535 table_init_bits (IS_SPECIAL, CHARS_SPECIAL);
536 table_init_bits (IS_URLSAFE, CHARS_URLSAFE);
539 gint main (gint argc, gchar **argv)
543 url_scanner_table_init ();
545 printf ("static guchar url_scanner_table[256] = {");
546 for (i = 0; i < 256; i++) {
548 "%s%3d%s", (i % 16) ? "" : "\n\t",
549 url_scanner_table[i], i != 255 ? "," : "\n");
556 #endif /* BUILD_TABLE */