gout

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

utils.c (3291B)


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