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