Home | History | Annotate | Download | only in cgi
      1 /*
      2  * Copyright 2001-2004 Brandon Long
      3  * All Rights Reserved.
      4  *
      5  * ClearSilver Templating System
      6  *
      7  * This code is made available under the terms of the ClearSilver License.
      8  * http://www.clearsilver.net/license.hdf
      9  *
     10  */
     11 
     12 #include "cs_config.h"
     13 
     14 #if HAVE_FEATURES_H
     15 #include <features.h>
     16 #endif
     17 #include <stdarg.h>
     18 #include <stdio.h>
     19 #include <stdlib.h>
     20 #include <string.h>
     21 #include "util/neo_misc.h"
     22 #include "util/neo_err.h"
     23 #include "cgi/cgiwrap.h"
     24 
     25 typedef struct _cgiwrapper
     26 {
     27   int argc;
     28   char **argv;
     29   char **envp;
     30   int env_count;
     31 
     32   READ_FUNC read_cb;
     33   WRITEF_FUNC writef_cb;
     34   WRITE_FUNC write_cb;
     35   GETENV_FUNC getenv_cb;
     36   PUTENV_FUNC putenv_cb;
     37   ITERENV_FUNC iterenv_cb;
     38 
     39   void *data;
     40   int emu_init;
     41 } CGIWRAPPER;
     42 
     43 static CGIWRAPPER GlobalWrapper = {0, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0};
     44 
     45 void cgiwrap_init_std (int argc, char **argv, char **envp)
     46 {
     47   /* Allow setting of these even after cgiwrap_init_emu is called */
     48   GlobalWrapper.argc = argc;
     49   GlobalWrapper.argv = argv;
     50   GlobalWrapper.envp = envp;
     51   GlobalWrapper.env_count = 0;
     52   while (envp[GlobalWrapper.env_count] != NULL) GlobalWrapper.env_count++;
     53 
     54   /* so you can compile the same code for embedded without mods.
     55    * Note that this setting is global for the lifetime of the program, so
     56    * you can never reset these values after calling cgiwrap_init_emu by
     57    * calling cgiwrap_init_std, you'll have to call cgiwrap_init_emu with NULL
     58    * values to reset */
     59   if (GlobalWrapper.emu_init) return;
     60 
     61   GlobalWrapper.read_cb = NULL;
     62   GlobalWrapper.writef_cb = NULL;
     63   GlobalWrapper.write_cb = NULL;
     64   GlobalWrapper.getenv_cb = NULL;
     65   GlobalWrapper.putenv_cb = NULL;
     66   GlobalWrapper.iterenv_cb = NULL;
     67   GlobalWrapper.data = NULL;
     68 }
     69 
     70 void cgiwrap_init_emu (void *data, READ_FUNC read_cb,
     71     WRITEF_FUNC writef_cb, WRITE_FUNC write_cb, GETENV_FUNC getenv_cb,
     72     PUTENV_FUNC putenv_cb, ITERENV_FUNC iterenv_cb)
     73 {
     74   /* leave argc, argv, envp, env_count alone since we either don't use them, or
     75    * they are used by the default versions if any of these are passed as NULL.
     76    * Note that means that if you pass NULL for anything here, you'd better
     77    * have called cgiwrap_init_std first! */
     78   GlobalWrapper.data = data;
     79   GlobalWrapper.read_cb = read_cb;
     80   GlobalWrapper.writef_cb = writef_cb;
     81   GlobalWrapper.write_cb = write_cb;
     82   GlobalWrapper.getenv_cb = getenv_cb;
     83   GlobalWrapper.putenv_cb = putenv_cb;
     84   GlobalWrapper.iterenv_cb = iterenv_cb;
     85   GlobalWrapper.emu_init = 1;
     86 }
     87 
     88 NEOERR *cgiwrap_getenv (const char *k, char **v)
     89 {
     90   if (GlobalWrapper.getenv_cb != NULL)
     91   {
     92     *v = GlobalWrapper.getenv_cb (GlobalWrapper.data, k);
     93   }
     94   else
     95   {
     96     char *s = getenv(k);
     97 
     98     if (s != NULL)
     99     {
    100       *v = strdup(s);
    101       if (*v == NULL)
    102       {
    103 	return nerr_raise (NERR_NOMEM, "Unable to duplicate env var %s=%s",
    104 	    k, s);
    105       }
    106     }
    107     else
    108     {
    109       *v = NULL;
    110     }
    111   }
    112   return STATUS_OK;
    113 }
    114 
    115 NEOERR *cgiwrap_putenv (const char *k, const char *v)
    116 {
    117   if (GlobalWrapper.putenv_cb != NULL)
    118   {
    119     if (GlobalWrapper.putenv_cb(GlobalWrapper.data, k, v))
    120       return nerr_raise(NERR_NOMEM, "putenv_cb says nomem when %s=%s", k, v);
    121   }
    122   else
    123   {
    124     char *buf;
    125     int l;
    126     l = strlen(k) + strlen(v) + 2;
    127     buf = (char *) malloc(sizeof(char) * l);
    128     if (buf == NULL)
    129       return nerr_raise(NERR_NOMEM, "Unable to allocate memory for putenv %s=%s", k, v);
    130     snprintf (buf, l, "%s=%s", k, v);
    131     if (putenv (buf))
    132       return nerr_raise(NERR_NOMEM, "putenv says nomem when %s", buf);
    133   }
    134   return STATUS_OK;
    135 }
    136 
    137 NEOERR *cgiwrap_iterenv (int num, char **k, char **v)
    138 {
    139   *k = NULL;
    140   *v = NULL;
    141   if (GlobalWrapper.iterenv_cb != NULL)
    142   {
    143     int r;
    144 
    145     r = GlobalWrapper.iterenv_cb(GlobalWrapper.data, num, k, v);
    146     if (r)
    147       return nerr_raise(NERR_SYSTEM, "iterenv_cb returned %d", r);
    148   }
    149   else if (GlobalWrapper.envp != NULL && num < GlobalWrapper.env_count)
    150   {
    151     char *c, *s = GlobalWrapper.envp[num];
    152 
    153     c = strchr (s, '=');
    154     if (c == NULL) return STATUS_OK;
    155     *c = '\0';
    156     *k = strdup(s);
    157     *c = '=';
    158     if (*k == NULL)
    159       return nerr_raise(NERR_NOMEM, "iterenv says nomem for %s", s);
    160     *v = strdup(c+1);
    161     if (*v == NULL)
    162     {
    163       free(*k);
    164       *k = NULL;
    165       return nerr_raise(NERR_NOMEM, "iterenv says nomem for %s", s);
    166     }
    167   }
    168   return STATUS_OK;
    169 }
    170 
    171 NEOERR *cgiwrap_writef (const char *fmt, ...)
    172 {
    173   va_list ap;
    174 
    175   va_start (ap, fmt);
    176   cgiwrap_writevf (fmt, ap);
    177   va_end (ap);
    178   return STATUS_OK;
    179 }
    180 
    181 NEOERR *cgiwrap_writevf (const char *fmt, va_list ap)
    182 {
    183   int r;
    184 
    185   if (GlobalWrapper.writef_cb != NULL)
    186   {
    187     r = GlobalWrapper.writef_cb (GlobalWrapper.data, fmt, ap);
    188     if (r)
    189       return nerr_raise_errno (NERR_IO, "writef_cb returned %d", r);
    190   }
    191   else
    192   {
    193     vprintf (fmt, ap);
    194     /* vfprintf(stderr, fmt, ap); */
    195   }
    196   return STATUS_OK;
    197 }
    198 
    199 NEOERR *cgiwrap_write (const char *buf, int buf_len)
    200 {
    201   int r;
    202 
    203   if (GlobalWrapper.write_cb != NULL)
    204   {
    205     r = GlobalWrapper.write_cb (GlobalWrapper.data, buf, buf_len);
    206     if (r != buf_len)
    207       return nerr_raise_errno (NERR_IO, "write_cb returned %d<%d", r, buf_len);
    208   }
    209   else
    210   {
    211     /* r = fwrite(buf, sizeof(char), buf_len, stderr);  */
    212     r = fwrite(buf, sizeof(char), buf_len, stdout);
    213     if (r != buf_len)
    214       return nerr_raise_errno (NERR_IO, "fwrite returned %d<%d", r, buf_len);
    215   }
    216   return STATUS_OK;
    217 }
    218 
    219 void cgiwrap_read (char *buf, int buf_len, int *read_len)
    220 {
    221   if (GlobalWrapper.read_cb != NULL)
    222   {
    223     *read_len = GlobalWrapper.read_cb (GlobalWrapper.data, buf, buf_len);
    224   }
    225   else
    226   {
    227 #ifdef __UCLIBC__
    228     /* According to
    229      * http://cvs.uclinux.org/cgi-bin/cvsweb.cgi/uClibc/libc/stdio/stdio.c#rev1.28
    230      * Note: there is a difference in behavior between glibc and uClibc here
    231      * regarding fread() on a tty stream.  glibc's fread() seems to return
    232      * after reading all _available_ data even if not at end-of-file, while
    233      * uClibc's fread() continues reading until all requested or eof or error.
    234      * The latter behavior seems correct w.r.t. the standards.
    235      *
    236      * So, we use read on uClibc.  This may be required on other platforms as
    237      * well.  Using raw and buffered i/o interchangeably can be problematic,
    238      * but everyone should be going through the cgiwrap interfaces which only
    239      * provide this one read function.
    240      */
    241      *read_len = read (fileno(stdin), buf, buf_len);
    242 #else
    243      *read_len = fread (buf, sizeof(char), buf_len, stdin);
    244 #endif
    245   }
    246 }
    247