Deleted Added
full compact
simple_httpd.c (94135) simple_httpd.c (112205)
1/*-
2 * Simple_HTTPd v1.1 - a very small, barebones HTTP server
3 *
4 * Copyright (c) 1998-1999 Marc Nicholas <marc@netstor.com>
5 * All rights reserved.
6 *
7 * Major rewrite by William Lloyd <wlloyd@slap.net>
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
1/*-
2 * Simple_HTTPd v1.1 - a very small, barebones HTTP server
3 *
4 * Copyright (c) 1998-1999 Marc Nicholas <marc@netstor.com>
5 * All rights reserved.
6 *
7 * Major rewrite by William Lloyd <wlloyd@slap.net>
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 * $FreeBSD: head/release/picobsd/tinyware/simple_httpd/simple_httpd.c 94135 2002-04-07 17:42:27Z asmodai $
30 * $FreeBSD: head/release/picobsd/tinyware/simple_httpd/simple_httpd.c 112205 2003-03-13 22:06:10Z dwmalone $
31 */
32
33#include <sys/stat.h>
34#include <sys/time.h>
35#include <sys/types.h>
36#include <sys/socket.h>
37#include <sys/wait.h>
38#include <netinet/in.h>
39#include <arpa/inet.h>
40
41#include <fcntl.h>
42#include <netdb.h>
43#include <signal.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <time.h>
48#include <unistd.h>
49
50int http_port = 80;
51int daemonize = 1;
52int verbose = 0;
53int http_sock, con_sock;
54
55char fetch_mode[100];
56char homedir[100];
57char logfile[80];
58char *adate();
59
60struct hostent *hst;
61struct sockaddr_in source;
62
63/* HTTP basics */
64static char httpd_server_ident[] = "Server: FreeBSD/PicoBSD simple_httpd 1.1\r";
65
66static char http_200[] = "HTTP/1.0 200 OK\r";
67
68/* Two parts, HTTP Header and then HTML */
69static char *http_404[2] =
70 {"HTTP/1.0 404 Not found\r\n",
71"<HTML><HEAD><TITLE>Error</TITLE></HEAD><BODY><H1>Error 404</H1>\
72Not found - file doesn't exist or you do not have permission.\n</BODY></HTML>\r\n"
73};
74
75static char *http_405[2] =
76 {"HTTP/1.0 405 Method Not allowed\r\nAllow: GET,HEAD\r\n",
77"<HTML><HEAD><TITLE>Error</TITLE></HEAD><BODY><H1>Error 405</H1>\
78This server only supports GET and HEAD requests.\n</BODY></HTML>\r\n"
79};
80
81/*
82 * Only called on initial invocation
83 */
84void
85init_servconnection(void)
86{
87 struct sockaddr_in server;
88
89 /* Create a socket */
90 http_sock = socket(AF_INET, SOCK_STREAM, 0);
91 if (http_sock < 0) {
92 perror("socket");
93 exit(1);
94 }
95 server.sin_family = AF_INET;
96 server.sin_port = htons(http_port);
97 server.sin_addr.s_addr = INADDR_ANY;
98 if (bind(http_sock, (struct sockaddr *) & server, sizeof(server)) < 0) {
99 perror("bind socket");
100 exit(1);
101 }
102 if (verbose) printf("simple_httpd:%d\n",http_port);
103}
104
105/*
106 * Wait here until we see an incoming http request
107 */
108void
109wait_connection(void)
110{
111 int lg;
112
113 lg = sizeof(struct sockaddr_in);
114
115 con_sock = accept(http_sock, (struct sockaddr *) & source, &lg);
116 if (con_sock <= 0) {
117 perror("accept");
118 exit(1);
119 }
120}
121
122/*
123 * Print timestamp for HTTP HEAD and GET
124 */
125void
126http_date(void)
127{
128 time_t tl;
129 char buff[50];
130
131 tl = time(NULL);
132 strftime(buff, 50, "Date: %a, %d %h %Y %H:%M:%S %Z\r\n", gmtime(&tl));
133 write(con_sock, buff, strlen(buff));
134 //return(buff);
135}
136
137/*
138 * Send data to the open socket
139 */
140void
141http_output(char *html)
142{
143 write(con_sock, html, strlen(html));
144 write(con_sock, "\r\n", 2);
145}
146
147
148/*
149 * Create and write the log information to file
150 * Log file format is one line per entry
151 */
152void
153log_line(char *req)
154{
155 char log_buff[256];
156 char msg[1024];
157 char env_host[80], env_addr[80];
158 long addr;
159 FILE *log;
160
161 strcpy(log_buff,inet_ntoa(source.sin_addr));
162 sprintf(env_addr, "REMOTE_ADDR=%s",log_buff);
163
164 addr=inet_addr(log_buff);
165
166 strcpy(msg,adate());
167 strcat(msg," ");
168 hst=gethostbyaddr((char*) &addr, 4, AF_INET);
169
170 /* If DNS hostname exists */
171 if (hst) {
172 strcat(msg,hst->h_name);
173 sprintf(env_host, "REMOTE_HOST=%s",hst->h_name);
174 }
175 strcat(msg," (");
176 strcat(msg,log_buff);
177 strcat(msg,") ");
178 strcat(msg,req);
179
180 if (daemonize) {
181 log=fopen(logfile,"a");
182 fprintf(log,"%s\n",msg);
183 fclose(log);
184 } else
185 printf("%s\n",msg);
186
187 /* This is for CGI scripts */
188 putenv(env_addr);
189 putenv(env_host);
190}
191
192/*
193 * We have a connection. Identify what type of request GET, HEAD, CGI, etc
194 * and do what needs to be done
195 */
196void
197http_request(void)
198{
199 int fd, lg, i;
200 int cmd = 0;
201 char *p, *par;
202 char *filename, *c;
203 struct stat file_status;
204 char req[1024];
205 char buff[8192];
206
207 lg = read(con_sock, req, 1024);
208
209 if (p=strstr(req,"\n")) *p=0;
210 if (p=strstr(req,"\r")) *p=0;
211
212 log_line(req);
213
214 c = strtok(req, " ");
215
216 /* Error msg if request is nothing */
217 if (c == NULL) {
218 http_output(http_404[0]);
219 http_output(http_404[1]);
220 goto end_request;
221 }
222
223 if (strncmp(c, "GET", 3) == 0) cmd = 1;
224 if (strncmp(c, "HEAD", 4) == 0) cmd = 2;
225
226 /* Do error msg for any other type of request */
227 if (cmd == 0) {
228 http_output(http_405[0]);
229 http_output(http_405[1]);
230 goto end_request;
231 }
232
233 filename = strtok(NULL, " ");
234
235 c = strtok(NULL, " ");
236 if (fetch_mode[0] != NULL) strcpy(filename,fetch_mode);
237 if (filename == NULL ||
238 strlen(filename)==1) filename="/index.html";
239
240 while (filename[0]== '/') filename++;
241
242 /* CGI handling. Untested */
243 if (!strncmp(filename,"cgi-bin/",8))
244 {
245 par=0;
246 if (par=strstr(filename,"?"))
247 {
248 *par=0;
249 par++;
250 }
251 if (access(filename,X_OK)) goto conti;
252 stat (filename,&file_status);
31 */
32
33#include <sys/stat.h>
34#include <sys/time.h>
35#include <sys/types.h>
36#include <sys/socket.h>
37#include <sys/wait.h>
38#include <netinet/in.h>
39#include <arpa/inet.h>
40
41#include <fcntl.h>
42#include <netdb.h>
43#include <signal.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <time.h>
48#include <unistd.h>
49
50int http_port = 80;
51int daemonize = 1;
52int verbose = 0;
53int http_sock, con_sock;
54
55char fetch_mode[100];
56char homedir[100];
57char logfile[80];
58char *adate();
59
60struct hostent *hst;
61struct sockaddr_in source;
62
63/* HTTP basics */
64static char httpd_server_ident[] = "Server: FreeBSD/PicoBSD simple_httpd 1.1\r";
65
66static char http_200[] = "HTTP/1.0 200 OK\r";
67
68/* Two parts, HTTP Header and then HTML */
69static char *http_404[2] =
70 {"HTTP/1.0 404 Not found\r\n",
71"<HTML><HEAD><TITLE>Error</TITLE></HEAD><BODY><H1>Error 404</H1>\
72Not found - file doesn't exist or you do not have permission.\n</BODY></HTML>\r\n"
73};
74
75static char *http_405[2] =
76 {"HTTP/1.0 405 Method Not allowed\r\nAllow: GET,HEAD\r\n",
77"<HTML><HEAD><TITLE>Error</TITLE></HEAD><BODY><H1>Error 405</H1>\
78This server only supports GET and HEAD requests.\n</BODY></HTML>\r\n"
79};
80
81/*
82 * Only called on initial invocation
83 */
84void
85init_servconnection(void)
86{
87 struct sockaddr_in server;
88
89 /* Create a socket */
90 http_sock = socket(AF_INET, SOCK_STREAM, 0);
91 if (http_sock < 0) {
92 perror("socket");
93 exit(1);
94 }
95 server.sin_family = AF_INET;
96 server.sin_port = htons(http_port);
97 server.sin_addr.s_addr = INADDR_ANY;
98 if (bind(http_sock, (struct sockaddr *) & server, sizeof(server)) < 0) {
99 perror("bind socket");
100 exit(1);
101 }
102 if (verbose) printf("simple_httpd:%d\n",http_port);
103}
104
105/*
106 * Wait here until we see an incoming http request
107 */
108void
109wait_connection(void)
110{
111 int lg;
112
113 lg = sizeof(struct sockaddr_in);
114
115 con_sock = accept(http_sock, (struct sockaddr *) & source, &lg);
116 if (con_sock <= 0) {
117 perror("accept");
118 exit(1);
119 }
120}
121
122/*
123 * Print timestamp for HTTP HEAD and GET
124 */
125void
126http_date(void)
127{
128 time_t tl;
129 char buff[50];
130
131 tl = time(NULL);
132 strftime(buff, 50, "Date: %a, %d %h %Y %H:%M:%S %Z\r\n", gmtime(&tl));
133 write(con_sock, buff, strlen(buff));
134 //return(buff);
135}
136
137/*
138 * Send data to the open socket
139 */
140void
141http_output(char *html)
142{
143 write(con_sock, html, strlen(html));
144 write(con_sock, "\r\n", 2);
145}
146
147
148/*
149 * Create and write the log information to file
150 * Log file format is one line per entry
151 */
152void
153log_line(char *req)
154{
155 char log_buff[256];
156 char msg[1024];
157 char env_host[80], env_addr[80];
158 long addr;
159 FILE *log;
160
161 strcpy(log_buff,inet_ntoa(source.sin_addr));
162 sprintf(env_addr, "REMOTE_ADDR=%s",log_buff);
163
164 addr=inet_addr(log_buff);
165
166 strcpy(msg,adate());
167 strcat(msg," ");
168 hst=gethostbyaddr((char*) &addr, 4, AF_INET);
169
170 /* If DNS hostname exists */
171 if (hst) {
172 strcat(msg,hst->h_name);
173 sprintf(env_host, "REMOTE_HOST=%s",hst->h_name);
174 }
175 strcat(msg," (");
176 strcat(msg,log_buff);
177 strcat(msg,") ");
178 strcat(msg,req);
179
180 if (daemonize) {
181 log=fopen(logfile,"a");
182 fprintf(log,"%s\n",msg);
183 fclose(log);
184 } else
185 printf("%s\n",msg);
186
187 /* This is for CGI scripts */
188 putenv(env_addr);
189 putenv(env_host);
190}
191
192/*
193 * We have a connection. Identify what type of request GET, HEAD, CGI, etc
194 * and do what needs to be done
195 */
196void
197http_request(void)
198{
199 int fd, lg, i;
200 int cmd = 0;
201 char *p, *par;
202 char *filename, *c;
203 struct stat file_status;
204 char req[1024];
205 char buff[8192];
206
207 lg = read(con_sock, req, 1024);
208
209 if (p=strstr(req,"\n")) *p=0;
210 if (p=strstr(req,"\r")) *p=0;
211
212 log_line(req);
213
214 c = strtok(req, " ");
215
216 /* Error msg if request is nothing */
217 if (c == NULL) {
218 http_output(http_404[0]);
219 http_output(http_404[1]);
220 goto end_request;
221 }
222
223 if (strncmp(c, "GET", 3) == 0) cmd = 1;
224 if (strncmp(c, "HEAD", 4) == 0) cmd = 2;
225
226 /* Do error msg for any other type of request */
227 if (cmd == 0) {
228 http_output(http_405[0]);
229 http_output(http_405[1]);
230 goto end_request;
231 }
232
233 filename = strtok(NULL, " ");
234
235 c = strtok(NULL, " ");
236 if (fetch_mode[0] != NULL) strcpy(filename,fetch_mode);
237 if (filename == NULL ||
238 strlen(filename)==1) filename="/index.html";
239
240 while (filename[0]== '/') filename++;
241
242 /* CGI handling. Untested */
243 if (!strncmp(filename,"cgi-bin/",8))
244 {
245 par=0;
246 if (par=strstr(filename,"?"))
247 {
248 *par=0;
249 par++;
250 }
251 if (access(filename,X_OK)) goto conti;
252 stat (filename,&file_status);
253 if (setuid(file_status.st_uid)) return(0);
254 if (seteuid(file_status.st_uid)) return(0);
253 if (setuid(file_status.st_uid)) return;
254 if (seteuid(file_status.st_uid)) return;
255 if (!fork())
256 {
257 close(1);
258 dup(con_sock);
259 //printf("HTTP/1.0 200 OK\nContent-type: text/html\n\n\n");
260 printf("HTTP/1.0 200 OK\r\n");
261 /* Plug in environment variable, others in log_line */
262 putenv("SERVER_SOFTWARE=FreeBSD/PicoBSD");
263
264 execlp (filename,filename,par,(char *)0);
265 }
266 wait(&i);
255 if (!fork())
256 {
257 close(1);
258 dup(con_sock);
259 //printf("HTTP/1.0 200 OK\nContent-type: text/html\n\n\n");
260 printf("HTTP/1.0 200 OK\r\n");
261 /* Plug in environment variable, others in log_line */
262 putenv("SERVER_SOFTWARE=FreeBSD/PicoBSD");
263
264 execlp (filename,filename,par,(char *)0);
265 }
266 wait(&i);
267 return(0);
267 return;
268 }
269 conti:
270 if (filename == NULL) {
271 http_output(http_405[0]);
272 http_output(http_405[1]);
273 goto end_request;
274 }
275 /* End of CGI handling */
276
277 /* Reject any request with '..' in it, bad hacker */
278 c = filename;
279 while (*c != '\0')
280 if (c[0] == '.' && c[1] == '.') {
281 http_output(http_404[0]);
282 http_output(http_404[1]);
283 goto end_request;
284 } else
285 c++;
286
287 /* Open filename */
288 fd = open(filename, O_RDONLY);
289 if (fd < 0) {
290 http_output(http_404[0]);
291 http_output(http_404[1]);
292 goto end_request;
293 }
294
295 /* Get file status information */
296 if (fstat(fd, &file_status) < 0) {
297 http_output(http_404[0]);
298 http_output(http_404[1]);
299 goto end_request;
300 }
301
302 /* Is it a regular file? */
303 if (!S_ISREG(file_status.st_mode)) {
304 http_output(http_404[0]);
305 http_output(http_404[1]);
306 goto end_request;
307 }
308
309 /* Past this point we are serving either a GET or HEAD */
310 /* Print all the header info */
311 http_output(http_200);
312 http_output(httpd_server_ident);
313 http_date();
314
315 sprintf(buff, "Content-length: %lld\r\n", file_status.st_size);
316
317 if (strstr(filename,".txt")) {
318 strcpy(buff,"Content-type: text/plain\r\n");
319 } else if (strstr(filename,".html") || strstr(filename,".htm")) {
320 strcpy(buff,"Content-type: text/html\r\n");
321 } else if (strstr(filename,".gif")) {
322 strcpy(buff,"Content-type: image/gif\r\n");
323 } else if (strstr(filename,".jpg")) {
324 strcpy(buff,"Content-type: image/jpeg\r\n");
325 } else {
326 /* Take a guess at content if we don't have something already */
327 strcpy(buff,"Content-type: ");
328 strcat(buff,strstr(filename,".")+1);
329 strcat(buff,"\r\n");
330 }
331 write(con_sock, buff, strlen(buff));
332
333 strftime(buff, 50, "Last-Modified: %a, %d %h %Y %H:%M:%S %Z\r\n\r\n", gmtime(&file_status.st_mtime));
334 write(con_sock, buff, strlen(buff));
335
336 /* Send data only if GET request */
337 if (cmd == 1) {
338 while (lg = read(fd, buff, 8192))
339 write(con_sock, buff, lg);
340 }
341
342end_request:
343 close(fd);
344 close(con_sock);
345
346}
347
348/*
349 * Simple httpd server for use in PicoBSD or other embedded application.
350 * Should satisfy simple httpd needs. For more demanding situations
351 * apache is probably a better (but much larger) choice.
352 */
353int
354main(int argc, char *argv[])
355{
356 extern char *optarg;
357 extern int optind;
358 int ch, ld;
359 int httpd_group = 65534;
360 pid_t server_pid;
361
362 /* Default for html directory */
363 strcpy (homedir,getenv("HOME"));
364 if (!geteuid()) strcpy (homedir,"/httphome");
365 else strcat (homedir,"/httphome");
366
367 /* Defaults for log file */
368 if (geteuid()) {
369 strcpy(logfile,getenv("HOME"));
370 strcat(logfile,"/");
371 strcat(logfile,"jhttp.log");
372 } else
373 strcpy(logfile,"/var/log/jhttpd.log");
374
375 /* Parse command line arguments */
376 while ((ch = getopt(argc, argv, "d:f:g:l:p:vDh")) != -1)
377 switch (ch) {
378 case 'd':
379 strcpy(homedir,optarg);
380 break;
381 case 'f':
382 daemonize = 0;
383 verbose = 1;
384 strcpy(fetch_mode,optarg);
385 break;
386 case 'g':
387 httpd_group = atoi(optarg);
388 break;
389 case 'l':
390 strcpy(logfile,optarg);
391 break;
392 case 'p':
393 http_port = atoi(optarg);
394 break;
395 case 'v':
396 verbose = 1;
397 break;
398 case 'D':
399 daemonize = 0;
400 break;
401 case '?':
402 case 'h':
403 default:
404 printf("usage: simple_httpd [[-d directory][-g grpid][-l logfile][-p port][-vD]]\n");
405 exit(1);
406 /* NOTREACHED */
407 }
408
409 /* Not running as root and no port supplied, assume 1080 */
410 if ((http_port == 80) && geteuid()) {
411 http_port = 1080;
412 }
413
414 /* Do we really have rights in the html directory? */
415 if (fetch_mode[0] == NULL) {
416 if (chdir(homedir)) {
417 perror("chdir");
418 puts(homedir);
419 exit(1);
420 }
421 }
422
423 /* Create log file if it doesn't exit */
424 if ((access(logfile,W_OK)) && daemonize) {
425 ld = open (logfile,O_WRONLY);
426 chmod (logfile,00600);
427 close(ld);
428 }
429
430 init_servconnection();
431
432 if (verbose) {
433 printf("Server started with options \n");
434 printf("port: %d\n",http_port);
435 if (fetch_mode[0] == NULL) printf("html home: %s\n",homedir);
436 if (daemonize) printf("logfile: %s\n",logfile);
437 }
438
439 /* httpd is spawned */
440 if (daemonize) {
441 if (server_pid = fork()) {
442 wait3(0,WNOHANG,0);
443 if (verbose) printf("pid: %d\n",server_pid);
444 exit(0);
445 }
446 wait3(0,WNOHANG,0);
447 }
448
449 if (fetch_mode[0] == NULL) setpgrp(0,httpd_group);
450
451 /* How many connections do you want?
452 * Keep this lower than the available number of processes
453 */
454 if (listen(http_sock,15) < 0) exit(1);
455
456 label:
457 wait_connection();
458
459 if (fork()) {
460 wait3(0,WNOHANG,0);
461 close(con_sock);
462 goto label;
463 }
464
465 http_request();
466
467 wait3(0,WNOHANG,0);
468 exit(0);
469}
470
471
472char *adate()
473{
474 static char out[50];
268 }
269 conti:
270 if (filename == NULL) {
271 http_output(http_405[0]);
272 http_output(http_405[1]);
273 goto end_request;
274 }
275 /* End of CGI handling */
276
277 /* Reject any request with '..' in it, bad hacker */
278 c = filename;
279 while (*c != '\0')
280 if (c[0] == '.' && c[1] == '.') {
281 http_output(http_404[0]);
282 http_output(http_404[1]);
283 goto end_request;
284 } else
285 c++;
286
287 /* Open filename */
288 fd = open(filename, O_RDONLY);
289 if (fd < 0) {
290 http_output(http_404[0]);
291 http_output(http_404[1]);
292 goto end_request;
293 }
294
295 /* Get file status information */
296 if (fstat(fd, &file_status) < 0) {
297 http_output(http_404[0]);
298 http_output(http_404[1]);
299 goto end_request;
300 }
301
302 /* Is it a regular file? */
303 if (!S_ISREG(file_status.st_mode)) {
304 http_output(http_404[0]);
305 http_output(http_404[1]);
306 goto end_request;
307 }
308
309 /* Past this point we are serving either a GET or HEAD */
310 /* Print all the header info */
311 http_output(http_200);
312 http_output(httpd_server_ident);
313 http_date();
314
315 sprintf(buff, "Content-length: %lld\r\n", file_status.st_size);
316
317 if (strstr(filename,".txt")) {
318 strcpy(buff,"Content-type: text/plain\r\n");
319 } else if (strstr(filename,".html") || strstr(filename,".htm")) {
320 strcpy(buff,"Content-type: text/html\r\n");
321 } else if (strstr(filename,".gif")) {
322 strcpy(buff,"Content-type: image/gif\r\n");
323 } else if (strstr(filename,".jpg")) {
324 strcpy(buff,"Content-type: image/jpeg\r\n");
325 } else {
326 /* Take a guess at content if we don't have something already */
327 strcpy(buff,"Content-type: ");
328 strcat(buff,strstr(filename,".")+1);
329 strcat(buff,"\r\n");
330 }
331 write(con_sock, buff, strlen(buff));
332
333 strftime(buff, 50, "Last-Modified: %a, %d %h %Y %H:%M:%S %Z\r\n\r\n", gmtime(&file_status.st_mtime));
334 write(con_sock, buff, strlen(buff));
335
336 /* Send data only if GET request */
337 if (cmd == 1) {
338 while (lg = read(fd, buff, 8192))
339 write(con_sock, buff, lg);
340 }
341
342end_request:
343 close(fd);
344 close(con_sock);
345
346}
347
348/*
349 * Simple httpd server for use in PicoBSD or other embedded application.
350 * Should satisfy simple httpd needs. For more demanding situations
351 * apache is probably a better (but much larger) choice.
352 */
353int
354main(int argc, char *argv[])
355{
356 extern char *optarg;
357 extern int optind;
358 int ch, ld;
359 int httpd_group = 65534;
360 pid_t server_pid;
361
362 /* Default for html directory */
363 strcpy (homedir,getenv("HOME"));
364 if (!geteuid()) strcpy (homedir,"/httphome");
365 else strcat (homedir,"/httphome");
366
367 /* Defaults for log file */
368 if (geteuid()) {
369 strcpy(logfile,getenv("HOME"));
370 strcat(logfile,"/");
371 strcat(logfile,"jhttp.log");
372 } else
373 strcpy(logfile,"/var/log/jhttpd.log");
374
375 /* Parse command line arguments */
376 while ((ch = getopt(argc, argv, "d:f:g:l:p:vDh")) != -1)
377 switch (ch) {
378 case 'd':
379 strcpy(homedir,optarg);
380 break;
381 case 'f':
382 daemonize = 0;
383 verbose = 1;
384 strcpy(fetch_mode,optarg);
385 break;
386 case 'g':
387 httpd_group = atoi(optarg);
388 break;
389 case 'l':
390 strcpy(logfile,optarg);
391 break;
392 case 'p':
393 http_port = atoi(optarg);
394 break;
395 case 'v':
396 verbose = 1;
397 break;
398 case 'D':
399 daemonize = 0;
400 break;
401 case '?':
402 case 'h':
403 default:
404 printf("usage: simple_httpd [[-d directory][-g grpid][-l logfile][-p port][-vD]]\n");
405 exit(1);
406 /* NOTREACHED */
407 }
408
409 /* Not running as root and no port supplied, assume 1080 */
410 if ((http_port == 80) && geteuid()) {
411 http_port = 1080;
412 }
413
414 /* Do we really have rights in the html directory? */
415 if (fetch_mode[0] == NULL) {
416 if (chdir(homedir)) {
417 perror("chdir");
418 puts(homedir);
419 exit(1);
420 }
421 }
422
423 /* Create log file if it doesn't exit */
424 if ((access(logfile,W_OK)) && daemonize) {
425 ld = open (logfile,O_WRONLY);
426 chmod (logfile,00600);
427 close(ld);
428 }
429
430 init_servconnection();
431
432 if (verbose) {
433 printf("Server started with options \n");
434 printf("port: %d\n",http_port);
435 if (fetch_mode[0] == NULL) printf("html home: %s\n",homedir);
436 if (daemonize) printf("logfile: %s\n",logfile);
437 }
438
439 /* httpd is spawned */
440 if (daemonize) {
441 if (server_pid = fork()) {
442 wait3(0,WNOHANG,0);
443 if (verbose) printf("pid: %d\n",server_pid);
444 exit(0);
445 }
446 wait3(0,WNOHANG,0);
447 }
448
449 if (fetch_mode[0] == NULL) setpgrp(0,httpd_group);
450
451 /* How many connections do you want?
452 * Keep this lower than the available number of processes
453 */
454 if (listen(http_sock,15) < 0) exit(1);
455
456 label:
457 wait_connection();
458
459 if (fork()) {
460 wait3(0,WNOHANG,0);
461 close(con_sock);
462 goto label;
463 }
464
465 http_request();
466
467 wait3(0,WNOHANG,0);
468 exit(0);
469}
470
471
472char *adate()
473{
474 static char out[50];
475 long now;
475 time_t now;
476 struct tm *t;
477 time(&now);
478 t = localtime(&now);
479 sprintf(out, "%02d:%02d:%02d %02d/%02d/%02d",
480 t->tm_hour, t->tm_min, t->tm_sec,
481 t->tm_mday, t->tm_mon+1, t->tm_year );
482 return out;
483}
476 struct tm *t;
477 time(&now);
478 t = localtime(&now);
479 sprintf(out, "%02d:%02d:%02d %02d/%02d/%02d",
480 t->tm_hour, t->tm_min, t->tm_sec,
481 t->tm_mday, t->tm_mon+1, t->tm_year );
482 return out;
483}