gout

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

utils.c (3312B)


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