snac.c (4237B)
1 /* snac - A simple, minimalistic ActivityPub instance */ 2 /* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ 3 4 #define XS_IMPLEMENTATION 5 6 #include "xs.h" 7 #include "xs_hex.h" 8 #include "xs_io.h" 9 #include "xs_unicode_tbl.h" 10 #include "xs_unicode.h" 11 #include "xs_json.h" 12 #include "xs_curl.h" 13 #include "xs_openssl.h" 14 #include "xs_socket.h" 15 #include "xs_unix_socket.h" 16 #include "xs_url.h" 17 #include "xs_httpd.h" 18 #include "xs_mime.h" 19 #include "xs_regex.h" 20 #include "xs_set.h" 21 #include "xs_time.h" 22 #include "xs_glob.h" 23 #include "xs_random.h" 24 #include "xs_match.h" 25 #include "xs_fcgi.h" 26 #include "xs_html.h" 27 #include "xs_po.h" 28 #include "xs_webmention.h" 29 30 #include "snac.h" 31 32 #include <sys/time.h> 33 #include <sys/stat.h> 34 #include <fcntl.h> 35 36 xs_str *srv_basedir = NULL; 37 xs_dict *srv_config = NULL; 38 xs_str *srv_baseurl = NULL; 39 xs_str *srv_proxy_token_seed = NULL; 40 xs_dict *srv_langs = NULL; 41 42 int dbglevel = 0; 43 44 int mkdiratx(int base, const char *pathname) 45 { 46 int ret; 47 48 if ((ret = mkdirat(base, pathname, DIR_PERM)) != -1) { 49 /* try to the set the setgid bit, to allow system users 50 to create files in these directories using the 51 command-line tool. This may fail in some restricted 52 environments, but it's of no use there anyway */ 53 fchmodat(base, pathname, DIR_PERM_ADD, 0); 54 } 55 if (errno == EEXIST) 56 return 0; 57 58 return ret; 59 } 60 61 int mkdirx(const char *pathname) 62 /* creates a directory with special permissions */ 63 { 64 return mkdiratx(AT_FDCWD, pathname); 65 } 66 67 68 int valid_status(int status) 69 /* is this HTTP status valid? */ 70 { 71 return status >= 200 && status <= 299; 72 } 73 74 75 xs_str *tid(int offset) 76 /* returns a time-based Id */ 77 { 78 struct timeval tv; 79 80 gettimeofday(&tv, NULL); 81 82 return xs_fmt("%010ld.%06ld", (long)tv.tv_sec + (long)offset, (long)tv.tv_usec); 83 } 84 85 86 double ftime(void) 87 /* returns the UNIX time as a float */ 88 { 89 xs *ntid = tid(0); 90 91 return atof(ntid); 92 } 93 94 95 int validate_uid(const char *uid) 96 /* returns if uid is a valid identifier */ 97 { 98 if (!uid || *uid == '\0') 99 return 0; 100 101 while (*uid) { 102 if (!(isalnum(*uid) || *uid == '_')) 103 return 0; 104 105 uid++; 106 } 107 108 return 1; 109 } 110 111 112 void srv_log(xs_str *str) 113 /* logs a debug message */ 114 { 115 if (xs_str_in(str, srv_basedir) != -1) { 116 /* replace basedir with ~ */ 117 str = xs_replace_i(str, srv_basedir, "~"); 118 } 119 120 xs *tm = xs_str_localtime(0, "%H:%M:%S"); 121 fprintf(stderr, "%s %s\n", tm, str); 122 123 /* if the ~/log/ folder exists, also write to a file there */ 124 xs *dt = xs_str_localtime(0, "%Y-%m-%d"); 125 xs *lf = xs_fmt("%s/log/%s.log", srv_basedir, dt); 126 FILE *f; 127 if ((f = fopen(lf, "a")) != NULL) { 128 fprintf(f, "%s %s\n", tm, str); 129 fclose(f); 130 } 131 132 xs_free(str); 133 } 134 135 136 void snac_log(snac *snac, xs_str *str) 137 /* prints a user debugging information */ 138 { 139 xs *o_str = str; 140 xs_str *msg = xs_fmt("[%s] %s", snac->uid, o_str); 141 142 if (xs_str_in(msg, snac->basedir) != -1) { 143 /* replace long basedir references with ~ */ 144 msg = xs_replace_i(msg, snac->basedir, "~"); 145 } 146 147 srv_log(msg); 148 } 149 150 151 xs_str *hash_password(const char *uid, const char *passwd, const char *nonce) 152 /* hashes a password */ 153 { 154 xs *d_nonce = NULL; 155 xs *combi; 156 xs *hash; 157 158 if (nonce == NULL) { 159 unsigned int r; 160 xs_rnd_buf(&r, sizeof(r)); 161 d_nonce = xs_fmt("%08x", r); 162 nonce = d_nonce; 163 } 164 165 combi = xs_fmt("%s:%s:%s", nonce, uid, passwd); 166 hash = xs_sha1_hex(combi, strlen(combi)); 167 168 return xs_fmt("%s:%s", nonce, hash); 169 } 170 171 172 int check_password(const char *uid, const char *passwd, const char *hash) 173 /* checks a password */ 174 { 175 int ret = 0; 176 xs *spl = xs_split_n(hash, ":", 1); 177 178 if (xs_list_len(spl) == 2) { 179 xs *n_hash = hash_password(uid, passwd, xs_list_get(spl, 0)); 180 181 ret = (strcmp(hash, n_hash) == 0); 182 } 183 184 return ret; 185 } 186 187 188 const char *http_status_text(int status) 189 /* translate status codes to canonical status texts */ 190 { 191 switch (status) { 192 case 599: return "Timeout"; 193 #define HTTP_STATUS(code, name, text) case HTTP_STATUS_ ## name: return #text; 194 #include "http_codes.h" 195 #undef HTTP_STATUS 196 default: return "Unknown"; 197 } 198 }