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