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 { .name
= "b", .title
= "Random", /* */
39 .text_cooldown
= 20, .blank_cooldown
= 10, /* */
40 .threads_per_page
= 10, .num_pages
= 10 }, /* */
41 { .name
= "sf", .title
= "General SF", /* */
42 .text_cooldown
= 40, .blank_cooldown
= 20, /* */
43 .threads_per_page
= 10, .num_pages
= 20 }, /* */
47 * What's the tripcode salt?
49 static const char *trip_salt
=
50 "The color of television, tuned to a dead channel";
53 * Here are some error messages. They will be fed into fprintf, so
54 * they are kept as #defines so that the compiler can provide some
55 * surety against misuse.
59 * Here's the generic HTTP 400 template. For things like using the
60 * wrong method, uploading a file with the wrong mime type, etc.
61 * This should be a format suitable for feeding to printf with
62 * EXACTLY one %s, which is a brief explanation of the problem.
64 #define BAD_REQUEST_FMT \
66 "Content-type: text/html\r\n\r\n" \
70 "<meta http-equiv=\"Content-Type\" content=\"text/html; " \
71 "charset=utf-8\" />" \
72 "<title>Kiteo. His eyes closed.</title>" \
73 "<link rel=\"stylesheet\" type=\"text/css\" " \
74 "href=\"/css/futaba.css\" />" \
75 "<link rel=\"icon\" type=\"image/png\" href=\"/icon.png\" />" \
78 "<div class=\"center-wrapper\">" \
79 "<div class=\"notice-box\">" \
80 "<p class=\"notice-title\">Malformed request</p>" \
88 * And here's the ban message template. This time there should be
89 * EXACTLY TWO %s format specifier. The first one gives ban reason,
90 * the second gives ban expiry. If you want to reorder them, you
91 * can use %2$s and %1$s.
95 "Content-type: text/html\r\n\r\n" \
99 "<meta http-equiv=\"Content-Type\" content=\"text/html; " \
100 "charset=utf-8\" />" \
101 "<title>\u515c\u7532\u5150\u3001\u30de\u30b0\u30de\u306b" \
102 "\u6b7b\u3059!</title>" \
103 "<link rel=\"stylesheet\" type=\"text/css\" " \
104 "href=\"/css/futaba.css\" />" \
105 "<link rel=\"icon\" type=\"image/png\" href=\"/icon.png\" />" \
108 "<div class=\"center-wrapper\">" \
109 "<div class=\"notice-box\">" \
110 "<p class=\"notice-title\">You are banned! Σ(゚Д゚ )</p>" \
111 "<ul><li>Expiry: %s</li>" \
112 "<li>Reason: %s</li></ul>" \
119 * Here's what they get if they fail a challenge (see below). No %s at all.
121 #define BAD_CHALLENGE_FMT \
123 "Content-type: text/html\r\n\r\n" \
127 "<meta http-equiv=\"Content-Type\" content=\"text/html; " \
128 "charset=utf-8\" />" \
129 "<title>(It's not “swordfish”)</title>" \
130 "<link rel=\"stylesheet\" type=\"text/css\" " \
131 "href=\"/css/futaba.css\" />" \
132 "<link rel=\"icon\" type=\"image/png\" href=\"/icon.png\" />" \
135 "<div class=\"center-wrapper\">" \
136 "<div class=\"notice-box\">" \
137 "<p class=\"notice-title\">You gotta answer the challenge!</p>" \
138 "<p>It's for your own good, you know.</p>" \
145 * Here's what they get if they use the wrong HTTP method on /action.
148 #define BAD_METHOD_FMT \
150 "Content-type: text/html\r\n\r\n" \
154 "<meta http-equiv=\"Content-Type\" content=\"text/html; " \
155 "charset=utf-8\" />" \
156 "<title>This is highly unorthodox</title>" \
157 "<link rel=\"stylesheet\" type=\"text/css\" " \
158 "href=\"/css/futaba.css\" />" \
159 "<link rel=\"icon\" type=\"image/png\" href=\"/icon.png\" />" \
162 "<div class=\"center-wrapper\">" \
163 "<div class=\"notice-box\">" \
164 "<p class=\"notice-title\">That wasn't a POST</p>" \
165 "<p>We only take POSTs here.</p>" \
172 * Upload was large enough for the server to accept it, but overflowed
173 * some other bound. This should be a template suitable for feeding
174 * into printf, with EXACTLY one %s, which will be something like
175 * "file" or "subject" or "comment".
177 #define TOO_LARGE_FMT \
179 "Content-type: text/html\r\n\r\n" \
183 "<meta http-equiv=\"Content-Type\" content=\"text/html; " \
184 "charset=utf-8\" />" \
185 "<title>Once the Big Zam is uploaded...</title>" \
186 "<link rel=\"stylesheet\" type=\"text/css\" " \
187 "href=\"/css/futaba.css\" />" \
188 "<link rel=\"icon\" type=\"image/png\" href=\"/icon.png\" />" \
191 "<div class=\"center-wrapper\">" \
192 "<div class=\"notice-box\">" \
193 "<p class=\"notice-title\">Too large!</p>" \
194 "<p>%s too large.</p>" \
201 * User is posting when they've posted too recently. This should
202 * be a template suitable for feeding into printf, with EXACTLY one
203 * %s, which will be replaced with something like "25 seconds".
205 #define COOLDOWN_FMT \
207 "Content-type: text/html\r\n\r\n" \
211 "<meta http-equiv=\"Content-Type\" content=\"text/html; " \
212 "charset=utf-8\" />" \
213 "<title>This sensation, is it Char!?</title>" \
214 "<link rel=\"stylesheet\" type=\"text/css\" " \
215 "href=\"/css/futaba.css\" />" \
216 "<link rel=\"icon\" type=\"image/png\" href=\"/icon.png\" />" \
219 "<div class=\"center-wrapper\">" \
220 "<div class=\"notice-box\">" \
221 "<p class=\"notice-title\">Slow down!</p>" \
222 "<p>You're posting too fast! Try again in %s.</p>" \
229 * Some kind of server error occured: out of memory, invalid UTF-8
230 * generated, somewhere, etc. Apologize profusely.
232 #define INTERNAL_ERROR_FMT \
234 "Content-type: text/html\r\n\r\n" \
238 "<meta http-equiv=\"Content-Type\" content=\"text/html; " \
239 "charset=utf-8\" />" \
240 "<title>Bitter failure</title>" \
241 "<link rel=\"stylesheet\" type=\"text/css\" " \
242 "href=\"/css/futaba.css\" />" \
243 "<link rel=\"icon\" type=\"image/png\" href=\"/icon.png\" />" \
246 "<div class=\"center-wrapper\">" \
247 "<div class=\"notice-box\">" \
248 "<p class=\"notice-title\">Internal error</p>" \
249 "<p>Please accept our apologies. " \
250 "Something unexpected has happened.</p>" \
257 * Here's the "Post successful" screen. There's one %s, which is
258 * the page to redirect back to.
260 #define POST_SUCCESSFUL_FMT \
262 "Content-type: text/html\r\n\r\n" \
266 "<meta http-equiv=\"Content-Type\" content=\"text/html; " \
267 "charset=utf-8\" />" \
268 "<meta http-equiv=\"refresh\" content=\"3;URL='%s'\" />" \
269 "<title>\u9280\u6cb3\u306e\u6b74\u53f2\u304c\u307e\u305f1\u30da" \
270 "\u30fc\u30b8</title>" \
271 "<link rel=\"stylesheet\" type=\"text/css\" " \
272 "href=\"/css/futaba.css\" />" \
273 "<link rel=\"icon\" type=\"image/png\" href=\"/icon.png\" />" \
276 "<div class=\"center-wrapper\">" \
277 "<div class=\"notice-box\">" \
278 "<p class=\"notice-title\">Post successful</p>" \
279 "<p>Returning you in 3\u20262\u20261\u2026</p>" \
286 * How large of a multipart/form-data (in bytes) should we listen
287 * to before throwing 413? This should be a bit larger than the
288 * max file size, but not large enough to cause DoS by malloc().
290 * This is separate from any upper limit configured in the web
291 * server. The web server's limit should be well above this value.
293 static const size_t max_form_data_size
= (5 * (1 << 20));
296 * How large of a file (in bytes) should we accept?
298 static const size_t max_file_size
= (4 * (1 << 20));
301 * How long of a comment (in bytes) should we accept?
303 static const size_t max_text_len
= (3 << 10);
306 * What mimetypes are allowed? Which ones should be thumbnailed (by
307 * what external command?), and which ones should simply have a
308 * fixed thumbnail (and where is it on the server?)
310 * This is handled by libmagic(3), in case you're wondering about
311 * that. We don't trust the Content-Type of the upload (but we do
312 * trust libmagic, and we trust libmagic to trust things... ugh).
314 * Please note that all format strings should take two arguments
315 * (%s). The first is the source, the latter is destination. If
316 * they need to be used multiple times, %1$s and %2$s will work.
318 static const struct filetype filetypes
[] = {
320 { .mime_type
= "image/jpeg", /* */
322 .install_command
= "jhead -purejpg %1$s >/dev/null" /* */
323 " 2>/dev/null && cp %1$s %2$s", /* */
324 .thumb_creation_command
= "convert %s -thumbnail" /* */
325 " 150x150 %s" }, /* */
326 { .mime_type
= "image/gif", /* */
328 .install_command
= "mv %s %s", /* */
329 .thumb_creation_command
= "convert %s[0] -thumbnail" /* */
330 " 150x150 %s" }, /* */
331 { .mime_type
= "image/png", /* */
333 .install_command
= "mv %s %s", /* */
334 .thumb_creation_command
= "convert %s -thumbnail" /* */
335 " 150x150 %s" }, /* */
336 { .mime_type
= "video/webm", /* */
338 .install_command
= "ffmpeg -y -i %s -an -c:v copy %s" /* */
339 " >/dev/null 2>/dev/null", /* */
340 .thumb_creation_command
=
341 "ffmpeg -y -i %s -c:v mjpeg -ss 0 -vframes 1" /* */
342 " -an -vf scale=w=150:h=150:force_original" /* */
343 "_aspect_ratio=decrease -f rawvideo %s" /* */
344 " >/dev/null 2>/dev/null" }, /* */
345 { .mime_type
= "application/epub+zip", /* */
346 .ext
= "epub", .install_command
= "mv %s %s", /* */
347 .static_thumbnail
= "/_icons/book.jpg" },
351 * Information about a file should be displayed: something like
352 * "webm, 201KB, 00:36". Where is the program which prints this
355 * The script will be passed two arguments. The first is the mimetype
356 * of the file, a string like "image/jpeg" or "application/epub+zip".
357 * The second will be an absolute path to the file itself.
359 * A decent starting point is located at tools/describe-file.sh of
360 * the source repository. The Makefile installs this to something
361 * like /usr/bin/rb79-describe-file, so that's what the default
362 * configuration uses.
364 static const char *file_description_prog
= "/usr/bin/rb79-describe-file";
366 /* A list of all possible header images, randomly selected from */
367 static const char *headers
[] = {
369 "/_banners/1.png", /* */
370 "/_banners/2.png", /* */
371 "/_banners/3.png", /* */
372 "/_banners/4.png", /* */
376 * Spambot traps (you can give up to 5 answers for each question,
379 static const struct challenge challenges
[] = {
381 { .question
= "<img src=\"/_hints/1.png\" alt=\"B__L\" />" /* */
382 "What is the more common name of the RB-79?", /* */
383 .answers
= { "ball", 0 } }, /* */
384 { .question
= "<img src=\"/_hints/2.png\" alt=\"H___\" />" /* */
385 " Which spherical toy did Amuro build?", /* */
386 .answers
= { "haro", "ball", 0 } }, /* */
387 { .question
= "<img src=\"/_hints/3.png\" " /* */
388 "alt=\"Is______\"/> Where did the Fifth " /* */
389 "Battle of Iserlohn happen? ", /* */
390 .answers
= { "iserlohn", 0 } }, /* */
391 { .question
= "<img src=\"/_hints/4.png\" " /* */
392 "alt=\"J______\"/> Which planet is the " /* */
393 "source of all evil? ", /* */
394 .answers
= { "jupiter", 0 } }, /* */
395 { .question
= "<img src=\"/_hints/5.png\" " /* */
396 "alt=\"B_s____l\"/> What is Sisko's " /* */
397 "favorite sport?", /* */
398 .answers
= { "baseball", 0 } },
402 * What are the wordfilters? pattern will be compiled by pcre2 with
403 * only the UTF compatible option, replace will be what the whole
404 * match gets replaced with.
406 * If you want case-insensitive, put (?i) at the start. If you want
407 * fancy backreferences, send a patch. If you want to replace with
408 * unicode, use \u1234 (this happens before HTML-escaping). If you
409 * want to use this to implement [spoiler], please don't.
411 static const struct wordfilter_input wordfilter_inputs
[] = {
413 { .pattern
= "(?i)nina purpleton", .replacement
= "worst girl" },
414 { .pattern
= "(?i)\\bkes\\b", .replacement
= "Quess" },
415 { .pattern
= "(?i)\\bquess\\b", .replacement
= "Kes" },
416 { .pattern
= "(?i).*?\\b(smh|fam|tbh|succ|thicc)\\b.*",
417 .replacement
= "( \u0361\u00b0 \u035c\u0296 \u0361\u00b0)" },