URI: 
       libgcgi.c - libgcgi - REST library for Gopher
  HTML git clone git://bitreich.org/libgcgi/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/libgcgi/
   DIR Log
   DIR Files
   DIR Refs
   DIR Tags
   DIR README
   DIR LICENSE
       ---
       libgcgi.c (6502B)
       ---
            1 #include <assert.h>
            2 #include <ctype.h>
            3 #include <errno.h>
            4 #include <stdarg.h>
            5 #include <stdint.h>
            6 #include <stdio.h>
            7 #include <stdlib.h>
            8 #include <string.h>
            9 #include <strings.h>
           10 #include <unistd.h>
           11 #include <sys/stat.h>
           12 
           13 #include "libgcgi.h"
           14 
           15 #define GCGI_MATCH_NUM 5
           16 
           17 char *gcgi_gopher_search;
           18 char *gcgi_gopher_path;
           19 char *gcgi_gopher_host;
           20 char *gcgi_gopher_port;
           21 struct gcgi_var_list gcgi_gopher_query;
           22 
           23 void
           24 gcgi_fatal(char *fmt, ...)
           25 {
           26         va_list va;
           27         char msg[1024];
           28 
           29         va_start(va, fmt);
           30         vsnprintf(msg, sizeof msg, fmt, va);
           31         printf("error: %s\n", msg);
           32         exit(1);
           33 }
           34 
           35 static char *
           36 gcgi_fopenread(char *path)
           37 {
           38         FILE *fp;
           39         char *buf;
           40         ssize_t ssz;
           41         size_t sz;
           42 
           43         if ((fp = fopen(path, "r")) == NULL)
           44                 return NULL;
           45         if (fseek(fp, 0, SEEK_END) == -1)
           46                 return NULL;
           47         if ((ssz = ftell(fp)) == -1)
           48                 return NULL;
           49         sz = ssz;
           50         if (fseek(fp, 0, SEEK_SET) == -1)
           51                 return NULL;
           52         if ((buf = malloc(sz + 1)) == NULL)
           53                 return NULL;
           54         if (fread(buf, sz, 1, fp) == sz) {
           55                 errno = EFBIG;
           56                 goto error_free;
           57         }
           58         if (ferror(fp))
           59                 goto error_free;
           60         fclose(fp);
           61         buf[sz] = '\0';
           62         return buf;
           63 error_free:
           64         free(buf);
           65         return NULL;
           66 }
           67 
           68 static int
           69 gcgi_cmp_var(const void *v1, const void *v2)
           70 {
           71         return strcasecmp(((struct gcgi_var *)v1)->key, ((struct gcgi_var *)v2)->key);
           72 }
           73 
           74 static void
           75 gcgi_add_var(struct gcgi_var_list *vars, char *key, char *val)
           76 {
           77         void *mem;
           78 
           79         vars->len++;
           80         if ((mem = realloc(vars->list, vars->len * sizeof *vars->list)) == NULL)
           81                  gcgi_fatal("realloc");
           82         vars->list = mem;
           83         vars->list[vars->len-1].key = key;
           84         vars->list[vars->len-1].val = val;
           85 }
           86 
           87 static void
           88 gcgi_sort_var_list(struct gcgi_var_list *vars)
           89 {
           90         qsort(vars->list, vars->len, sizeof *vars->list, gcgi_cmp_var);
           91 }
           92 
           93 char *
           94 gcgi_get_var(struct gcgi_var_list *vars, char *key)
           95 {
           96         struct gcgi_var *v, q = { .key = key };
           97 
           98         v = bsearch(&q, vars->list, vars->len, sizeof *vars->list, gcgi_cmp_var);
           99         return (v == NULL) ? NULL : v->val;
          100 }
          101 
          102 void
          103 gcgi_set_var(struct gcgi_var_list *vars, char *key, char *val)
          104 {
          105         struct gcgi_var *v, q;
          106 
          107         q.key = key;
          108         v = bsearch(&q, vars->list, vars->len, sizeof *vars->list, gcgi_cmp_var);
          109         if (v != NULL) {
          110                 v->val = val;
          111                 return;
          112         }
          113         gcgi_add_var(vars, key, val);
          114         gcgi_sort_var_list(vars);
          115 }
          116 
          117 void
          118 gcgi_read_var_list(struct gcgi_var_list *vars, char *path)
          119 {
          120         char *line, *tail, *key, *s;
          121 
          122         line = NULL;
          123 
          124         if ((tail = vars->buf = gcgi_fopenread(path)) == NULL)
          125                 gcgi_fatal("opening %s: %s", path, strerror(errno));
          126         while ((line = strsep(&tail, "\n")) != NULL) {
          127                 if (line[0] == '\0')
          128                         break;
          129                 key = strsep(&line, ":");
          130                 if (line == NULL || *line++ != ' ')
          131                         gcgi_fatal("%s: missing ': ' separator", path);
          132                 gcgi_add_var(vars, key, line);
          133         }
          134         gcgi_set_var(vars, "text", tail ? tail : "");
          135         gcgi_set_var(vars, "file", (s = strrchr(path, '/')) ? s + 1 : path);
          136         gcgi_sort_var_list(vars);
          137 }
          138 
          139 void
          140 gcgi_free_var_list(struct gcgi_var_list *vars)
          141 {
          142         free(vars->buf);
          143         free(vars->list);
          144 }
          145 
          146 int
          147 gcgi_write_var_list(struct gcgi_var_list *vars, char *dst)
          148 {
          149         FILE *fp;
          150         struct gcgi_var *v;
          151         size_t n;
          152         char path[1024];
          153         char *text;
          154 
          155         text = NULL;
          156 
          157         snprintf(path, sizeof path, "%s.tmp", dst);
          158         if ((fp = fopen(path, "w")) == NULL)
          159                 gcgi_fatal("opening '%s' for writing", path);
          160 
          161         for (v = vars->list, n = vars->len; n > 0; v++, n--) {
          162                 if (strcasecmp(v->key, "text") == 0) {
          163                         text = text ? text : v->val;
          164                         continue;
          165                 }
          166                 assert(strchr(v->key, '\n') == NULL);
          167                 assert(strchr(v->val, '\n') == NULL);
          168                 fprintf(fp, "%s: %s\n", v->key, v->val);
          169         }
          170         fprintf(fp, "\n%s", text ? text : "");
          171 
          172         fclose(fp);
          173         if (rename(path, dst) == -1)
          174                 gcgi_fatal( "renaming '%s' to '%s'", path, dst);
          175         return 0;
          176 }
          177 
          178 static int
          179 gcgi_match(char const *glob, char *path, char **matches, size_t m)
          180 {
          181         if (m >= GCGI_MATCH_NUM)
          182                 gcgi_fatal("too many wildcards in glob");
          183         matches[m] = NULL;
          184         while (*glob != '*' && *path != '\0' && *glob == *path)
          185                 glob++, path++;
          186         if (glob[0] == '*') {
          187                 if (*glob != '\0' && gcgi_match(glob + 1, path, matches, m + 1)) {
          188                         if (matches[m] == NULL)
          189                                 matches[m] = path;
          190                         *path = '\0';
          191                         return 1;
          192                 } else if (*path != '\0' && gcgi_match(glob, path + 1, matches, m)) {
          193                         matches[m] = (char *)path;
          194                         return 1;
          195                 }
          196         }
          197         return *glob == '\0' && *path == '\0';
          198 }
          199 
          200 static void
          201 gcgi_decode_url(struct gcgi_var_list *vars, char *s)
          202 {
          203         char *tok, *eq;
          204 
          205         while ((tok = strsep(&s, "&"))) {
          206                 if ((eq = strchr(tok, '=')) == NULL)
          207                         continue;
          208                 *eq = '\0';
          209                 gcgi_add_var(vars, tok, eq + 1);
          210         }
          211         gcgi_sort_var_list(vars);
          212 }
          213 
          214 void
          215 gcgi_handle_request(struct gcgi_handler h[], char **argv, int argc)
          216 {
          217         char *query_string;
          218 
          219         if (argc != 5)
          220                 gcgi_fatal("wrong number of arguments: %c", argc);
          221         assert(argv[0] && argv[1] && argv[2] && argv[3]);
          222 
          223         /* executable.[d]cgi $search $arguments $host $port */
          224         gcgi_gopher_search = argv[1];
          225         gcgi_gopher_path = argv[2];
          226         gcgi_gopher_host = argv[3];
          227         gcgi_gopher_port = argv[4];
          228         query_string = strchr(gcgi_gopher_path, '?');
          229         if (query_string != NULL) {
          230                 *query_string++ = '\0';
          231                 gcgi_decode_url(&gcgi_gopher_query, query_string);
          232         }
          233 
          234         for (; h->glob != NULL; h++) {
          235                 char *matches[GCGI_MATCH_NUM + 1];
          236                 if (!gcgi_match(h->glob, gcgi_gopher_path, matches, 0))
          237                         continue;
          238                 h->fn(matches);
          239                 return;
          240         }
          241         gcgi_fatal("no handler for '%s'", gcgi_gopher_path);
          242 }
          243 
          244 static char*
          245 gcgi_next_var(char *head, char **tail)
          246 {
          247         char *beg, *end;
          248 
          249         if ((beg = strstr(head, "{{")) == NULL
          250           || (end = strstr(beg, "}}")) == NULL)
          251                 return NULL;
          252         *beg = *end = '\0';
          253         *tail = end + strlen("}}");
          254         return beg + strlen("{{");
          255 }
          256 
          257 void
          258 gcgi_print_gophermap(char type, char *desc, char *path, char *host, char *port)
          259 {
          260         assert(type >= 0x30);
          261         printf("%c%s\t%s\t%s\t%s\n", type, desc, path, host, port);
          262 }
          263 
          264 void
          265 gcgi_print_gph(char type, char *desc, char *path, char *host, char *port)
          266 {
          267         assert(type >= 0x30);
          268         if (host == NULL)
          269                 host = "server";
          270         if (port == NULL)
          271                 port = "port";
          272         printf("[%c|%s|%s|%s|%s]\n", type, desc, path, host, port);
          273 }
          274 
          275 void
          276 gcgi_template(char const *path, struct gcgi_var_list *vars)
          277 {
          278         FILE *fp;
          279         size_t sz;
          280         char *line, *head, *tail, *key, *val;
          281 
          282         if ((fp = fopen(path, "r")) == NULL)
          283                 gcgi_fatal("opening template %s", path);
          284         sz = 0;
          285         line = NULL;
          286         while (getline(&line, &sz, fp) > 0) {
          287                 head = tail = line;
          288                 for (; (key = gcgi_next_var(head, &tail)); head = tail) {
          289                         fputs(head, stdout);
          290                         if ((val = gcgi_get_var(vars, key)))
          291                                 fputs(val, stdout);
          292                         else
          293                                 fprintf(stdout, "{{error:%s}}", key);
          294                 }
          295                 fputs(tail, stdout);
          296         }
          297         if (ferror(fp))
          298                 gcgi_fatal("reading from template: %s", strerror(errno));
          299         fclose(fp);
          300 }