aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Ankarström <john@ankarstrom.se>2021-09-19 01:20:30 +0200
committerJohn Ankarström <john@ankarstrom.se>2021-09-19 01:20:30 +0200
commit4b838fced8cd5fd2c24300e3f9132a1474051a76 (patch)
tree62be70f1da57b87c863f03eb9054056f9b841aa2
parent7708b5f493bf3057af331624d29664ad17a87dbc (diff)
downloadcforum-4b838fced8cd5fd2c24300e3f9132a1474051a76.tar.gz
Implement URL decode and HTML escape functions
-rw-r--r--README10
-rw-r--r--ctl.c49
-rw-r--r--query.c27
-rw-r--r--query.h3
-rw-r--r--site.h11
-rw-r--r--t/newuser.t4
6 files changed, 82 insertions, 22 deletions
diff --git a/README b/README
index 9472b04..01cf301 100644
--- a/README
+++ b/README
@@ -9,15 +9,15 @@ It is also rather small:
wc -l *.c *.h */*.t */*.lex
101 cforum.c
- 222 ctl.c
+ 267 ctl.c
268 db.c
10 err.c
- 121 query.c
+ 144 query.c
6 ctl.h
40 db.h
0 err.h
- 15 query.h
- 21 site.h
+ 16 query.h
+ 12 site.h
3 t/err.t
1 t/foot.t
29 t/front.t
@@ -26,4 +26,4 @@ It is also rather small:
28 t/post.t
12 t/user.t
95 mktpl/mktpl.lex
- 1020 total
+ 1080 total
diff --git a/ctl.c b/ctl.c
index 712b31a..a3eb920 100644
--- a/ctl.c
+++ b/ctl.c
@@ -21,6 +21,23 @@ printdate(int timestamp)
printf(buf);
}
+/* Print HTML-escaped string. */
+void
+printhtml(char *s)
+{
+ char *t;
+
+ for(t = s; *t; t++)
+ switch(*t){
+ case '&': printf("&amp;"); break;
+ case '\"': printf("&quot;"); break;
+ case '\'': printf("&apos;"); break;
+ case '<': printf("&lt;"); break;
+ case '>': printf("&gt;"); break;
+ default: printf("%c", *t);
+ }
+}
+
/*
* The `new' functions provide a way to add a new attachment/post/user.
* On GET, they show a form. On POST, they insert the posted information
@@ -39,6 +56,7 @@ newuser()
{
char *confirm, *hlite, msg[128], *name, *full, *p, *pass, *v;
char title[] = "New User";
+ int i;
*msg = 0;
confirm = hlite = name = full = pass = NULL;
@@ -73,8 +91,14 @@ newuser()
}
/* Decode URL-encoded fields. */
+ if(name && *name)
+ name[urldecode(name, -1)] = 0;
+ if(full && *full)
+ full[urldecode(full, -1)] = 0;
+ if(pass && *pass)
+ pass[urldecode(pass, -1)] = 0;
- /* Restrain lengths of decoded fields. */
+ /* Constrain lengths of decoded fields. */
if(name && *name && strlen(name)-1 > MAXUSERNAME){
hlite = strdup("name");
snprintf(msg, 128, "Username longer than %d characters",
@@ -94,13 +118,34 @@ newuser()
goto err;
}
+#define ISVIS(c) ((unsigned int)c >= 20)
+#define ISALNUM(c) (c>='A' && c<='Z' || c>='a' && c<='z' || c>='0' && c<='9')
+
+ /* Constrain character sets. */
+ for(i = 0; name[i]; i++)
+ if(!ISVIS(name[i]) || !(name[i] == '_' || ISALNUM(name[i]))){
+ hlite = strdup("name");
+ snprintf(msg, 128,
+ "Username may only contain ASCII letters, "
+ "numbers and underscores.");
+ goto err;
+ }
+ for(i = 0; full[i]; i++)
+ if(!ISVIS(full[i])){
+ fprintf(stderr, "%d\n", full[i]);
+ hlite = strdup("full");
+ snprintf(msg, 128,
+ "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");
goto err;
}
-
+
if(pass && confirm && strcmp(pass, confirm) != 0){
snprintf(msg, 128, "Passwords do not match");
goto err;
diff --git a/query.c b/query.c
index 27770e4..96cfbcb 100644
--- a/query.c
+++ b/query.c
@@ -115,8 +115,31 @@ split(char *param)
return param+n+1;
}
-void
-urldecode(char *s)
+/*
+ * Decode a URL-encoded string in place. If len is negative, the string
+ * is assumed to be NUL-terminated. The length of the new string is
+ * returned. Note that the new string is NOT NUL-terminated! To produce
+ * a NUL-terminated string, use s[urldecode(s, -1)] = 0.
+ */
+int
+urldecode(char *s, int len)
{
+ unsigned int code, i, j;
+
+ if(len < 0)
+ len = strlen(s);
+
+ for(i = j = 0; i < len; i++, j++){
+ if(s[i] == '+')
+ s[j] = ' ';
+ else if(s[i] == '%'){
+ if(!sscanf(s+i+1, "%2x", &code))
+ code = '?';
+ s[j] = code;
+ i += 2;
+ }else
+ s[j] = s[i];
+ }
+ return j;
} \ No newline at end of file
diff --git a/query.h b/query.h
index 6a4c1cc..d9c7ac9 100644
--- a/query.h
+++ b/query.h
@@ -13,4 +13,5 @@ enum method{
char *nextparam(enum method, int *, int);
void setquery(void);
-char *split(char *); \ No newline at end of file
+char *split(char *);
+int urldecode(char *, int); \ No newline at end of file
diff --git a/site.h b/site.h
index 800cdea..8c70a64 100644
--- a/site.h
+++ b/site.h
@@ -1,17 +1,8 @@
-/*
- * TODO: It is probably better to use the CONTENT_LENGTH
- * environment variable to decide the length of the entire
- * posted query string and then compare it to a max value,
- * such as:
- */
-
+/* Maximum allowed Content-Length for various forms. */
#define MAXATTDATA 4096
#define MAXUSERDATA 512
#define MAXPOSTDATA 4096
-/* Maximum size of newuser parameter, incl. NUL. */
-#define MAXUSERPARAM 512
-
/* Maximum size of user information, incl. NUL. */
#define MAXUSERNAME 40
#define MAXUSERFULL 128
diff --git a/t/newuser.t b/t/newuser.t
index a0a0dd9..e62fdc2 100644
--- a/t/newuser.t
+++ b/t/newuser.t
@@ -7,11 +7,11 @@
<table border="0">
<tr id="name">
<td><label for="name">Username</label></td>
- <td><input type="text" name="name" value="<% if(name) printf("%s", name); %>"></td>
+ <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><input type="text" name="full" value="<% if(full) printf("%s", full); %>"></td>
+ <td><input type="text" name="full" value="<% if(full) printhtml(full); %>"></td>
<td><small>(optional)</small></td>
</tr>
<tr id="pass">