gout

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

utils.c (3794B)


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