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 }