]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/httpd/redirect.c
ip/tftpd: remove sunkernel hack
[plan9front.git] / sys / src / cmd / ip / httpd / redirect.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include "httpd.h"
5 #include "httpsrv.h"
6
7 enum
8 {
9         HASHSIZE = 1019,
10 };
11
12 typedef struct Redir    Redir;
13 struct Redir
14 {
15         Redir   *next;
16         char    *pat;
17         char    *repl;
18         uint    flags;          /* generated from repl's decorations */
19 };
20
21 static Redir *redirtab[HASHSIZE];
22 static Redir *vhosttab[HASHSIZE];
23 static char emptystring[1];
24 /* these two arrays must be kept in sync */
25 static char decorations[] = { Modsilent, Modperm, Modsubord, Modonly, '\0' };
26 static uint redirflags[] = { Redirsilent, Redirperm, Redirsubord, Redironly, };
27
28 /* replacement field decorated with redirection modifiers? */
29 static int
30 isdecorated(char *repl)
31 {
32         return strchr(decorations, repl[0]) != nil;
33 }
34
35 static uint
36 decor2flags(char *repl)
37 {
38         uint flags;
39         char *p;
40
41         flags = 0;
42         while ((p = strchr(decorations, *repl++)) != nil)
43                 flags |= redirflags[p - decorations];
44         return flags;
45 }
46
47 /* return replacement without redirection modifiers */
48 char *
49 undecorated(char *repl)
50 {
51         while (isdecorated(repl))
52                 repl++;
53         return repl;
54 }
55
56 static int
57 hashasu(char *key, int n)
58 {
59         ulong h;
60
61         h = 0;
62         while(*key != 0)
63                 h = 65599*h + *(uchar*)key++;
64         return h % n;
65 }
66
67 static void
68 insert(Redir **tab, char *pat, char *repl)
69 {
70         Redir **l;
71         Redir *srch;
72         ulong hash;
73
74         hash = hashasu(pat, HASHSIZE);
75         for(l = &tab[hash]; *l; l = &(*l)->next)
76                 ;
77         *l = srch = ezalloc(sizeof(Redir));
78         srch->pat = pat;
79         srch->flags = decor2flags(repl);
80         srch->repl = undecorated(repl);
81         srch->next = 0;
82 }
83
84 static void
85 cleartab(Redir **tab)
86 {
87         Redir *t;
88         int i;
89
90         for(i = 0; i < HASHSIZE; i++){
91                 while((t = tab[i]) != nil){
92                         tab[i] = t->next;
93                         free(t->pat);
94                         free(t->repl);
95                         free(t);
96                 }
97         }
98 }
99
100 void
101 redirectinit(void)
102 {
103         static Biobuf *b = nil;
104         static Qid qid;
105         char *file, *line, *s, *host, *field[3];
106         static char pfx[] = "http://";
107
108         file = "/sys/lib/httpd.rewrite";
109         if(b != nil){
110                 if(updateQid(Bfildes(b), &qid) == 0)
111                         return;
112                 Bterm(b);
113         }
114         b = Bopen(file, OREAD);
115         if(b == nil)
116                 sysfatal("can't read from %s", file);
117         updateQid(Bfildes(b), &qid);
118
119         cleartab(redirtab);
120         cleartab(vhosttab);
121
122         while((line = Brdline(b, '\n')) != nil){
123                 line[Blinelen(b)-1] = 0;
124                 s = strchr(line, '#');
125                 if(s != nil && (s == line || s[-1] == ' ' || s[-1] == '\t'))
126                         *s = '\0';      /* chop comment iff after whitespace */
127                 if(tokenize(line, field, nelem(field)) == 2){
128                         if(strncmp(field[0], pfx, STRLEN(pfx)) == 0 &&
129                            strncmp(undecorated(field[1]), pfx, STRLEN(pfx)) != 0){
130                                 /* url -> filename */
131                                 host = field[0] + STRLEN(pfx);
132                                 s = strrchr(host, '/');
133                                 if(s)
134                                         *s = 0;  /* chop trailing slash */
135
136                                 insert(vhosttab, estrdup(host), estrdup(field[1]));
137                         }else{
138                                 insert(redirtab, estrdup(field[0]), estrdup(field[1]));
139                         }
140                 }
141         }
142         syslog(0, HTTPLOG, "redirectinit pid=%d", getpid());
143 }
144
145 static Redir*
146 lookup(Redir **tab, char *pat, int count)
147 {
148         Redir *srch;
149         ulong hash;
150
151         hash = hashasu(pat,HASHSIZE);
152         for(srch = tab[hash]; srch != nil; srch = srch->next)
153                 if(strcmp(pat, srch->pat) == 0) {
154                         /* only exact match wanted? */
155                         if (!(srch->flags & Redironly) || count == 0)
156                                 return srch;
157                 }
158         return nil;
159 }
160
161 static char*
162 prevslash(char *p, char *s)
163 {
164         while(--s > p)
165                 if(*s == '/')
166                         break;
167         return s;
168 }
169
170 /*
171  * find the longest match of path against the redirection table,
172  * chopping off the rightmost path component until success or
173  * there's nothing left.  return a copy of the replacement string
174  * concatenated with a slash and the portion of the path *not* matched.
175  * So a match of /who/gre/some/stuff.html matched against
176  *      /who/gre        http://gremlinsrus.org
177  * returns
178  *      http://gremlinsrus.org/some/stuff.html
179  *
180  * further flags: if Redironly, match only the named page and no
181  * subordinate ones.  if Redirsubord, map the named patch and any
182  * subordinate ones to the same replacement URL.
183  */
184 char*
185 redirect(HConnect *hc, char *path, uint *flagp)
186 {
187         Redir *redir;
188         char *s, *newpath, *repl;
189         int c, n, count;
190
191         count = 0;
192         for(s = strchr(path, '\0'); s > path; s = prevslash(path, s)){
193                 c = *s;
194                 *s = '\0';
195                 redir = lookup(redirtab, path, count++);
196                 *s = c;
197                 if(redir != nil){
198                         if (flagp)
199                                 *flagp = redir->flags;
200                         repl = redir->repl;
201                         if(redir->flags & Redirsubord)
202                                 /* don't append s, all matches map to repl */
203                                 s = "";
204                         n = strlen(repl) + strlen(s) + 2 + UTFmax;
205                         newpath = halloc(hc, n);
206                         snprint(newpath, n, "%s%s", repl, s);
207                         return newpath;
208                 }
209         }
210         return nil;
211 }
212
213 /*
214  * if host is virtual, return implicit prefix for URI within webroot.
215  * if not, return empty string.
216  * return value should not be freed by caller.
217  */
218 char*
219 masquerade(char *host)
220 {
221         Redir *redir;
222
223         redir = lookup(vhosttab, host, 0);
224         if(redir == nil)
225                 return emptystring;
226         return redir->repl;
227 }