From c2ba75793e78afb00223003ddfcbc05714bd01bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20Ankarstr=C3=B6m?= <john@ankarstrom.se>
Date: Sun, 19 Sep 2021 01:45:42 +0200
Subject: Add simple captcha

---
 README      |  8 ++++----
 ctl.c       | 58 +++++++++++++++++++++++++++++++++++++++++++++-------------
 t/head.t    |  1 +
 t/newuser.t |  6 +++++-
 4 files changed, 55 insertions(+), 18 deletions(-)

diff --git a/README b/README
index 01cf301..93d4c80 100644
--- a/README
+++ b/README
@@ -9,7 +9,7 @@ It is also rather small:
 
 	wc -l *.c *.h */*.t */*.lex
 	     101 cforum.c
-	     267 ctl.c
+	     299 ctl.c
 	     268 db.c
 	      10 err.c
 	     144 query.c
@@ -21,9 +21,9 @@ It is also rather small:
 	       3 t/err.t
 	       1 t/foot.t
 	      29 t/front.t
-	      14 t/head.t
-	      34 t/newuser.t
+	      15 t/head.t
+	      38 t/newuser.t
 	      28 t/post.t
 	      12 t/user.t
 	      95 mktpl/mktpl.lex
-	    1080 total
+	    1117 total
diff --git a/ctl.c b/ctl.c
index a3eb920..91761cc 100644
--- a/ctl.c
+++ b/ctl.c
@@ -54,12 +54,14 @@ newpost()
 void
 newuser()
 {
-	char *confirm, *hlite, msg[128], *name, *full, *p, *pass, *v;
+#define MAXMSG 256
+	char *captcha, *confirm, *hlite, msg[MAXMSG], *name, *full, *p, *pass, *v;
 	char title[] = "New User";
 	int i;
+	struct user *user;
 	
 	*msg = 0;
-	confirm = hlite = name = full = pass = NULL;
+	captcha = confirm = hlite = name = full = pass = NULL;
 	
 	if(query.method == GET){
 		printf("Content-Type: text/html\n\n");
@@ -68,7 +70,7 @@ newuser()
 	}
 	
 	if(query.length > MAXUSERDATA){
-		snprintf(msg, 128, "Input exceeded server limitations");
+		snprintf(msg, MAXMSG, "Input exceeded server limitations");
 		printf("Status: 431 Request Header Fields Too Large\n");
 		printf("Content-Type: text/html\n\n");
 		#include "t/newuser.tc"
@@ -78,6 +80,8 @@ newuser()
 	while(p = nextparam(POST, NULL, MAXUSERDATA)){
 		if(!(v = split(p))) continue;
 		
+		if(!captcha && strcmp(p, "captcha") == 0)
+			captcha = strdup(v);
 		if(!confirm && strcmp(p, "confirm") == 0)
 			confirm = strdup(v);
 		else if(!name && strcmp(p, "name") == 0)
@@ -91,6 +95,10 @@ newuser()
 	}
 	
 	/* Decode URL-encoded fields. */
+	if(captcha && *captcha)
+		captcha[urldecode(captcha, -1)] = 0;
+	if(confirm && *confirm)
+		confirm[urldecode(confirm, -1)] = 0;
 	if(name && *name)
 		name[urldecode(name, -1)] = 0;
 	if(full && *full)
@@ -101,19 +109,19 @@ newuser()
 	/* Constrain lengths of decoded fields. */
 	if(name && *name && strlen(name)-1 > MAXUSERNAME){
 		hlite = strdup("name");
-		snprintf(msg, 128, "Username longer than %d characters",
+		snprintf(msg, MAXMSG, "Username longer than %d characters",
 		    MAXUSERNAME-1);
 		goto err;
 	}
 	if(full && *full && strlen(full)-1 > MAXUSERFULL){
 		hlite = strdup("full");
-		snprintf(msg, 128, "Full name longer than %d characters",
+		snprintf(msg, MAXMSG, "Full name longer than %d characters",
 		    MAXUSERFULL-1);
 		goto err;
 	}
 	if(pass && *pass && strlen(pass)-1 > MAXUSERPASS){
 		hlite = strdup("pass");
-		snprintf(msg, 128, "Password longer than %d characters",
+		snprintf(msg, MAXMSG, "Password longer than %d characters",
 		    MAXUSERPASS-1);
 		goto err;
 	}
@@ -125,7 +133,7 @@ newuser()
 	for(i = 0; name[i]; i++)
 		if(!ISVIS(name[i]) || !(name[i] == '_' || ISALNUM(name[i]))){
 			hlite = strdup("name");
-			snprintf(msg, 128,
+			snprintf(msg, MAXMSG,
 			    "Username may only contain ASCII letters, "
 			    "numbers and underscores.");
 			goto err;
@@ -134,25 +142,49 @@ newuser()
 		if(!ISVIS(full[i])){
 			fprintf(stderr, "%d\n", full[i]);
 			hlite = strdup("full");
-			snprintf(msg, 128,
+			snprintf(msg, MAXMSG,
 			    "Full name may only contain visible characters");
 			goto err;
 		}
 
 	/* Ensure all required fields are there. */
-	if(!name || !*name || !pass || !*pass){
-		hlite = (!name || !*name)? strdup("name"): strdup("pass");
-		snprintf(msg, 128, "Required field missing");
+	if(captcha && strcmp(captcha, "9") != 0){
+		hlite = strdup("captcha");
+		snprintf(msg, MAXMSG, "Incorrectly answered captcha");
+		goto err;
+	}
+	if(!name || !*name){
+		hlite = strdup("name");
+		snprintf(msg, MAXMSG, "Username is required");
+		goto err;
+	}
+	if(!pass || !*pass){
+		hlite = strdup("pass");
+		snprintf(msg, MAXMSG, "Password is required");
 		goto err;
 	}
 
 	if(pass && confirm && strcmp(pass, confirm) != 0){
-		snprintf(msg, 128, "Passwords do not match");
+		snprintf(msg, MAXMSG, "Passwords do not match");
+		goto err;
+	}
+	
+	if(!(user = malloc(sizeof(struct user))))
+		err(1, "malloc");
+	
+	user->name = name;
+	user->full = *full? full: NULL;
+	user->hash = pass; /* TODO */
+	
+	if(!adduser(user)){
+		snprintf(msg, MAXMSG, "Could not add user to database: %s",
+		    sqlite3_errmsg(db));
 		goto err;
 	}
 	
 	printf("Content-Type: text/html\n\n");
-	printf("You are valid\n");
+	snprintf(msg, MAXMSG, "SUCCESS: User was added to database");
+	#include "t/newuser.tc"
 	return;
 err:
 	printf("Status: 400 Bad Request\n");
diff --git a/t/head.t b/t/head.t
index 7962c5b..4fbd19a 100644
--- a/t/head.t
+++ b/t/head.t
@@ -4,6 +4,7 @@
 <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
 <title><%= title %></title>
 <style type="text/css">
+	form.hlite-captcha #captcha,
 	form.hlite-confirm #confirm,
 	form.hlite-name #name,
 	form.hlite-full #full,
diff --git a/t/newuser.t b/t/newuser.t
index e62fdc2..7d53f0c 100644
--- a/t/newuser.t
+++ b/t/newuser.t
@@ -10,7 +10,7 @@
 			<td><input type="text" name="name" value="<% if(name) printhtml(name); %>"></td>
 		</tr>
 		<tr id="full">
-			<td><label for="full">Full Name</label></td>
+			<td><label for="full">Full name</label></td>
 			<td><input type="text" name="full" value="<% if(full) printhtml(full); %>"></td>
 			<td><small>(optional)</small></td>
 		</tr>
@@ -22,6 +22,10 @@
 			<td><label for="confirm">Confirm</label></td>
 			<td><input type="password" name="confirm" value=""></td>
 		</tr>
+		<tr id="captcha">
+			<td><label for="captcha">What is 2 + 7?</label></td>
+			<td><input type="text" name="captcha" value="<% if(captcha) printhtml(captcha); %>"></td>
+		</tr>
 	</table>
 	<p>By clicking <i>Create</i>, you confirm</p>
 	<ol>
-- 
cgit v1.2.3