gout

A static git page generator
git clone https://git.bracken.jp/gout.git
Log | Files | Refs | README | LICENSE

utils.c (3178B)


      1 #include "utils.h"
      2 
      3 #include <err.h>
      4 #include <errno.h>
      5 #include <limits.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 #include <sys/stat.h>
      9 #include <sys/types.h>
     10 
     11 #include "third_party/openbsd/strlcat.h"
     12 #include "third_party/openbsd/strlcpy.h"
     13 
     14 char* path_concat(char* out, size_t out_len, const char* p1, const char* p2) {
     15   size_t p1_len = strlen(p1);
     16   size_t p2_len = strlen(p2);
     17   if (p1_len == 0) {
     18     estrlcpy(out, p2, out_len);
     19     return out;
     20   }
     21   if (p2_len == 0) {
     22     estrlcpy(out, p1, out_len);
     23     return out;
     24   }
     25   if (p1_len + p2_len + 2 > out_len) {
     26     errx(1, "path truncated. p1: '%s' p2: '%s'", p1, p2);
     27   }
     28   if (snprintf(out, out_len, "%s/%s", p1, p2) < 0) {
     29     err(1, "snprintf");
     30   }
     31   return out;
     32 }
     33 
     34 int mkdirp(const char* path) {
     35   char mut_path[PATH_MAX];
     36   estrlcpy(mut_path, path, sizeof(mut_path));
     37 
     38   for (char* p = mut_path + (mut_path[0] == '/'); *p; p++) {
     39     if (*p != '/') {
     40       continue;
     41     }
     42     *p = '\0';
     43     if (mkdir(mut_path, S_IRWXU | S_IRWXG | S_IRWXO) < 0 && errno != EEXIST) {
     44       return -1;
     45     }
     46     *p = '/';
     47   }
     48   if (mkdir(mut_path, S_IRWXU | S_IRWXG | S_IRWXO) < 0 && errno != EEXIST) {
     49     return -1;
     50   }
     51   return 0;
     52 }
     53 
     54 void* ecalloc(size_t count, size_t size) {
     55   void* ptr = calloc(count, size);
     56   if (!ptr) {
     57     err(1, "calloc");
     58   }
     59   return ptr;
     60 }
     61 
     62 char* estrdup(const char* s) {
     63   char* out_str = strdup(s);
     64   if (!out_str) {
     65     err(1, "strcpy");
     66   }
     67   return out_str;
     68 }
     69 
     70 size_t estrlcpy(char* dst, const char* src, size_t dsize) {
     71   size_t len = strlcpy(dst, src, dsize);
     72   if (len >= dsize) {
     73     errx(1, "string truncated: '%s'", src);
     74   }
     75   return len;
     76 }
     77 
     78 size_t estrlcat(char* dst, const char* src, size_t dsize) {
     79   size_t len = strlcat(dst, src, dsize);
     80   if (len >= dsize) {
     81     errx(1, "string truncated: '%s'", src);
     82   }
     83   return len;
     84 }
     85 
     86 FILE* efopen(const char* filename, const char* flags) {
     87   FILE* fp = fopen(filename, flags);
     88   if (!fp) {
     89     err(1, "fopen: '%s'", filename);
     90   }
     91   return fp;
     92 }
     93 
     94 void checkfileerror(FILE* fp, const char* name, char mode) {
     95   if (mode == 'r' && ferror(fp)) {
     96     errx(1, "read error: %s", name);
     97   } else if (mode == 'w' && (fflush(fp) || ferror(fp))) {
     98     errx(1, "write error: %s", name);
     99   }
    100 }
    101 
    102 bool is_safe_repo_path(const char* path) {
    103   if (path[0] == '/') {
    104     return false;
    105   }
    106 
    107   const char* p = path;
    108   while (*p) {
    109     /* Locate the next path component, or end of path. */
    110     const char* start = p;
    111     while (*p && *p != '/') {
    112       p++;
    113     }
    114 
    115     /* Check for "..". */
    116     size_t len = p - start;
    117     if (len == 2 && start[0] == '.' && start[1] == '.') {
    118       return false;
    119     }
    120 
    121     /* Skip over the path delimiter. */
    122     if (*p) {
    123       p++;
    124     }
    125   }
    126   return true;
    127 }
    128 
    129 bool is_safe_url(const char* url) {
    130   return strncmp(url, "http://", 7) == 0 || strncmp(url, "https://", 8) == 0 ||
    131          strncmp(url, "git://", 6) == 0;
    132 }
    133 
    134 bool is_safe_mailto(const char* email) {
    135   /* A basic sanity check. We don't need a full RFC 5322 validator. We just
    136    * want to prevent injection of HTML/JS. Disallow characters that are special
    137    * in HTML or could be used to craft malicious URIs. */
    138   return strpbrk(email, "<>\"'\\\r\n") == NULL;
    139 }