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