snac2

Fork of https://codeberg.org/grunfink/snac2
git clone https://git.inz.fi/snac2
Log | Files | Refs | README | LICENSE

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 }