]> mj.ucw.cz Git - libucw.git/blob - lib/urlkey.c
e6cc55d66fb1f55de7c9ef5c4cbdea56571bbc04
[libucw.git] / lib / urlkey.c
1 /*
2  *      Sherlock Library -- URL Keys & URL Fingerprints
3  *
4  *      (c) 2003 Martin Mares <mj@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #undef LOCAL_DEBUG
11
12 #include "lib/lib.h"
13 #include "lib/conf.h"
14 #include "lib/index.h"
15 #include "lib/url.h"
16 #include "lib/fastbuf.h"
17 #include "lib/chartype.h"
18 #include "lib/hashfunc.h"
19
20 #include <string.h>
21 #include <fcntl.h>
22
23 /*** Prefix recognition table ***/
24
25 struct pxtab_rhs {
26   struct pxtab_node *node;
27   uns len;
28   byte rhs[1];
29 };
30
31 struct pxtab_node {
32   struct pxtab_node *parent;
33   struct pxtab_rhs *rhs;
34   uns len, total_len;
35   byte component[0];
36 };
37
38 #define HASH_NODE struct pxtab_node
39 #define HASH_PREFIX(p) pxtab_##p
40 #define HASH_KEY_COMPLEX(x) x parent, x component, x len
41 #define HASH_KEY_DECL struct pxtab_node *parent UNUSED, byte *component UNUSED, uns len UNUSED
42 #define HASH_WANT_FIND
43 #define HASH_WANT_LOOKUP
44 #define HASH_GIVE_HASHFN
45 #define HASH_GIVE_EQ
46 #define HASH_GIVE_EXTRA_SIZE
47 #define HASH_GIVE_INIT_KEY
48 #define HASH_GIVE_ALLOC
49
50 static inline uns
51 pxtab_hash(HASH_KEY_DECL)
52 {
53   return ((uns)parent) ^ hash_block(component, len);
54 }
55
56 static inline int
57 pxtab_eq(struct pxtab_node *p1, byte *c1, uns l1, struct pxtab_node *p2, byte *c2, uns l2)
58 {
59   return p1 == p2 && l1 == l2 && !memcmp(c1, c2, l1);
60 }
61
62 static inline int
63 pxtab_extra_size(HASH_KEY_DECL)
64 {
65   return len;
66 }
67
68 static inline void
69 pxtab_init_key(struct pxtab_node *node, HASH_KEY_DECL)
70 {
71   node->parent = parent;
72   node->len = len;
73   memcpy(node->component, component, len);
74   node->rhs = NULL;
75 }
76
77 static inline void *
78 pxtab_alloc(uns size)
79 {
80   return cfg_malloc(size);
81 }
82
83 #include "lib/hashtable.h"
84
85 static inline byte *
86 pxtab_skip_first_comp(byte *x)
87 {
88   while (*x && *x != ':')
89     x++;
90   byte *y = x;
91   while (*x != '/' || x[1] != '/')
92     {
93       if (!*x)
94         return y;
95       x++;
96     }
97   return x+2;
98 }
99
100 static inline byte *
101 pxtab_skip_next_comp(byte *x)
102 {
103   for(;;)
104     {
105       if (!*x)
106         return x;
107       if (*x == '/')
108         return x+1;
109       x++;
110     }
111 }
112
113 static struct pxtab_node *
114 pxtab_find_rule(byte *lhs)
115 {
116   byte *next;
117   struct pxtab_node *node, *parent = NULL;
118
119   next = pxtab_skip_first_comp(lhs);
120   DBG("\tfirst: %.*s", next-lhs, lhs);
121   node = pxtab_find(NULL, lhs, next-lhs);
122   while (node && *next)
123     {
124       parent = node;
125       lhs = next;
126       next = pxtab_skip_next_comp(lhs);
127       DBG("\tnext: %.*s", next-lhs, lhs);
128       node = pxtab_find(parent, lhs, next-lhs);
129     }
130   return node ? : parent;
131 }
132
133 static struct pxtab_node *
134 pxtab_add_rule(byte *lhs, struct pxtab_rhs *rhs)
135 {
136   byte *lhs_start = lhs;
137   byte *next;
138   struct pxtab_node *node, *parent;
139
140   next = pxtab_skip_first_comp(lhs);
141   DBG("\tfirst: %.*s", next-lhs, lhs);
142   node = pxtab_lookup(NULL, lhs, next-lhs);
143   for(;;)
144     {
145       if (node->rhs)
146         return NULL;
147       if (!*next)
148         break;
149       lhs = next;
150       next = pxtab_skip_next_comp(lhs);
151       parent = node;
152       DBG("\tnext: %.*s", next-lhs, lhs);
153       node = pxtab_lookup(parent, lhs, next-lhs);
154     }
155   DBG("\tsetting rhs, %d to eat", next-lhs_start);
156   node->rhs = rhs;
157   node->total_len = next - lhs_start;
158   return node;
159 }
160
161 static struct pxtab_rhs *
162 pxtab_add_rhs(byte *rhs)
163 {
164   uns len = strlen(rhs);
165   struct pxtab_rhs *r = cfg_malloc(sizeof(*r) + len);
166   r->len = len;
167   memcpy(r->rhs, rhs, len+1);
168   struct pxtab_node *node = pxtab_add_rule(rhs, r);
169   r->node = node;
170   return r;
171 }
172
173 static void
174 pxtab_load(byte *name)
175 {
176   struct fastbuf *f;
177   struct pxtab_rhs *rhs = NULL;
178   byte line[MAX_URL_SIZE], url[MAX_URL_SIZE], *c, *d;
179   int err;
180   int lino = 0;
181
182   DBG("Loading prefix table %s", name);
183   f = bopen(name, O_RDONLY, 4096);
184   while (bgets(f, line, sizeof(line)))
185     {
186       lino++;
187       c = line;
188       while (Cblank(*c))
189         c++;
190       if (!*c || *c == '#')
191         continue;
192       if (err = url_auto_canonicalize(c, url))
193         die("%s, line %d: Invalid URL (%s)", name, lino, url_error(err));
194       if (!(d = strrchr(c, '/')) || d[1])
195         die("%s, line %d: Prefix rules must end with a slash", name, lino);
196       if (c == line)
197         {
198           DBG("Creating RHS <%s>", c);
199           if (!(rhs = pxtab_add_rhs(c)))
200             die("%s, line %d: Right-hand side already mapped", name, lino);
201         }
202       else if (!rhs)
203         die("%s, line %d: Syntax error", name, lino);
204       else
205         {
206           DBG("Adding LHS <%s>", c);
207           if (!pxtab_add_rule(c, rhs))
208             die("%s, line %d: Duplicate rule", name, lino);
209         }
210     }
211   bclose(f);
212 }
213
214 /*** Configuration ***/
215
216 static uns urlkey_www_hack;
217 static byte *urlkey_pxtab_path;
218
219 static struct cfitem urlkey_config[] = {
220   { "URLKey",           CT_SECTION,     NULL },
221   { "WWWHack",          CT_INT,         &urlkey_www_hack },
222   { "PrefixTable",      CT_STRING,      &urlkey_pxtab_path },
223   { NULL,               CT_STOP,        NULL }
224 };
225
226 static void CONSTRUCTOR urlkey_conf_init(void)
227 {
228   cf_register(urlkey_config);
229 }
230
231 void
232 url_key_init(uns load_prefixes)
233 {
234   pxtab_init();
235   if (load_prefixes && urlkey_pxtab_path)
236     pxtab_load(urlkey_pxtab_path);
237 }
238
239 static inline byte *
240 url_key_remove_www(byte *url, byte **pbuf)
241 {
242   if (urlkey_www_hack && !strncmp(url, "http://www.", 11))
243     {
244       byte *buf = *pbuf;
245       strcpy(buf, "http://");
246       strcpy(buf+7, url+11);
247       DBG("\tWWW hack: %s -> %s", url, buf);
248       url = buf;
249       *pbuf = buf + MAX_URL_SIZE;
250     }
251   return url;
252 }
253
254 byte *
255 url_key(byte *url, byte *buf)
256 {
257   DBG("Generating URL key for %s", url);
258   url = url_key_remove_www(url, &buf);
259   struct pxtab_node *rule = pxtab_find_rule(url);
260   if (rule && rule->rhs && rule->rhs->node != rule)
261     {
262       struct pxtab_rhs *rhs = rule->rhs;
263       DBG("\tApplying rule <%s>, remove %d, add %d", rhs->rhs, rule->total_len, rhs->len);
264       memcpy(buf, rhs->rhs, rhs->len);
265       strcpy(buf + rhs->len, url + rule->total_len);
266       url = buf;
267       buf += MAX_URL_SIZE;
268     }
269   url = url_key_remove_www(url, &buf);
270   DBG("\tOutput: %s", url);
271   return url;
272 }
273
274 void
275 url_fingerprint(byte *url, struct fingerprint *fp)
276 {
277   byte buf[URL_KEY_BUF_SIZE];
278   return fingerprint(url_key(url, buf), fp);
279 }