data.c - quark - quark web server
 (HTM) git clone git://git.suckless.org/quark
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
       data.c (4933B)
       ---
            1 /* See LICENSE file for copyright and license details. */
            2 #include <dirent.h>
            3 #include <stdio.h>
            4 #include <stdlib.h>
            5 #include <string.h>
            6 #include <sys/stat.h>
            7 #include <time.h>
            8 #include <unistd.h>
            9 
           10 #include "data.h"
           11 #include "http.h"
           12 #include "util.h"
           13 
           14 enum status (* const data_fct[])(const struct response *,
           15                                  struct buffer *, size_t *) = {
           16         [RESTYPE_DIRLISTING] = data_prepare_dirlisting_buf,
           17         [RESTYPE_ERROR]      = data_prepare_error_buf,
           18         [RESTYPE_FILE]       = data_prepare_file_buf,
           19 };
           20 
           21 static int
           22 compareent(const struct dirent **d1, const struct dirent **d2)
           23 {
           24         int v;
           25 
           26         v = ((*d2)->d_type == DT_DIR ? 1 : -1) -
           27             ((*d1)->d_type == DT_DIR ? 1 : -1);
           28         if (v) {
           29                 return v;
           30         }
           31 
           32         return strcmp((*d1)->d_name, (*d2)->d_name);
           33 }
           34 
           35 static char *
           36 suffix(int t)
           37 {
           38         switch (t) {
           39         case DT_FIFO: return "|";
           40         case DT_DIR:  return "/";
           41         case DT_LNK:  return "@";
           42         case DT_SOCK: return "=";
           43         }
           44 
           45         return "";
           46 }
           47 
           48 static void
           49 html_escape(const char *src, char *dst, size_t dst_siz)
           50 {
           51         const struct {
           52                 char c;
           53                 char *s;
           54         } escape[] = {
           55                 { '&',  "&amp;"  },
           56                 { '<',  "&lt;"   },
           57                 { '>',  "&gt;"   },
           58                 { '"',  "&quot;" },
           59                 { '\'', "&#x27;" },
           60         };
           61         size_t i, j, k, esclen;
           62 
           63         for (i = 0, j = 0; src[i] != '\0'; i++) {
           64                 for (k = 0; k < LEN(escape); k++) {
           65                         if (src[i] == escape[k].c) {
           66                                 break;
           67                         }
           68                 }
           69                 if (k == LEN(escape)) {
           70                         /* no escape char at src[i] */
           71                         if (j == dst_siz - 1) {
           72                                 /* silent truncation */
           73                                 break;
           74                         } else {
           75                                 dst[j++] = src[i];
           76                         }
           77                 } else {
           78                         /* escape char at src[i] */
           79                         esclen = strlen(escape[k].s);
           80 
           81                         if (j >= dst_siz - esclen) {
           82                                 /* silent truncation */
           83                                 break;
           84                         } else {
           85                                 memcpy(&dst[j], escape[k].s, esclen);
           86                                 j += esclen;
           87                         }
           88                 }
           89         }
           90         dst[j] = '\0';
           91 }
           92 
           93 enum status
           94 data_prepare_dirlisting_buf(const struct response *res,
           95                             struct buffer *buf, size_t *progress)
           96 {
           97         enum status s = 0;
           98         struct dirent **e;
           99         size_t i;
          100         int dirlen;
          101         char esc[PATH_MAX /* > NAME_MAX */ * 6]; /* strlen("&...;") <= 6 */
          102 
          103         /* reset buffer */
          104         memset(buf, 0, sizeof(*buf));
          105 
          106         /* read directory */
          107         if ((dirlen = scandir(res->internal_path, &e, NULL, compareent)) < 0) {
          108                 return S_FORBIDDEN;
          109         }
          110 
          111         if (*progress == 0) {
          112                 /* write listing header (sizeof(esc) >= PATH_MAX) */
          113                 html_escape(res->path, esc, MIN(PATH_MAX, sizeof(esc)));
          114                 if (buffer_appendf(buf,
          115                                    "<!DOCTYPE html>\n<html>\n\t<head>"
          116                                    "<title>Index of %s</title></head>\n"
          117                                    "\t<body>\n\t\t<a href=\"..\">..</a>",
          118                                    esc) < 0) {
          119                         s = S_REQUEST_TIMEOUT;
          120                         goto cleanup;
          121                 }
          122         }
          123 
          124         /* listing entries */
          125         for (i = *progress; i < (size_t)dirlen; i++) {
          126                 /* skip hidden files, "." and ".." */
          127                 if (e[i]->d_name[0] == '.') {
          128                         continue;
          129                 }
          130 
          131                 /* entry line */
          132                 html_escape(e[i]->d_name, esc, sizeof(esc));
          133                 if (buffer_appendf(buf,
          134                                    "<br />\n\t\t<a href=\"%s%s\">%s%s</a>",
          135                                    esc,
          136                                    (e[i]->d_type == DT_DIR) ? "/" : "",
          137                                    esc,
          138                                    suffix(e[i]->d_type))) {
          139                         /* buffer full */
          140                         break;
          141                 }
          142         }
          143         *progress = i;
          144 
          145         if (*progress == (size_t)dirlen) {
          146                 /* listing footer */
          147                 if (buffer_appendf(buf, "\n\t</body>\n</html>\n") < 0) {
          148                         s = S_REQUEST_TIMEOUT;
          149                         goto cleanup;
          150                 }
          151                 (*progress)++;
          152         }
          153 
          154 cleanup:
          155         while (dirlen--) {
          156                 free(e[dirlen]);
          157         }
          158         free(e);
          159 
          160         return s;
          161 }
          162 
          163 enum status
          164 data_prepare_error_buf(const struct response *res, struct buffer *buf,
          165                    size_t *progress)
          166 {
          167         /* reset buffer */
          168         memset(buf, 0, sizeof(*buf));
          169 
          170         if (*progress == 0) {
          171                 /* write error body */
          172                 if (buffer_appendf(buf,
          173                                    "<!DOCTYPE html>\n<html>\n\t<head>\n"
          174                                    "\t\t<title>%d %s</title>\n\t</head>\n"
          175                                    "\t<body>\n\t\t<h1>%d %s</h1>\n"
          176                                    "\t</body>\n</html>\n",
          177                                    res->status, status_str[res->status],
          178                                    res->status, status_str[res->status])) {
          179                         return S_INTERNAL_SERVER_ERROR;
          180                 }
          181                 (*progress)++;
          182         }
          183 
          184         return 0;
          185 }
          186 
          187 enum status
          188 data_prepare_file_buf(const struct response *res, struct buffer *buf,
          189                   size_t *progress)
          190 {
          191         FILE *fp;
          192         enum status s = 0;
          193         ssize_t r;
          194         size_t remaining;
          195 
          196         /* reset buffer */
          197         memset(buf, 0, sizeof(*buf));
          198 
          199         /* open file */
          200         if (!(fp = fopen(res->internal_path, "r"))) {
          201                 s = S_FORBIDDEN;
          202                 goto cleanup;
          203         }
          204 
          205         /* seek to lower bound + progress */
          206         if (fseek(fp, res->file.lower + *progress, SEEK_SET)) {
          207                 s = S_INTERNAL_SERVER_ERROR;
          208                 goto cleanup;
          209         }
          210 
          211         /* read data into buf */
          212         remaining = res->file.upper - res->file.lower + 1 - *progress;
          213         while ((r = fread(buf->data + buf->len, 1,
          214                           MIN(sizeof(buf->data) - buf->len,
          215                           remaining), fp))) {
          216                 if (r < 0) {
          217                         s = S_INTERNAL_SERVER_ERROR;
          218                         goto cleanup;
          219                 }
          220                 buf->len += r;
          221                 *progress += r;
          222                 remaining -= r;
          223         }
          224 
          225 cleanup:
          226         if (fp) {
          227                 fclose(fp);
          228         }
          229 
          230         return s;
          231 }