2 * Configure your site here
6 * Where is the directory full of static files that we'll be
7 * modifying? (absolute path to a folder, please)
9 static const char *static_www_folder
= "/var/www/rb79";
12 * So, we have to have databases and lockfiles and stuff. Where
13 * do they go? Probably somewhere far away from network access, and
14 * regularly backed-up, I'd guess. (absolute path to a folder,
17 static const char *work_path
= "/opt/rb79";
20 * When we convert files (stripping EXIF out of jpegs, etc.) they
21 * go to a temporary directory, which is created through mkdtemp.
22 * What is the template for that directory name? It should end in
23 * "XXXXXX", and it should be somewhere that isn't automatically
24 * destroyed (e.g. by a cleanup cronjob).
26 static const char *temp_dir_template
= "/tmp/rb79_conv_XXXXXX";
29 * The boards: each name should just be a folder inside static_www_folder.
30 * You should probably avoid names starting with underscores since
31 * I reserve those names to put templates and stuff in.
33 static const struct board boards
[] = {
35 { .name
= "m", .title
= "Mecha", /* */
36 .text_cooldown
= 40, .blank_cooldown
= 20, /* */
37 .threads_per_page
= 10, .num_pages
= 20, /* */
38 .appears_in_recent
= 1 }, /* */
39 { .name
= "b", .title
= "Random", /* */
40 .text_cooldown
= 20, .blank_cooldown
= 10, /* */
41 .threads_per_page
= 10, .num_pages
= 10, /* */
42 .appears_in_recent
= 0 }, /* */
43 { .name
= "sf", .title
= "General SF", /* */
44 .text_cooldown
= 40, .blank_cooldown
= 20, /* */
45 .threads_per_page
= 10, .num_pages
= 20, /* */
46 .appears_in_recent
= 1 }, /* */
50 * What's the tripcode salt?
52 static const char *trip_salt
=
53 "The color of television, tuned to a dead channel";
56 * Here are some error messages. They will be fed into fprintf, so
57 * they are kept as #defines so that the compiler can provide some
58 * surety against misuse.
62 * Here's the generic HTTP 400 template. For things like using the
63 * wrong method, uploading a file with the wrong mime type, etc.
64 * This should be a format suitable for feeding to printf with
65 * EXACTLY one %s, which is a brief explanation of the problem.
67 #define BAD_REQUEST_FMT \
69 "Content-type: text/html\r\n\r\n" \
73 "<meta http-equiv=\"Content-Type\" content=\"text/html; " \
74 "charset=utf-8\" />" \
75 "<title>Kiteo. His eyes closed.</title>" \
76 "<link rel=\"stylesheet\" type=\"text/css\" " \
77 "href=\"/css/futaba.css\" />" \
78 "<link rel=\"icon\" type=\"image/png\" href=\"/icon.png\" />" \
81 "<div class=\"center-wrapper\">" \
82 "<div class=\"notice-box\">" \
83 "<p class=\"notice-title\">Malformed request</p>" \
91 * And here's the ban message template. This time there should be
92 * EXACTLY TWO %s format specifier. The first one gives ban reason,
93 * the second gives ban expiry. If you want to reorder them, you
94 * can use %2$s and %1$s.
98 "Content-type: text/html\r\n\r\n" \
102 "<meta http-equiv=\"Content-Type\" content=\"text/html; " \
103 "charset=utf-8\" />" \
104 "<title>\u515c\u7532\u5150\u3001\u30de\u30b0\u30de\u306b" \
105 "\u6b7b\u3059!</title>" \
106 "<link rel=\"stylesheet\" type=\"text/css\" " \
107 "href=\"/css/futaba.css\" />" \
108 "<link rel=\"icon\" type=\"image/png\" href=\"/icon.png\" />" \
111 "<div class=\"center-wrapper\">" \
112 "<div class=\"notice-box\">" \
113 "<p class=\"notice-title\">You are banned! Σ(゚Д゚ )</p>" \
114 "<ul><li>Expiry: %s</li>" \
115 "<li>Reason: %s</li></ul>" \
122 * Here's what they get if they fail a challenge (see below). No %s at all.
124 #define BAD_CHALLENGE_FMT \
126 "Content-type: text/html\r\n\r\n" \
130 "<meta http-equiv=\"Content-Type\" content=\"text/html; " \
131 "charset=utf-8\" />" \
132 "<title>(It's not “swordfish”)</title>" \
133 "<link rel=\"stylesheet\" type=\"text/css\" " \
134 "href=\"/css/futaba.css\" />" \
135 "<link rel=\"icon\" type=\"image/png\" href=\"/icon.png\" />" \
138 "<div class=\"center-wrapper\">" \
139 "<div class=\"notice-box\">" \
140 "<p class=\"notice-title\">You gotta answer the challenge!</p>" \
141 "<p>It's for your own good, you know.</p>" \
148 * Here's what they get if they use the wrong HTTP method on /action.
151 #define BAD_METHOD_FMT \
153 "Content-type: text/html\r\n\r\n" \
157 "<meta http-equiv=\"Content-Type\" content=\"text/html; " \
158 "charset=utf-8\" />" \
159 "<title>This is highly unorthodox</title>" \
160 "<link rel=\"stylesheet\" type=\"text/css\" " \
161 "href=\"/css/futaba.css\" />" \
162 "<link rel=\"icon\" type=\"image/png\" href=\"/icon.png\" />" \
165 "<div class=\"center-wrapper\">" \
166 "<div class=\"notice-box\">" \
167 "<p class=\"notice-title\">That wasn't a POST</p>" \
168 "<p>We only take POSTs here.</p>" \
175 * Upload was large enough for the server to accept it, but overflowed
176 * some other bound. This should be a template suitable for feeding
177 * into printf, with EXACTLY one %s, which will be something like
178 * "file" or "subject" or "comment".
180 #define TOO_LARGE_FMT \
182 "Content-type: text/html\r\n\r\n" \
186 "<meta http-equiv=\"Content-Type\" content=\"text/html; " \
187 "charset=utf-8\" />" \
188 "<title>Once the Big Zam is uploaded...</title>" \
189 "<link rel=\"stylesheet\" type=\"text/css\" " \
190 "href=\"/css/futaba.css\" />" \
191 "<link rel=\"icon\" type=\"image/png\" href=\"/icon.png\" />" \
194 "<div class=\"center-wrapper\">" \
195 "<div class=\"notice-box\">" \
196 "<p class=\"notice-title\">Too large!</p>" \
197 "<p>%s too large.</p>" \
204 * User is posting when they've posted too recently. This should
205 * be a template suitable for feeding into printf, with EXACTLY one
206 * %s, which will be replaced with something like "25 seconds".
208 #define COOLDOWN_FMT \
210 "Content-type: text/html\r\n\r\n" \
214 "<meta http-equiv=\"Content-Type\" content=\"text/html; " \
215 "charset=utf-8\" />" \
216 "<title>This sensation, is it Char!?</title>" \
217 "<link rel=\"stylesheet\" type=\"text/css\" " \
218 "href=\"/css/futaba.css\" />" \
219 "<link rel=\"icon\" type=\"image/png\" href=\"/icon.png\" />" \
222 "<div class=\"center-wrapper\">" \
223 "<div class=\"notice-box\">" \
224 "<p class=\"notice-title\">Slow down!</p>" \
225 "<p>You're posting too fast! Try again in %s.</p>" \
232 * Some kind of server error occured: out of memory, invalid UTF-8
233 * generated, somewhere, etc. Apologize profusely.
235 #define INTERNAL_ERROR_FMT \
237 "Content-type: text/html\r\n\r\n" \
241 "<meta http-equiv=\"Content-Type\" content=\"text/html; " \
242 "charset=utf-8\" />" \
243 "<title>Bitter failure</title>" \
244 "<link rel=\"stylesheet\" type=\"text/css\" " \
245 "href=\"/css/futaba.css\" />" \
246 "<link rel=\"icon\" type=\"image/png\" href=\"/icon.png\" />" \
249 "<div class=\"center-wrapper\">" \
250 "<div class=\"notice-box\">" \
251 "<p class=\"notice-title\">Internal error</p>" \
252 "<p>Please accept our apologies. " \
253 "Something unexpected has happened.</p>" \
260 * Here's the "Post successful" screen. There's one %s, which is
261 * the page to redirect back to.
263 #define POST_SUCCESSFUL_FMT \
265 "Content-type: text/html\r\n\r\n" \
269 "<meta http-equiv=\"Content-Type\" content=\"text/html; " \
270 "charset=utf-8\" />" \
271 "<meta http-equiv=\"refresh\" content=\"3;URL='%s'\" />" \
272 "<title>\u9280\u6cb3\u306e\u6b74\u53f2\u304c\u307e\u305f1\u30da" \
273 "\u30fc\u30b8</title>" \
274 "<link rel=\"stylesheet\" type=\"text/css\" " \
275 "href=\"/css/futaba.css\" />" \
276 "<link rel=\"icon\" type=\"image/png\" href=\"/icon.png\" />" \
279 "<div class=\"center-wrapper\">" \
280 "<div class=\"notice-box\">" \
281 "<p class=\"notice-title\">Post successful</p>" \
282 "<p>Returning you in 3\u20262\u20261\u2026</p>" \
289 * How large of a multipart/form-data (in bytes) should we listen
290 * to before throwing 413? This should be a bit larger than the
291 * max file size, but not large enough to cause DoS by malloc().
293 * This is separate from any upper limit configured in the web
294 * server. The web server's limit should be well above this value.
296 static const size_t max_form_data_size
= (5 * (1 << 20));
299 * How large of a file (in bytes) should we accept?
301 static const size_t max_file_size
= (4 * (1 << 20));
304 * How long of a comment (in bytes) should we accept?
306 static const size_t max_text_len
= (3 << 10);
309 * What mimetypes are allowed? Which ones should be thumbnailed (by
310 * what external command?), and which ones should simply have a
311 * fixed thumbnail (and where is it on the server?)
313 * This is handled by libmagic(3), in case you're wondering about
314 * that. We don't trust the Content-Type of the upload (but we do
315 * trust libmagic, and we trust libmagic to trust things... ugh).
317 * Please note that all format strings should take two arguments
318 * (%s). The first is the source, the latter is destination. If
319 * they need to be used multiple times, %1$s and %2$s will work.
321 static const struct filetype filetypes
[] = {
323 { .mime_type
= "image/jpeg", /* */
325 .install_command
= "jhead -purejpg %1$s >/dev/null" /* */
326 " 2>/dev/null && cp %1$s %2$s", /* */
327 .thumb_creation_command
= "convert %s -thumbnail" /* */
328 " 150x150 %s" }, /* */
329 { .mime_type
= "image/gif", /* */
331 .install_command
= "mv %s %s", /* */
332 .thumb_creation_command
= "convert %s[0] -thumbnail" /* */
333 " 150x150 %s" }, /* */
334 { .mime_type
= "image/png", /* */
336 .install_command
= "mv %s %s", /* */
337 .thumb_creation_command
= "convert %s -thumbnail" /* */
338 " 150x150 %s" }, /* */
339 { .mime_type
= "video/webm", /* */
341 .install_command
= "ffmpeg -y -i %s -an -c:v copy %s" /* */
342 " >/dev/null 2>/dev/null", /* */
343 .thumb_creation_command
=
344 "ffmpeg -y -i %s -c:v mjpeg -ss 0 -vframes 1" /* */
345 " -an -vf scale=w=150:h=150:force_original" /* */
346 "_aspect_ratio=decrease -f rawvideo %s" /* */
347 " >/dev/null 2>/dev/null" }, /* */
348 { .mime_type
= "application/epub+zip", /* */
349 .ext
= "epub", .install_command
= "mv %s %s", /* */
350 .static_thumbnail
= "/_icons/book.jpg" },
354 * Information about a file should be displayed: something like
355 * "webm, 201KB, 00:36". Where is the program which prints this
358 * The script will be passed two arguments. The first is the mimetype
359 * of the file, a string like "image/jpeg" or "application/epub+zip".
360 * The second will be an absolute path to the file itself.
362 * A decent starting point is located at tools/describe-file.sh of
363 * the source repository. The Makefile installs this to something
364 * like /usr/bin/rb79-describe-file, so that's what the default
365 * configuration uses.
367 static const char *file_description_prog
= "/usr/bin/rb79-describe-file";
369 /* A list of all possible header images, randomly selected from */
370 static const char *headers
[] = {
372 "/_banners/1.png", /* */
373 "/_banners/2.png", /* */
374 "/_banners/3.png", /* */
375 "/_banners/4.png", /* */
379 * Spambot traps (you can give up to 5 answers for each question,
382 static const struct challenge challenges
[] = {
384 { .question
= "<img src=\"/_hints/1.png\" alt=\"B__L\" />" /* */
385 "What is the more common name of the RB-79?", /* */
386 .answers
= { "ball", 0 } }, /* */
387 { .question
= "<img src=\"/_hints/2.png\" alt=\"H___\" />" /* */
388 " Which spherical toy did Amuro build?", /* */
389 .answers
= { "haro", "ball", 0 } }, /* */
390 { .question
= "<img src=\"/_hints/3.png\" " /* */
391 "alt=\"Is______\"/> Where did the Fifth " /* */
392 "Battle of Iserlohn happen? ", /* */
393 .answers
= { "iserlohn", 0 } }, /* */
394 { .question
= "<img src=\"/_hints/4.png\" " /* */
395 "alt=\"J______\"/> Which planet is the " /* */
396 "source of all evil? ", /* */
397 .answers
= { "jupiter", 0 } }, /* */
398 { .question
= "<img src=\"/_hints/5.png\" " /* */
399 "alt=\"B_s____l\"/> What is Sisko's " /* */
400 "favorite sport?", /* */
401 .answers
= { "baseball", 0 } },
405 * What are the wordfilters? pattern will be compiled by pcre2 with
406 * only the UTF compatible option, replace will be what the whole
407 * match gets replaced with.
409 * If you want case-insensitive, put (?i) at the start. If you want
410 * fancy backreferences, send a patch. If you want to replace with
411 * unicode, use \u1234 (this happens before HTML-escaping). If you
412 * want to use this to implement [spoiler], please don't.
414 static const struct wordfilter_input wordfilter_inputs
[] = {
416 { .pattern
= "(?i)nina purpleton", .replacement
= "worst girl" },
417 { .pattern
= "(?i)\\bkes\\b", .replacement
= "Quess" },
418 { .pattern
= "(?i)\\bquess\\b", .replacement
= "Kes" },
419 { .pattern
= "(?i).*?\\b(smh|fam|tbh|succ|thicc)\\b.*",
420 .replacement
= "( \u0361\u00b0 \u035c\u0296 \u0361\u00b0)" },
424 * What are some phrases that can't be posted? As with wordfilters,
425 * pattern will be compiled with pcre2, only with the UTF compatible
426 * option. ban_duration is (if non-zero) the amount of time they'll be
427 * banned for, with reason ban_reason (secret if empty)
429 static const struct forbidden_input forbidden_inputs
[] = {
431 { .pattern
= "Actually [Ss][Ee][Ee][Dd] Destiny was good",
432 .ban_duration
= 43200, .ban_reason
= "I disagree" },
433 { .pattern
= "dick pills", .ban_duration
= 31536000,