1 /*
2  * This file is an example of how to embed web-server functionality
3  * into existing application.
4  * Compilation line:
5  * cc example.c shttpd.c -DEMBEDDED
6  */
7 
8 #ifdef _WIN32
9 #include <winsock.h>
10 #define	snprintf			_snprintf
11 
12 #ifndef _WIN32_WCE
13 #ifdef _MSC_VER /* pragmas not valid on MinGW */
14 #endif /* _MSC_VER */
15 #define ALIAS_URI "/my_c"
16 #define ALIAS_DIR "c:\\"
17 
18 #else /* _WIN32_WCE */
19 /* Windows CE-specific definitions */
20 #pragma comment(lib,"ws2")
21 //#include "compat_wince.h"
22 #define ALIAS_URI "/my_root"
23 #define ALIAS_DIR "\\"
24 #endif /* _WIN32_WCE */
25 
26 #else
27 #include <sys/types.h>
28 #include <sys/select.h>
29 #include <sys/wait.h>
30 #define ALIAS_URI "/my_etc"
31 #define ALIAS_DIR "/etc/"
32 #endif
33 
34 #ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */
35 #include <time.h>
36 #include <errno.h>
37 #include <signal.h>
38 #endif
39 
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <assert.h>
43 #include <string.h>
44 
45 #include "shttpd.h"
46 
47 /*
48  * This callback function is attached to the "/" and "/abc.html" URIs,
49  * thus is acting as "index.html" file. It shows a bunch of links
50  * to other URIs, and allows to change the value of program's
51  * internal variable. The pointer to that variable is passed to the
52  * callback function as arg->user_data.
53  */
54 static void
show_index(struct shttpd_arg * arg)55 show_index(struct shttpd_arg *arg)
56 {
57 	int		*p = arg->user_data;	/* integer passed to us */
58 	char		value[20];
59 	const char	*host, *request_method, *query_string, *request_uri;
60 
61 	request_method = shttpd_get_env(arg, "REQUEST_METHOD");
62 	request_uri = shttpd_get_env(arg, "REQUEST_URI");
63 	query_string = shttpd_get_env(arg, "QUERY_STRING");
64 
65 	/* Change the value of integer variable */
66 	value[0] = '\0';
67 	if (!strcmp(request_method, "POST")) {
68 		/* If not all data is POSTed, wait for the rest */
69 		if (arg->flags & SHTTPD_MORE_POST_DATA)
70 			return;
71 		(void) shttpd_get_var("name1", arg->in.buf, arg->in.len,
72 		    value, sizeof(value));
73 	} else if (query_string != NULL) {
74 		(void) shttpd_get_var("name1", query_string,
75 		    strlen(query_string), value, sizeof(value));
76 	}
77 	if (value[0] != '\0') {
78 		*p = atoi(value);
79 
80 		/*
81 		 * Suggested by Luke Dunstan. When POST is used,
82 		 * send 303 code to force the browser to re-request the
83 		 * page using GET method. This prevents the possibility of
84 		 * the user accidentally resubmitting the form when using
85 		 * Refresh or Back commands in the browser.
86 		 */
87 		if (!strcmp(request_method, "POST")) {
88 			shttpd_printf(arg, "HTTP/1.1 303 See Other\r\n"
89 				"Location: %s\r\n\r\n", request_uri);
90 			arg->flags |= SHTTPD_END_OF_OUTPUT;
91 			return;
92 		}
93 	}
94 
95 	shttpd_printf(arg, "%s",
96 		"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"
97 		"<html><body><h1>Welcome to embedded example of SHTTPD");
98 	shttpd_printf(arg, " v. %s </h1><ul>", shttpd_version());
99 
100 	shttpd_printf(arg, "<li><code>REQUEST_METHOD: %s "
101 	    "REQUEST_URI: \"%s\" QUERY_STRING: \"%s\""
102 	    " REMOTE_ADDR: %s REMOTE_USER: \"(null)\"</code><hr>",
103 	    request_method, request_uri,
104 	    query_string ? query_string : "(null)",
105 	    shttpd_get_env(arg, "REMOTE_ADDR"));
106 	shttpd_printf(arg, "<li>Internal int variable value: <b>%d</b>", *p);
107 
108 	shttpd_printf(arg, "%s",
109 		"<form method=\"GET\">Enter new value: "
110 		"<input type=\"text\" name=\"name1\"/>"
111 		"<input type=\"submit\" "
112 		"value=\"set new value using GET method\"></form>");
113 	shttpd_printf(arg, "%s",
114 		"<form method=\"POST\">Enter new value: "
115 		"<input type=\"text\" name=\"name1\"/>"
116 		"<input type=\"submit\" "
117 		"value=\"set new value using POST method\"></form>");
118 
119 	shttpd_printf(arg, "%s",
120 		"<hr><li><a href=\"/secret\">"
121 		"Protected page</a> (guest:guest)<hr>"
122 		"<li><a href=\"/huge\">Output lots of data</a><hr>"
123 		"<li><a href=\"" ALIAS_URI "/\">Aliased " ALIAS_DIR " directory</a><hr>");
124 	shttpd_printf(arg, "%s",
125 		"<li><a href=\"/Makefile\">Regular file (Makefile)</a><hr>"
126 		"<li><a href=\"/ssi_test.shtml\">SSI file "
127 			"(ssi_test.shtml)</a><hr>"
128 		"<li><a href=\"/users/joe/\">Wildcard URI example</a><hr>"
129 		"<li><a href=\"/not-existent/\">Custom 404 handler</a><hr>");
130 
131 	host = shttpd_get_header(arg, "Host");
132 	shttpd_printf(arg, "<li>'Host' header value: [%s]<hr>",
133 	    host ? host : "NOT SET");
134 
135 	shttpd_printf(arg, "<li>Upload file example. "
136 	    "<form method=\"post\" enctype=\"multipart/form-data\" "
137 	    "action=\"/post\"><input type=\"file\" name=\"file\">"
138 	    "<input type=\"submit\"></form>");
139 
140 	shttpd_printf(arg, "%s", "</body></html>");
141 	arg->flags |= SHTTPD_END_OF_OUTPUT;
142 }
143 
144 /*
145  * This callback is attached to the URI "/post"
146  * It uploads file from a client to the server. This is the demostration
147  * of how to use POST method to send lots of data from the client.
148  * The uploaded file is saved into "uploaded.txt".
149  * This function is called many times during single request. To keep the
150  * state (how many bytes we have received, opened file etc), we allocate
151  * a "struct state" structure for every new connection.
152  */
153 static void
show_post(struct shttpd_arg * arg)154 show_post(struct shttpd_arg *arg)
155 {
156 	const char	*s, *path = "uploaded.txt";
157 	struct state {
158 		size_t	cl;		/* Content-Length	*/
159 		size_t	nread;		/* Number of bytes read	*/
160 		FILE	*fp;
161 	} *state;
162 
163 	/* If the connection was broken prematurely, cleanup */
164 	if (arg->flags & SHTTPD_CONNECTION_ERROR && arg->state) {
165 		(void) fclose(((struct state *) arg->state)->fp);
166 		free(arg->state);
167 	} else if ((s = shttpd_get_header(arg, "Content-Length")) == NULL) {
168 		shttpd_printf(arg, "HTTP/1.0 411 Length Required\n\n");
169 		arg->flags |= SHTTPD_END_OF_OUTPUT;
170 	} else if (arg->state == NULL) {
171 		/* New request. Allocate a state structure, and open a file */
172 		arg->state = state = calloc(1, sizeof(*state));
173 		state->cl = strtoul(s, NULL, 10);
174 		state->fp = fopen(path, "wb+");
175 		shttpd_printf(arg, "HTTP/1.0 200 OK\n"
176 			"Content-Type: text/plain\n\n");
177 	} else {
178 		state = arg->state;
179 
180 		/*
181 		 * Write the POST data to a file. We do not do any URL
182 		 * decoding here. File will contain form-urlencoded stuff.
183 		 */
184 		(void) fwrite(arg->in.buf, arg->in.len, 1, state->fp);
185 		state->nread += arg->in.len;
186 
187 		/* Tell SHTTPD we have processed all data */
188 		arg->in.num_bytes = arg->in.len;
189 
190 		/* Data stream finished? Close the file, and free the state */
191 		if (state->nread >= state->cl) {
192 			shttpd_printf(arg, "Written %d bytes to %s",
193 			    state->nread, path);
194 			(void) fclose(state->fp);
195 			free(state);
196 			arg->flags |= SHTTPD_END_OF_OUTPUT;
197 		}
198 	}
199 }
200 
201 /*
202  * This callback function is attached to the "/secret" URI.
203  * It shows simple text message, but in order to be shown, user must
204  * authorized himself against the passwords file "passfile".
205  */
206 static void
show_secret(struct shttpd_arg * arg)207 show_secret(struct shttpd_arg *arg)
208 {
209 	shttpd_printf(arg, "%s", "HTTP/1.1 200 OK\r\n");
210 	shttpd_printf(arg, "%s", "Content-Type: text/html\r\n\r\n");
211 	shttpd_printf(arg, "%s", "<html><body>");
212 	shttpd_printf(arg, "%s", "<p>This is a protected page</body></html>");
213 	arg->flags |= SHTTPD_END_OF_OUTPUT;
214 }
215 
216 /*
217  * This callback function is attached to the "/huge" URI.
218  * It outputs binary data to the client.
219  * The number of bytes already sent is stored directly in the arg->state.
220  */
221 static void
show_huge(struct shttpd_arg * arg)222 show_huge(struct shttpd_arg *arg)
223 {
224 	int		state = (int) arg->state;
225 
226 	if (state == 0) {
227 		shttpd_printf(arg, "%s", "HTTP/1.1 200 OK\r\n");
228 		shttpd_printf(arg, "%s", "Content-Type: text/plain\r\n\r\n");
229 	}
230 
231 	while (arg->out.num_bytes < arg->out.len) {
232 		arg->out.buf[arg->out.num_bytes] = state % 72 ? 'A' : '\n';
233 		arg->out.num_bytes++;
234 		state++;
235 
236 		if (state > 1024 * 1024) {	/* Output 1Mb Kb of data */
237 			arg->flags |= SHTTPD_END_OF_OUTPUT;
238 			break;
239 		}
240 	}
241 
242 	arg->state = (void *) state;
243 }
244 
245 /*
246  * This callback function is used to show how to handle 404 error
247  */
248 static void
show_404(struct shttpd_arg * arg)249 show_404(struct shttpd_arg *arg)
250 {
251 	shttpd_printf(arg, "%s", "HTTP/1.1 200 OK\r\n");
252 	shttpd_printf(arg, "%s", "Content-Type: text/plain\r\n\r\n");
253 	shttpd_printf(arg, "%s", "Oops. File not found! ");
254 	shttpd_printf(arg, "%s", "This is a custom error handler.");
255 	arg->flags |= SHTTPD_END_OF_OUTPUT;
256 }
257 
258 /*
259  * This callback function is attached to the wildcard URI "/users/.*"
260  * It shows a greeting message and an actual URI requested by the user.
261  */
262 static void
show_users(struct shttpd_arg * arg)263 show_users(struct shttpd_arg *arg)
264 {
265 	shttpd_printf(arg, "%s", "HTTP/1.1 200 OK\r\n");
266 	shttpd_printf(arg, "%s", "Content-Type: text/html\r\n\r\n");
267 	shttpd_printf(arg, "%s", "<html><body>");
268 	shttpd_printf(arg, "%s", "<h1>Hi. This is a wildcard uri handler"
269 	    "for the URI /users/*/ </h1>");
270 	shttpd_printf(arg, "<h2>URI: %s</h2></body></html>",
271 		shttpd_get_env(arg, "REQUEST_URI"));
272 	arg->flags |= SHTTPD_END_OF_OUTPUT;
273 }
274 
275 /*
276  * This function will be called on SSI directive <!--#if true -->, or
277  * <!--#elif true -->, and 'returns' TRUE condition
278  */
279 static void
ssi_test_true(struct shttpd_arg * arg)280 ssi_test_true(struct shttpd_arg	*arg)
281 {
282 	arg->flags |= SHTTPD_SSI_EVAL_TRUE;
283 }
284 
285 /*
286  * This function will be called on SSI directive <!--#if false -->, or
287  * <!--#elif false -->, and 'returns' FALSE condition
288  */
289 static void
ssi_test_false(struct shttpd_arg * arg)290 ssi_test_false(struct shttpd_arg *arg)
291 {
292 	/* Do not set SHTTPD_SSI_EVAL_TRUE flag, that means FALSE */
293 }
294 
295 /*
296  * This function will be called on SSI <!--#call print_stuff -->
297  */
298 static void
ssi_print_stuff(struct shttpd_arg * arg)299 ssi_print_stuff(struct shttpd_arg *arg)
300 {
301 	time_t	t = time(NULL);
302 
303 	shttpd_printf(arg,
304 	    "SSI user callback output: Current time: %s", ctime(&t));
305 	if (arg->in.buf != NULL)
306 		shttpd_printf(arg, "SSI param passed: [%s]", arg->in.buf);
307 	arg->flags |= SHTTPD_END_OF_OUTPUT;
308 }
309 
310 /*
311  * Make sure we have ho zombies from CGIs
312  */
313 static void
signal_handler(int sig_num)314 signal_handler(int sig_num)
315 {
316 	switch (sig_num) {
317 #ifndef _WIN32
318 	case SIGCHLD:
319 		while (waitpid(-1, &sig_num, WNOHANG) > 0) ;
320 		break;
321 #endif /* !_WIN32 */
322 	default:
323 		break;
324 	}
325 }
326 
main(int argc,char * argv[])327 int main(int argc, char *argv[])
328 {
329 	int			data = 1234567;
330 	struct shttpd_ctx	*ctx;
331 
332 	/* Get rid of warnings */
333 	argc = argc;
334 	argv = argv;
335 
336 #ifndef _WIN32
337 	signal(SIGPIPE, SIG_IGN);
338 	signal(SIGCHLD, &signal_handler);
339 #endif /* !_WIN32 */
340 
341 	/*
342 	 * Initialize SHTTPD context.
343 	 * Attach folder c:\ to the URL /my_c  (for windows), and
344 	 * /etc/ to URL /my_etc (for UNIX). These are Apache-like aliases.
345 	 * Set WWW root to current directory.
346 	 * Start listening on ports 8080 and 8081
347 	 */
348 	ctx = shttpd_init(argc, argv);
349 	shttpd_set_option(ctx, "ssl_cert", "shttpd.pem");
350 	shttpd_set_option(ctx, "aliases", ALIAS_URI "=" ALIAS_DIR);
351 	shttpd_set_option(ctx, "ports", "8080,8081s");
352 
353 	/* Register an index page under two URIs */
354 	shttpd_register_uri(ctx, "/", &show_index, (void *) &data);
355 	shttpd_register_uri(ctx, "/abc.html", &show_index, (void *) &data);
356 
357 	/* Register a callback on wildcard URI */
358 	shttpd_register_uri(ctx, "/users/*/", &show_users, NULL);
359 
360 	/* Show how to use password protection */
361 	shttpd_register_uri(ctx, "/secret", &show_secret, NULL);
362 	shttpd_set_option(ctx, "protect", "/secret=passfile");
363 
364 	/* Show how to use stateful big data transfer */
365 	shttpd_register_uri(ctx, "/huge", &show_huge, NULL);
366 
367 	/* Register URI for file upload */
368 	shttpd_register_uri(ctx, "/post", &show_post, NULL);
369 
370 	/* Register SSI callbacks */
371 	shttpd_register_ssi_func(ctx, "true", ssi_test_true, NULL);
372 	shttpd_register_ssi_func(ctx, "false", ssi_test_false, NULL);
373 	shttpd_register_ssi_func(ctx, "print_stuff", ssi_print_stuff, NULL);
374 
375 	shttpd_handle_error(ctx, 404, show_404, NULL);
376 
377 	/* Serve connections infinitely until someone kills us */
378 	for (;;)
379 		shttpd_poll(ctx, 1000);
380 
381 	/* Probably unreached, because we will be killed by a signal */
382 	shttpd_fini(ctx);
383 
384 	return (EXIT_SUCCESS);
385 }
386