2 * - by Richard W.M. Jones <rich@annexia.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 * $Id: rewrite.c,v 1.7 2002/10/20 13:09:10 rich Exp $
38 #include "process_rq.h"
42 #define RW_DEBUG 0 /* Set this to enable debugging. */
44 static pool rw_pool = 0;
46 /* Cache of host -> struct rw *. The null host is stored with key = "". */
47 static shash rw_cache;
49 /* Pre-parsed rules. */
52 vector rules; /* Vector of struct rw_rule. */
58 const char *pattern_text;
62 #define RW_RULE_EXTERNAL 0x0001
63 #define RW_RULE_LAST 0x0002
64 #define RW_RULE_QSA 0x0004
67 static struct rw *parse_rules (const char *cfg);
68 static const char *append_qs (process_rq p, const char *path);
71 rewrite_reset_rules ()
73 if (rw_pool) delete_pool (rw_pool);
74 rw_pool = new_subpool (global_pool);
76 rw_cache = new_shash (rw_pool, struct rw *);
80 apply_rewrites (const process_rq p, const char *path, const char **location)
83 const char *host = p->host_header ? p->host_header : "";
84 int i, matches = 0, qsa = 0;
87 fprintf (stderr, "apply_rewrites: original path = %s\n",
91 /* Get the configuration entry. (Note the alias is not known
92 * yet, so rewrite rules inside alias sections have no effect).
94 if (!shash_get (rw_cache, host, rw))
98 cfg = cfg_get_string (p->host, 0, "rewrite", 0);
99 if (cfg) rw = parse_rules (cfg);
100 shash_insert (rw_cache, host, rw);
103 if (!rw) /* No matching rule. */
106 fprintf (stderr, "apply_rewrites: no matching rule\n");
111 /* Look for a matching rule. */
112 for (i = 0; i < vector_size (rw->rules); ++i)
115 const char *old_path = path;
117 vector_get (rw->rules, i, rule);
120 fprintf (stderr, "apply_rewrites: try matching against %s\n",
124 path = presubst (p->pool, old_path, rule.pattern, rule.sub, 0);
125 if (path != old_path) /* It matched. */
128 if (rule.flags & RW_RULE_QSA) qsa = 1;
131 fprintf (stderr, "apply_rewrites: it matches %s\n",
135 /* External link? If so, send a redirect. External rules are
138 if (rule.flags & RW_RULE_EXTERNAL)
142 "apply_rewrites: external: send redirect to %s\n",
145 *location = qsa ? append_qs (p, path) : path;
150 if (rule.flags & RW_RULE_LAST)
154 "apply_rewrites: last rule: finished with %s\n",
157 *location = qsa ? append_qs (p, path) : path;
161 /* Jump back to the beginning of the list. */
168 "apply_rewrites: finished with %s\n",
174 *location = qsa ? append_qs (p, path) : path;
181 /* Append query string, if there is one. */
183 append_qs (process_rq p, const char *path)
186 const char *qs = http_request_query_string (p->http_request);
188 if (qs && strlen (qs) > 0)
190 const char *t = strchr (path, '?');
192 if (t) /* Path already has a query string? */
195 return psprintf (pool, "%s&%s", path, qs);
197 return psprintf (pool, "%s%s", path, qs);
199 else /* Path doesn't have a query string. */
200 return psprintf (pool, "%s?%s", path, qs);
202 return path; /* No query string. */
205 static void parse_error (const char *line, const char *msg);
208 parse_rules (const char *cfg)
210 pool tmp = new_subpool (rw_pool);
217 /* Split up the configuration string into lines. */
218 lines = pstrcsplit (tmp, cfg, '\n');
220 /* Remove any empty lines (these have probably already been removed,
221 * but we can safely do this again anyway).
223 for (i = 0; i < vector_size (lines); ++i)
225 vector_get (lines, i, line);
227 if (strcmp (line, "") == 0)
229 vector_erase (lines, i);
234 if (vector_size (lines) == 0) { delete_pool (tmp); return 0; }
236 /* Allocate space for the return structure. */
237 rw = pmalloc (rw_pool, sizeof *rw);
238 rw->rules = new_vector (rw_pool, struct rw_rule);
240 /* Each line is a separate rule in the current syntax, so examine
241 * each line and turn it into a rule.
243 for (i = 0; i < vector_size (lines); ++i)
247 vector_get (lines, i, line);
249 v = pstrresplit (tmp, line, re_ws);
251 if (vector_size (v) < 2 || vector_size (v) > 3)
252 parse_error (line, "unrecognised format");
253 vector_get (v, 0, rule.pattern_text);
254 rule.pattern_text = pstrdup (rw_pool, rule.pattern_text);
255 rule.pattern = precomp (rw_pool, rule.pattern_text, 0);
256 vector_get (v, 1, rule.sub);
257 rule.sub = pstrdup (rw_pool, rule.sub);
259 /* Parse the flags. */
261 if (vector_size (v) == 3)
266 vector_get (v, 2, flags);
267 v = pstrresplit (tmp, flags, re_comma);
269 for (j = 0; j < vector_size (v); ++j)
273 vector_get (v, j, flag);
275 if (strcasecmp (flag, "external") == 0)
276 rule.flags |= RW_RULE_EXTERNAL;
277 else if (strcasecmp (flag, "last") == 0)
278 rule.flags |= RW_RULE_LAST;
279 else if (strcasecmp (flag, "qsa") == 0)
280 rule.flags |= RW_RULE_QSA;
282 parse_error (line, "unknown flag");
288 "parse rule: pattern=%s sub=%s flags=0x%04x\n",
289 rule.pattern_text, rule.sub, rule.flags);
292 vector_push_back (rw->rules, rule);
300 parse_error (const char *line, const char *msg)