Un po' di materiale su {{{splice}}} e inizio della ripulitura degli
[gapil.git] / sources / wwwd.c
1 /* wwwd.c
2  * 
3  * Copyright (C) 2005 Simone Piccardi
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or (at
8  * your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 /****************************************************************
20  *
21  * Program wwwd 
22  * Elementary WWW server (port 80)
23  *
24  * Author: Simone Piccardi
25  * Mar. 2005
26  *
27  * Usage: wwwd -h give all info
28  *
29  ****************************************************************/
30 /* 
31  * Include needed headers
32  */
33 #define _GNU_SOURCE
34 #include <sys/types.h>   /* primitive system data types */
35 #include <sys/stat.h>    /* file characteristics constants and functions */
36 #include <unistd.h>      /* unix standard library */
37 #include <stdlib.h>      /* C standard library */
38 #include <arpa/inet.h>   /* IP addresses conversion utilities */
39 #include <sys/socket.h>  /* socket constants, types and functions */
40 #include <stdio.h>       /* standard I/O library */
41 #include <time.h>        /* date and time constants, types and functions */
42 #include <syslog.h>      /* syslog system functions */
43 #include <signal.h>      /* signal constants, types and functions */
44 #include <errno.h>       /* error definitions and routines */
45 #include <string.h>      /* C strings library */
46
47 #include "Gapil.h"
48
49 #define BACKLOG 10
50 #define MAXLINE 256
51 int demonize  = 1;  /* daemon use option: default is daemon */
52 int debugging = 0;  /* debug info printing option: default is no debug */
53
54 /* Subroutines declaration */
55 struct code_page {
56     char * code; 
57     char * name;
58     char * body;
59 };
60
61 void usage(void);
62 void ServPage(int sockfd);
63 void PrintErr(char * error);
64 void print_headers(FILE *file, struct code_page code);
65 void print_error(FILE *file, struct code_page page, char * string);
66
67 /* Program beginning */
68 int main(int argc, char *argv[])
69 {
70 /* 
71  * Variables definition  
72  */
73     int list_fd, conn_fd;
74     int compat = 0;
75     int reroot = 0;
76     int reuse = 1;
77     char * rootdir;
78     pid_t pid;
79     struct sockaddr_in cli_add;
80     socklen_t len;
81     char debug[MAXLINE], ipaddr[20];
82     /*
83      * Input section: decode parameters passed in the calling 
84      * Use getopt function
85      */
86     int i;
87     opterr = 0;  /* don't want writing to stderr */
88     while ( (i = getopt(argc, argv, "hwdicr:")) != -1) {
89         switch (i) {
90         /* 
91          * Handling options 
92          */ 
93         case 'h':  
94             printf("Wrong -h option use\n");
95             usage();
96             return(0);
97             break;
98         case 'i':
99             demonize = 0;
100             break;
101         case 'c':
102             compat = 1;
103             break;
104         case 'd':
105             debugging = 1;
106             break;
107         case 'w':
108             reuse = 0;
109             break;
110         case 'r':
111             reroot = 1;
112             rootdir = optarg;
113             break;
114         case '?':   /* unrecognized options */
115             printf("Unrecognized options -%c\n",optopt);
116             usage();
117         default:    /* should not reached */
118             usage();
119         }
120     }
121     /* ***********************************************************
122      * 
123      *           Options processing completed
124      *
125      *                Main code beginning
126      * 
127      * ***********************************************************/
128     /* Main code begin here */
129     if (compat) {                             /* install signal handler */
130         Signal(SIGCHLD, HandSigCHLD);         /* non restarting handler */
131     } else {
132         SignalRestart(SIGCHLD, HandSigCHLD);  /* restarting handler */
133     }
134     /* create and bind socket */
135     if ( (list_fd = sockbindopt(argv[optind], "www", 6, 
136                                 SOCK_STREAM, reuse)) < 0) {
137         return 1;
138     }   
139     /* chroot if requested */
140     if (reroot) {
141         if (chdir(rootdir)) {
142             perror("Cannot find directory to chroot");
143             exit(1);
144         }
145         if (chroot(rootdir)) {
146             perror("Cannot chroot");
147             exit(1);
148         }
149     }
150     /* release privileges and go daemon */
151     if (setgid(65534) !=0) { /* first give away group privileges */
152         perror("cannot give away group privileges");
153         exit(1);
154     }
155     if (setuid(65534) !=0) { /* and only after user ... */
156         perror("cannot give away user privileges");
157         exit(1);
158     }
159     if (demonize) {          /* go daemon */
160         openlog(argv[0], 0, LOG_DAEMON); /* open logging */
161         if (daemon(0, 0) != 0) {
162             perror("cannot start as daemon");
163             exit(1);
164         }
165     }
166     /* main body */
167     if (listen(list_fd, BACKLOG) < 0 ) {
168         PrintErr("listen error");
169         exit(1);
170     }
171     /* handle echo to client */
172     while (1) {
173         /* accept connection */
174         len = sizeof(cli_add);
175         while (((conn_fd = accept(list_fd, (struct sockaddr *)&cli_add, &len)) 
176                 < 0) && (errno == EINTR)); 
177         if (conn_fd < 0) {
178             PrintErr("accept error");
179             exit(1);
180         }
181         if (debugging) {
182             inet_ntop(AF_INET, &cli_add.sin_addr, ipaddr, sizeof(ipaddr));
183             snprintf(debug, MAXLINE, "Accepted connection form %s\n", ipaddr);
184             if (demonize) {
185                 syslog(LOG_DEBUG, debug);
186             } else {
187                 printf("%s", debug);
188             }
189         }
190         /* fork to handle connection */
191         if ( (pid = fork()) < 0 ){
192             PrintErr("fork error");
193             exit(1);
194         }
195         if (pid == 0) {      /* child */
196             close(list_fd);          /* close listening socket */   
197             ServPage(conn_fd);       /* handle echo */
198             if (debugging) {
199                 snprintf(debug, MAXLINE, "Closed connection %s\n", ipaddr);
200                 if (demonize) {
201                     syslog(LOG_DEBUG, debug);
202                 } else {
203                     printf("%s", debug);
204                 }
205             }
206             exit(0);
207         } else {             /* parent */
208             close(conn_fd);          /* close connected socket */
209         }
210     }
211     /* normal exit, never reached */
212     exit(0);
213 }
214 /*
215  * routine to print usage info and exit
216  */
217 void usage(void) 
218 {
219     printf("Elementary echo server\n");
220     printf("Usage:\n");
221     printf("  wwwd [-h] \n");
222     printf("  -h           print this help\n");
223     printf("  -d           write debug info\n");
224     printf("  -i           use interactively\n");
225     printf("  -c           disable BSD semantics\n");
226     printf("  -r /path     chroot on /path\n");
227     exit(1);
228 }
229 /*
230  * routine to handle echo for connection
231  */
232 void ServPage(int sockfd) 
233 {
234     char buffer[MAXLINE];
235     char outbuf[1024];
236     FILE *sock, *file;
237     char *line, *copy, *method, *ptr, *filename, *version;
238     char *methods[] = { "GET", "HEAD", NULL };
239     struct code_page codes[] = {
240         { "200", "OK", "%s"},
241         { "400", "Bad Request", 
242           "Your browser sent a request that this server could not understand."
243           "<P>The request line<P>%s<P> is invalid following the protocol<P>"}, 
244         { "404", "Not Found", 
245           "The requested URL %s was not found on this server.<P>"},
246         { "500", "Internal Server Error", 
247           "We got an error processing your request.<P>Error is: %s<P>"},
248         { "405", "Method Not Allowed", "Method %s not allowed.<P>"},
249         { "403", "Forbidden", "You cannot access %s.<P>"}
250     };
251     int nleft;
252     int i;
253
254     sock = fdopen(sockfd, "w+");
255     /* main loop, reading 0 char means client close connection */
256     line = fgets(buffer, MAXLINE, sock);
257     if (line == NULL) {
258         PrintErr("Errore in lettura");
259         return;
260     }
261     /* parsing first line, getting method and filename */
262     copy = strndupa(line, MAXLINE);
263     if ((method = strtok_r(copy, " ", &ptr)) == NULL) {
264         print_headers(sock, codes[1]);
265         print_error(sock, codes[1], line);
266         return;
267     }
268     if ((filename = strtok_r(NULL, " ", &ptr)) == NULL) {
269         print_headers(sock, codes[1]);
270         print_error(sock, codes[1], line);
271         return;
272     }
273     if ((version = strtok_r(NULL, " ", &ptr)) == NULL) {
274         print_headers(sock, codes[1]);
275         print_error(sock, codes[1], line);
276         return;
277     }
278     i = 0;
279     while ( (ptr = methods[i]) != NULL) {
280         if ( (strncmp(ptr, method, strlen(ptr)) == 0)) {
281             break;
282         }
283         i++;
284     }
285     if (i>=2) {
286         print_headers(sock, codes[4]);
287         print_error(sock, codes[4], method);
288         return;
289     }
290
291     while (strcmp(line,"\r\n")) {
292         line = fgets(buffer, MAXLINE, sock);
293     }
294
295     if ( (file = fopen(filename, "r")) == NULL) {
296         if ( (errno == EACCES)||(errno == EPERM) ) {
297             print_headers(sock, codes[5]);
298             print_error(sock, codes[5], filename);
299         } else {
300             print_headers(sock, codes[2]);
301             print_error(sock, codes[2], filename);
302         }
303         return;
304     }
305     print_headers(sock, codes[0]);
306     while (!feof(file)) {
307         if ( (nleft = full_fread(file, outbuf, 1024)) != 0) {
308             if (ferror(file)) {
309                 strncpy(buffer, strerror(errno), MAXLINE);
310                 print_headers(sock, codes[3]);
311                 print_error(sock, codes[3], buffer);
312                 return;
313             }
314         }
315         if (full_fwrite(sock, outbuf, 1024-nleft) != 0) {
316             if (ferror(file)) {
317                 strncpy(buffer, strerror(errno), MAXLINE);
318                 print_headers(sock, codes[3]);
319                 print_error(sock, codes[3], buffer);
320                 return;
321             }   
322         }
323     }
324     fclose(file);
325     fclose(sock);
326     return;
327 }
328 /*
329  * routine to print error on stout or syslog
330  */
331 void PrintErr(char * error) 
332 {
333     if (demonize) {                       /* daemon mode */
334         syslog(LOG_ERR, "%s: %m", error); /* log string and error message */
335     } else {
336         perror(error);
337     }
338     return;
339 }
340
341 void print_headers(FILE *file, struct code_page code)
342 {
343     time_t tempo;
344
345     fprintf(file, "HTTP/1.0 %s %s \n", code.code, code.name);
346     time(&tempo);
347     fprintf(file, "Date: %s", ctime(&tempo));
348     fprintf(file, "Server: WWWd test server\n");
349     fprintf(file, "Connection: close\n");
350     fprintf(file, "Content-Type: text/html; charset=iso-8859-1\n");
351     fprintf(file, "\n");
352     return;
353 }
354
355 void print_error(FILE *file, struct code_page page, char * string)
356 {
357     fprintf(file, "<HTML><HEAD><TITLE>%s %s</TITLE></HEAD>\n",
358             page.code, page.name);
359     fprintf(file, "<BODY><H1>%s</H1>\n", page.name);
360     fprintf(file, page.body, string);
361     fprintf(file, "<HR><ADDRESS>WWWd by S. Piccardi</ADDRESS></BODY></HTML>");
362     return;
363 }
364