First version of www gateway.
[egate.git] / www / wproto.c
1 /*
2  * Copyright (c) 1995, the EUROPAGATE consortium (see below).
3  *
4  * The EUROPAGATE consortium members are:
5  *
6  *    University College Dublin
7  *    Danmarks Teknologiske Videnscenter
8  *    An Chomhairle Leabharlanna
9  *    Consejo Superior de Investigaciones Cientificas
10  *
11  * Permission to use, copy, modify, distribute, and sell this software and
12  * its documentation, in whole or in part, for any purpose, is hereby granted,
13  * provided that:
14  *
15  * 1. This copyright and permission notice appear in all copies of the
16  * software and its documentation. Notices of copyright or attribution
17  * which appear at the beginning of any file must remain unchanged.
18  *
19  * 2. The names of EUROPAGATE or the project partners may not be used to
20  * endorse or promote products derived from this software without specific
21  * prior written permission.
22  *
23  * 3. Users of this software (implementors and gateway operators) agree to
24  * inform the EUROPAGATE consortium of their use of the software. This
25  * information will be used to evaluate the EUROPAGATE project and the
26  * software, and to plan further developments. The consortium may use
27  * the information in later publications.
28  * 
29  * 4. Users of this software agree to make their best efforts, when
30  * documenting their use of the software, to acknowledge the EUROPAGATE
31  * consortium, and the role played by the software in their work.
32  *
33  * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND,
34  * EXPRESS, IMPLIED, OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
35  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
36  * IN NO EVENT SHALL THE EUROPAGATE CONSORTIUM OR ITS MEMBERS BE LIABLE
37  * FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF
38  * ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
39  * OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND
40  * ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
41  * USE OR PERFORMANCE OF THIS SOFTWARE.
42  *
43  * $Log: wproto.c,v $
44  * Revision 1.1  1995/10/20 11:49:26  adam
45  * First version of www gateway.
46  *
47  */
48
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <sys/time.h>
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <fcntl.h>
55 #include <unistd.h>
56 #include <stdarg.h>
57 #include <ctype.h>
58 #include <errno.h>
59
60 #include "wproto.h"
61
62 static int wproto_dumpcache(WCLIENT wc, int level);
63 static int wproto_findcache(WCLIENT wc, char *name);
64 static void wproto_uncache(WCLIENT wc, int level);
65
66 static char *mod = "wproto";
67
68 void wo_puts(WCLIENT wc, char *s)
69 {
70     int len;
71
72     if (wc->outbuffer_offset + (len = strlen(s)) + 1 > wc->outbuffer_size)
73         wc->outbuffer = realloc(wc->outbuffer, wc->outbuffer_size +=
74         OUTBUFFER_CHUNK);
75     memcpy(wc->outbuffer + wc->outbuffer_offset, s, len + 1);
76     wc->outbuffer_offset += len;
77 }
78
79 void wo_printf(WCLIENT wc, const char *fmt, ...)
80 {
81     va_list ap;
82     char tmpbuf[4048];
83
84     va_start(ap, fmt);
85     vsprintf(tmpbuf, fmt, ap);
86     wo_puts(wc, tmpbuf);
87     va_end(ap);
88 }
89
90 void wo_clear(WCLIENT wc, char *type)
91 {
92     if (!wc->outbuffer)
93         wc->outbuffer = malloc(wc->outbuffer_size = OUTBUFFER_CHUNK);
94     wc->outbuffer_offset = 0;
95     wo_printf(wc, "Content-type: %s\n\n", type);
96 }
97
98 int wo_puthtml(WCLIENT wc, char *name)
99 {
100     FILE *f; 
101     char ch;
102
103     wo_clear(wc, "text/html");
104     if (!(f = fopen(name, "r")))
105     {
106         wo_printf(wc, "<BR>Failed to open file: %s<BR>", name);
107         return 0;
108     }
109     while (ch = getc(f), !feof(f))
110     {
111         if (wo_putc(wc, ch) < 0)
112         {
113             fclose(f);
114             return -1;
115         }
116     }
117     fclose(f);
118     return 0;
119 }
120
121 int wo_flush(WCLIENT wc)
122 {
123     int wrote, towrite;
124
125     if (!(wc->outbuffer_offset))
126         return 0;
127     towrite = wc->outbuffer_offset;
128     wc->outbuffer_offset = 0;
129     for (;;)
130     {
131         wrote = write(wc->lineout, wc->outbuffer + wc->outbuffer_offset,
132             towrite);
133         if (wrote <= 0)
134         {
135             gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "write response");
136             return -1;
137         }
138         if (wc->cache_fd >= 0)
139             if (write(wc->cache_fd, wc->outbuffer + wc->outbuffer_offset,
140                 towrite) < 0)
141             {   
142                 gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "write cache");
143                 return -1;
144             }
145         towrite -= wrote;
146         if (!towrite)
147             break;
148         wc->outbuffer_offset += wrote;
149     }
150     wc->outbuffer_offset = 0;
151     return 0;
152 }
153
154 int wo_overflow(WCLIENT wc, char ch)
155 {
156     gw_log (GW_LOG_DEBUG, mod, "wo_overflow");
157     if (wo_flush(wc) < 0)
158         return -1;
159     return wo_putc(wc, ch);
160 }
161
162 int wo_finish(WCLIENT wc)
163 {
164     gw_log (GW_LOG_DEBUG, mod, "wo_finish");
165     if (wo_flush(wc) < 0)
166         return -1;
167     close(wc->lineout);
168     wc->lineout = -1;
169     if (wc->cache_fd >= 0)
170     {
171         close(wc->cache_fd);
172         wc->cache_fd = -1;
173     }
174     return 0;
175 }
176
177 static void descramble(char *t, const char *o)
178 {
179     unsigned int v;
180
181     while (*o)
182     {
183         if (*o == '%' && isxdigit(*(o + 1)) && isxdigit(*(o + 2)))
184         {
185             sscanf(o + 1, "%2x", &v);
186             o += 3;
187             *(t++) = (char) v;
188         }
189         else
190             *(t++) = *(o++);
191     }
192     *t = '\0';
193 }
194
195 static void decode_form(wform_data *form, char *buf)
196 {
197     int i = 0;
198     char *p;
199     char tmp[512];
200
201     while (*buf)
202     {
203         for (p = form[i].name; *buf && *buf != '='; buf++)
204             *(p++) = *buf;
205         *p = '\0';
206         if (*buf)
207             buf++;
208         for (p = tmp; *buf && *buf != '&'; buf++)
209             *(p++) = *buf;
210         *p = '\0';
211         descramble(form[i].value, tmp);
212         if (*buf)
213             buf++;
214         i++;
215     }
216     *form[i].name = '\0';
217 }
218
219 char *wgetval(WCLIENT wc, char *name)
220 {
221     int i;
222
223     for (i = 0; *wc->wf_data[i].name; i++)
224         if (!strcmp(name, wc->wf_data[i].name))
225             return wc->wf_data[i].value;
226     return 0;
227 }
228
229 int wproto_process(WCLIENT wc, int timeout)
230 {
231     int toread, rs, level;
232     char combuf[COMBUF], *p,*t;
233     fd_set input;
234     struct timeval to, *top;
235
236     for (;;)
237     {
238         gw_log (GW_LOG_DEBUG, mod, "process waiting for input.");
239         if (timeout > 0)
240         {
241             to.tv_usec = 0;
242             to.tv_sec = timeout;
243             top = &to;
244         }
245         else
246             top = 0;
247         FD_ZERO(&input);
248         FD_SET(wc->linein, &input);
249         while ((rs = select(wc->linein + 1, &input, 0, 0, top)) < 0 &&
250             errno == EINTR)
251             ;
252         if (rs < 0)
253         {
254             gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "select");
255             return -1;
256         }
257         if (rs == 0)
258         {
259             gw_log (GW_LOG_STAT, mod, 
260                     "wproto_process returning 0 after %d second timeout.",
261                     timeout);
262             return 0;
263         }
264         if (read(wc->linein, &toread, sizeof(toread)) < sizeof(toread))
265         {
266             gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "wp_proc:len read failed");
267             exit(1);
268         }
269         toread -= sizeof(toread);
270         if (read(wc->linein, combuf, toread) < toread)
271         {
272             gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "wp_proc: data read failed");
273             exit(1);
274         }
275         p = combuf;
276         for (t = wc->wf_serverp; (*t = *p); t++, p++);
277         p++;
278         for (t = wc->wf_parms; (*t = *p); t++, p++);
279         p++;
280         p++;         /* we don't deal with envvars yet */
281         decode_form(wc->wf_data, p);
282         if (wc->lineout < 0 && (wc->lineout = open(wc->wf_serverp, O_WRONLY))
283             < 0)
284         {
285             gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "open %s", wc->wf_serverp);
286             exit(1);
287         }
288         /* look in cache only if request carries no forms data. */
289         if (!*wc->wf_data[0].name && (level = wproto_findcache(wc,
290             wc->wf_parms)) >= 0)
291         {
292             wproto_dumpcache(wc, level);
293             wo_finish(wc);
294         }
295         else
296             return 1;
297     }
298 }
299
300 WCLIENT wproto_init(void)
301 {
302     char path2[256];
303     wclient_data *new;
304
305     gw_log (GW_LOG_DEBUG, mod, "wproto_init");
306     close(1);    /* release us from the wserver */
307     new = malloc(sizeof(*new));
308     sprintf(new->path, "%s/%s/clt%d", FIFOROOT, FIFODIR, getpid());
309     if (mkfifo(new->path, 0666 | S_IFIFO) < 0)
310     {
311         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "mkfifo(%s)", new->path);
312         exit(1);
313     }
314     gw_log (GW_LOG_DEBUG, mod, "Synchronizing with server.");
315     sprintf(path2, "%s/%s/srv%d", FIFOROOT, FIFODIR, getppid());
316     if ((new->lineout = open(path2, O_WRONLY)) < 0)
317     {
318         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "open %s", path2);
319         exit(1);
320     }
321     if (write(new->lineout, "OK", 2) < 2)
322     {
323         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "write");
324         exit(1);
325     }
326     gw_log (GW_LOG_DEBUG, mod, "Synchronized.");
327     if ((new->linein = open(new->path, O_RDONLY)) < 0)
328     {
329         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "open input handle %s", new->path);
330         exit(1);
331     }
332     /* we put a handle on this so we get a blocking read when no peer */
333     if (open(new->path, O_WRONLY | O_NDELAY) < 0)
334     {
335         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "open dummy %s", new->path);
336         exit(1);
337     }
338     new->outbuffer = 0;
339     new->cache_level = -1;
340     new->cache_fd = -1;
341     return new;
342 }
343
344 static void wproto_uncache(WCLIENT wc, int level)
345 {
346     for (;wc->cache_level >= level; wc->cache_level--)
347         unlink(wc->cache[wc->cache_level].path);
348 }
349
350 void wproto_terminate(WCLIENT wc)
351 {
352     close(wc->linein);
353     unlink(wc->path);
354     wproto_uncache(wc, 0);
355     free(wc);
356 }
357
358 int wproto_cache(WCLIENT wc, int level)
359 {
360     cache_data *p;
361
362     if (level > wc->cache_level + 1)
363     {
364         gw_log (GW_LOG_FATAL, mod, "Illegal cache level increment.");
365         exit(1);
366     }
367     wproto_uncache(wc, level);
368     p = &wc->cache[++wc->cache_level];
369     sprintf(p->path, "%s/%s/csh%d.%d", FIFOROOT, FIFODIR, getpid(), level);
370     if ((wc->cache_fd = open(p->path, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0)
371     {
372         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "open %s", p->path);
373         return -1;
374     }
375     strcpy(p->name, wc->wf_parms);
376     return 0;
377 }
378
379 static int wproto_findcache(WCLIENT wc, char *name)
380 {
381     int i;
382
383     for (i = 0; i <= wc->cache_level; i++)
384         if (!strcmp(wc->cache[i].name, name))
385             return i;
386     return -1;
387 }
388
389 static int wproto_dumpcache(WCLIENT wc, int level)
390 {
391     int fd, rd;
392
393     gw_log (GW_LOG_STAT, mod, "Using Cache: %s", wc->cache[level].name);
394     if ((fd = open(wc->cache[level].path, O_RDONLY)) < 0)
395     {
396         gw_log (GW_LOG_FATAL, mod, "open (R) %s", wc->cache[level].path);
397         return -1;
398     }
399     while ((rd = read(fd, wc->outbuffer, OUTBUFFER_CHUNK)) > 0)
400         if (write(wc->lineout, wc->outbuffer, rd) < rd)
401         {
402             gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "write toline");
403             return -1;
404         }
405     if (rd < 0)
406     {
407         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "read");
408         return -1;
409     }
410     wproto_uncache(wc, level + 1);
411     return 0;
412 }