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 }