/*
* Sherlock Library -- URL Functions (according to RFC 1738 and 1808)
*
- * (c) 1997--1999 Martin Mares <mj@ucw.cz>
+ * (c) 1997--2001 Martin Mares <mj@ucw.cz>
+ * (c) 2001 Robert Spalek <robert@ucw.cz>
*/
#include "lib/lib.h"
#include "lib/url.h"
#include "lib/chartype.h"
+#include "lib/conf.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
+/* Configuration */
+
+static uns url_ignore_spaces;
+static uns url_ignore_underflow;
+static byte *url_component_separators = "";
+static uns url_min_repeat_count = 0x7fffffff;
+static uns url_max_repeat_length = 0;
+
+static struct cfitem url_config[] = {
+ { "URL", CT_SECTION, NULL },
+ { "IgnoreSpaces", CT_INT, &url_ignore_spaces },
+ { "IgnoreUnderflow", CT_INT, &url_ignore_underflow },
+ { "ComponentSeparators", CT_STRING, &url_component_separators },
+ { "MinRepeatCount", CT_INT, &url_min_repeat_count },
+ { "MaxRepeatLength", CT_INT, &url_max_repeat_length },
+ { NULL, CT_STOP, NULL }
+};
+
+static void CONSTRUCTOR url_init_config(void)
+{
+ cf_register(url_config);
+}
+
/* Escaping and de-escaping */
static uns
int
url_deescape(byte *s, byte *d)
{
+ byte *dstart = d;
byte *end = d + MAX_URL_SIZE - 10;
while (*s)
{
*d++ = val;
s += 3;
}
- else if (*s >= 0x20)
+ else if (*s > 0x20)
*d++ = *s++;
+ else if (Cspace(*s))
+ {
+ byte *s0 = s;
+ while (Cspace(*s))
+ s++;
+ if (!url_ignore_spaces || !(!*s || d == dstart))
+ {
+ while (Cspace(*s0))
+ {
+ if (d >= end)
+ return URL_ERR_TOO_LONG;
+ *d++ = *s0++;
+ }
+ }
+ }
else
return URL_ERR_INVALID_CHAR;
}
else if (a[1] == '.' && (a[2] == '/' || !a[2])) /* "../" */
{
a += 2;
- if (d <= u->buf + 1)
- /*
- * RFC 1808 says we should leave ".." as a path segment, but
- * we intentionally break the rule and refuse the URL.
- */
- return URL_PATH_UNDERFLOW;
- d--; /* Discard trailing slash */
- while (d[-1] != '/')
- d--;
if (a[0])
a++;
+ if (d <= u->buf + 1)
+ {
+ /*
+ * RFC 1808 says we should leave ".." as a path segment, but
+ * we intentionally break the rule and refuse the URL.
+ */
+ if (!url_ignore_underflow)
+ return URL_PATH_UNDERFLOW;
+ }
+ else
+ {
+ d--; /* Discard trailing slash */
+ while (d[-1] != '/')
+ d--;
+ }
continue;
}
}
return errmsg[err];
}
-/* A "macro" for canonical split */
+/* Standard cookbook recipes */
int
url_canon_split(byte *u, byte *buf1, byte *buf2, struct url *url)
return url_canonicalize(url);
}
+int
+url_auto_canonicalize(byte *src, byte *dst)
+{
+ byte buf1[MAX_URL_SIZE], buf2[MAX_URL_SIZE], buf3[MAX_URL_SIZE];
+ int err;
+ struct url ur;
+
+ (void)((err = url_canon_split(src, buf1, buf2, &ur)) ||
+ (err = url_pack(&ur, buf3)) ||
+ (err = url_enescape(buf3, dst)));
+ return err;
+}
+
/* Testing */
#ifdef TEST
}
#endif
+
+struct component {
+ byte *start;
+ int length;
+ u32 hash;
+};
+
+static inline u32
+hashf(byte *start, int length)
+{
+ u32 hf = length;
+ while (length-- > 0)
+ hf = (hf << 8 | hf >> 24) ^ *start++;
+ return hf;
+}
+
+static inline uns
+repeat_count(struct component *comp, uns count, uns len)
+{
+ struct component *orig_comp = comp;
+ uns found = 0;
+ while (1)
+ {
+ uns i;
+ comp += len;
+ count -= len;
+ found++;
+ if (count < len)
+ return found;
+ for (i=0; i<len; i++)
+ if (comp[i].hash != orig_comp[i].hash
+ || comp[i].length != orig_comp[i].length
+ || memcmp(comp[i].start, orig_comp[i].start, comp[i].length))
+ return found;
+ }
+}
+
+int
+url_has_repeated_component(byte *url)
+{
+ struct component *comp;
+ uns comps, comp_len, rep_prefix;
+ byte *c;
+ uns i;
+
+ for (comps=0, c=url; c; comps++)
+ {
+ c = strpbrk(c, url_component_separators);
+ if (c)
+ c++;
+ }
+ if (comps < url_min_repeat_count)
+ return 0;
+ comp = alloca(comps * sizeof(struct component));
+ for (i=0, c=url; c; i++)
+ {
+ comp[i].start = c;
+ c = strpbrk(c, url_component_separators);
+ if (c)
+ {
+ comp[i].length = c - comp[i].start;
+ c++;
+ }
+ else
+ comp[i].length = strlen(comp[i].start);
+ }
+ ASSERT(i == comps);
+ for (i=0; i<comps; i++)
+ comp[i].hash = hashf(comp[i].start, comp[i].length);
+ for (comp_len = 1; comp_len <= url_max_repeat_length && comp_len <= comps; comp_len++)
+ for (rep_prefix = 0; rep_prefix <= comps - comp_len; rep_prefix++)
+ if (repeat_count(comp + rep_prefix, comps - rep_prefix, comp_len) >= url_min_repeat_count)
+ return comp_len;
+ return 0;
+}