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 }