daaf96735039bfeb538f638864524c4a9b13d50e
[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.4  1995/10/31 16:56:25  adam
45  * Record presentation.
46  *
47  * Revision 1.3  1995/10/27  15:12:10  adam
48  * IrTcl incorporated in the gateway.
49  * Better separation of script types.
50  * Z39.50 gateway scripts entered.
51  *
52  * Revision 1.2  1995/10/23  16:55:39  adam
53  * A lot of changes - really.
54  *
55  * Revision 1.1  1995/10/20  11:49:26  adam
56  * First version of www gateway.
57  *
58  */
59
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <sys/time.h>
63 #include <sys/types.h>
64 #include <sys/stat.h>
65 #include <fcntl.h>
66 #include <unistd.h>
67 #include <stdarg.h>
68 #include <ctype.h>
69 #include <errno.h>
70
71 #include "wproto.h"
72
73 static int wproto_dumpcache(WCLIENT wc, int level);
74 static int wproto_findcache(WCLIENT wc, char *name);
75 static void wproto_uncache(WCLIENT wc, int level);
76
77 static char *mod = "wproto";
78
79 void wo_puts(WCLIENT wc, char *s)
80 {
81     int len;
82
83     if (wc->outbuffer_offset + (len = strlen(s)) + 1 > wc->outbuffer_size)
84         wc->outbuffer = realloc(wc->outbuffer, wc->outbuffer_size +=
85         OUTBUFFER_CHUNK);
86     memcpy(wc->outbuffer + wc->outbuffer_offset, s, len + 1);
87     wc->outbuffer_offset += len;
88 }
89
90 void wo_printf(WCLIENT wc, const char *fmt, ...)
91 {
92     va_list ap;
93     char tmpbuf[4048];
94
95     va_start(ap, fmt);
96     vsprintf(tmpbuf, fmt, ap);
97     wo_puts(wc, tmpbuf);
98     va_end(ap);
99 }
100
101 void wo_clear(WCLIENT wc, char *type)
102 {
103     if (!wc->outbuffer)
104         wc->outbuffer = malloc(wc->outbuffer_size = OUTBUFFER_CHUNK);
105     wc->outbuffer_offset = 0;
106     wo_printf(wc, "Content-type: %s\n\n", type);
107 }
108
109 int wo_puthtml(WCLIENT wc, char *name)
110 {
111     FILE *f; 
112     char ch;
113
114     wo_clear(wc, "text/html");
115     if (!(f = fopen(name, "r")))
116     {
117         wo_printf(wc, "<BR>Failed to open file: %s<BR>", name);
118         return 0;
119     }
120     while (ch = getc(f), !feof(f))
121     {
122         if (wo_putc(wc, ch) < 0)
123         {
124             fclose(f);
125             return -1;
126         }
127     }
128     fclose(f);
129     return 0;
130 }
131
132 int wo_flush(WCLIENT wc)
133 {
134     int wrote, towrite;
135
136     if (!(wc->outbuffer_offset))
137         return 0;
138     towrite = wc->outbuffer_offset;
139     wc->outbuffer_offset = 0;
140     for (;;)
141     {
142         wrote = write(wc->lineout, wc->outbuffer + wc->outbuffer_offset,
143             towrite);
144         if (wrote <= 0)
145         {
146             gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "write response");
147             return -1;
148         }
149         if (wc->cache_fd >= 0)
150             if (write(wc->cache_fd, wc->outbuffer + wc->outbuffer_offset,
151                 towrite) < 0)
152             {   
153                 gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "write cache");
154                 return -1;
155             }
156         towrite -= wrote;
157         if (!towrite)
158             break;
159         wc->outbuffer_offset += wrote;
160     }
161     wc->outbuffer_offset = 0;
162     return 0;
163 }
164
165 int wo_overflow(WCLIENT wc, char ch)
166 {
167     gw_log (GW_LOG_DEBUG, mod, "wo_overflow");
168     if (wo_flush(wc) < 0)
169         return -1;
170     return wo_putc(wc, ch);
171 }
172
173 int wo_finish(WCLIENT wc)
174 {
175     gw_log (GW_LOG_DEBUG, mod, "wo_finish");
176     if (wo_flush(wc) < 0)
177         return -1;
178     close(wc->lineout);
179     wc->lineout = -1;
180     if (wc->cache_fd >= 0)
181     {
182         close(wc->cache_fd);
183         wc->cache_fd = -1;
184     }
185     return 0;
186 }
187
188 static void descramble(char *t, const char *o)
189 {
190     unsigned int v;
191
192     while (*o)
193     {
194         if (*o == '%' && isxdigit(*(o + 1)) && isxdigit(*(o + 2)))
195         {
196             sscanf(o + 1, "%2x", &v);
197             o += 3;
198             *(t++) = (char) v;
199         }
200         else
201             *(t++) = *(o++);
202     }
203     *t = '\0';
204 }
205
206 static void decode_form(wform_data *form, char *buf)
207 {
208     int i = 0;
209     char *p;
210     char tmp[512];
211
212     while (*buf)
213     {
214         for (p = form[i].name; *buf && *buf != '='; buf++)
215             *(p++) = *buf;
216         *p = '\0';
217         if (*buf)
218             buf++;
219         for (p = tmp; *buf && *buf != '&'; buf++)
220             *(p++) = *buf;
221         *p = '\0';
222         descramble(form[i].value, tmp);
223         if (*buf)
224             buf++;
225         i++;
226     }
227     *form[i].name = '\0';
228 }
229
230 char *wgetval(WCLIENT wc, char *name)
231 {
232     int i;
233
234     for (i = 0; *wc->wf_data[i].name; i++)
235         if (!strcmp(name, wc->wf_data[i].name))
236             return wc->wf_data[i].value;
237     return 0;
238 }
239
240 int wproto_process(WCLIENT wc, int timeout)
241 {
242     int toread, rs, level;
243     char combuf[COMBUF], *p,*t;
244     fd_set input;
245     struct timeval to, *top;
246
247     for (;;)
248     {
249         gw_log (GW_LOG_DEBUG, mod, "process waiting for input.");
250         if (timeout > 0)
251         {
252             to.tv_usec = 0;
253             to.tv_sec = timeout;
254             top = &to;
255         }
256         else
257             top = 0;
258         FD_ZERO(&input);
259         FD_SET(wc->linein, &input);
260         /* go through select handle list */
261         while ((rs = select(wc->linein + 1, &input, 0, 0, top)) < 0 &&
262             errno == EINTR)
263             ;
264         if (rs < 0)
265         {
266             gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "select");
267             return -1;
268         }
269         if (rs == 0)
270         {
271             gw_log (GW_LOG_STAT, mod, 
272                     "wproto_process returning 0 after %d second timeout.",
273                     timeout);
274             return 0;
275         }
276         /* determine handle (fifo or user) */
277         if (read(wc->linein, &toread, sizeof(toread)) < sizeof(toread))
278         {
279             gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "wp_proc:len read failed");
280             exit(1);
281         }
282         toread -= sizeof(toread);
283         if (read(wc->linein, combuf, toread) < toread)
284         {
285             gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "wp_proc: data read failed");
286             exit(1);
287         }
288         p = combuf;
289         for (t = wc->wf_serverp; (*t = *p); t++, p++);
290         p++;
291         for (t = wc->wf_parms; (*t = *p); t++, p++);
292         p++;
293         p++;         /* we don't deal with envvars yet */
294         decode_form(wc->wf_data, p);
295         if (wc->lineout < 0 && (wc->lineout = open(wc->wf_serverp, O_WRONLY))
296             < 0)
297         {
298             gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "open %s", wc->wf_serverp);
299             exit(1);
300         }
301         /* look in cache only if request carries no forms data. */
302         if (!*wc->wf_data[0].name && (level = wproto_findcache(wc,
303             wc->wf_parms)) >= 0)
304         {
305             wproto_dumpcache(wc, level);
306             wo_finish(wc);
307         }
308         else
309             return 1;
310     }
311 }
312
313 WCLIENT wproto_init(void)
314 {
315     char path2[256];
316     wclient_data *new;
317
318     gw_log (GW_LOG_DEBUG, mod, "wproto_init");
319     close(1);    /* release us from the wserver */
320     new = malloc(sizeof(*new));
321     new->id = getpid();
322     sprintf(new->path, "%s/%s/clt%d", FIFOROOT, FIFODIR, new->id);
323     if (mkfifo(new->path, 0666 | S_IFIFO) < 0)
324     {
325         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "mkfifo(%s)", new->path);
326         exit(1);
327     }
328     gw_log (GW_LOG_DEBUG, mod, "Synchronizing with server.");
329     sprintf(path2, "%s/%s/srv%d", FIFOROOT, FIFODIR, getppid());
330     if ((new->lineout = open(path2, O_WRONLY)) < 0)
331     {
332         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "open out %s", path2);
333         exit(1);
334     }
335     if (write(new->lineout, "OK", 2) < 2)
336     {
337         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "write");
338         exit(1);
339     }
340     gw_log (GW_LOG_DEBUG, mod, "Synchronized.");
341     if ((new->linein = open(new->path, O_RDONLY)) < 0)
342     {
343         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "open input %s", new->path);
344         exit(1);
345     }
346     gw_log (GW_LOG_DEBUG, mod, "init. linein=%d lineout=%d",
347             new->linein, new->lineout);
348     /* we put a handle on this so we get a blocking read when no peer */
349     if (open(new->path, O_WRONLY | O_NDELAY) < 0)
350     {
351         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "open dummy %s", new->path);
352         exit(1);
353     }
354     new->outbuffer = 0;
355     new->cache_level = -1;
356     new->cache_fd = -1;
357     return new;
358 }
359
360 static void wproto_uncache(WCLIENT wc, int level)
361 {
362     for (;wc->cache_level >= level; wc->cache_level--)
363         unlink(wc->cache[wc->cache_level].path);
364 }
365
366 void wproto_terminate(WCLIENT wc)
367 {
368     close(wc->linein);
369     unlink(wc->path);
370     wproto_uncache(wc, 0);
371     free(wc);
372 }
373
374 int wproto_cache(WCLIENT wc, int level)
375 {
376     cache_data *p;
377
378     if (level > wc->cache_level + 1)
379     {
380         gw_log (GW_LOG_FATAL, mod, "Illegal cache level increment.");
381         exit(1);
382     }
383     wproto_uncache(wc, level);
384     p = &wc->cache[++wc->cache_level];
385     sprintf(p->path, "%s/%s/csh%d.%d", FIFOROOT, FIFODIR, wc->id, level);
386     if ((wc->cache_fd = open(p->path, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0)
387     {
388         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "open %s", p->path);
389         return -1;
390     }
391     strcpy(p->name, wc->wf_parms);
392     return 0;
393 }
394
395 static int wproto_findcache(WCLIENT wc, char *name)
396 {
397     int i;
398
399     for (i = 0; i <= wc->cache_level; i++)
400         if (!strcmp(wc->cache[i].name, name))
401             return i;
402     return -1;
403 }
404
405 static int wproto_dumpcache(WCLIENT wc, int level)
406 {
407     int fd, rd;
408
409     gw_log (GW_LOG_STAT, mod, "Using Cache: %s", wc->cache[level].name);
410     if ((fd = open(wc->cache[level].path, O_RDONLY)) < 0)
411     {
412         gw_log (GW_LOG_FATAL, mod, "open (R) %s", wc->cache[level].path);
413         return -1;
414     }
415     while ((rd = read(fd, wc->outbuffer, OUTBUFFER_CHUNK)) > 0)
416         if (write(wc->lineout, wc->outbuffer, rd) < rd)
417         {
418             gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "write toline");
419             return -1;
420         }
421     if (rd < 0)
422     {
423         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "read");
424         return -1;
425     }
426     wproto_uncache(wc, level + 1);
427     return 0;
428 }