Upload 2.0.2
[physicsfs] / extras / physfshttpd.c
1 /*
2  * This is a quick and dirty HTTP server that uses PhysicsFS to retrieve
3  *  files. It is not robust at all, probably buggy, and definitely poorly
4  *  designed. It's just meant to show that it can be done.
5  *
6  * Basically, you compile this code, and run it:
7  *   ./physfshttpd archive1.zip archive2.zip /path/to/a/real/dir etc...
8  *
9  * The files are appended in order to the PhysicsFS search path, and when
10  *  a client request comes it, it looks for the file in said search path.
11  *
12  * My goal was to make this work in less than 300 lines of C, so again, it's
13  *  not to be used for any serious purpose. Patches to make this application
14  *  suck less will be readily and gratefully accepted.
15  *
16  * Command line I used to build this on Linux:
17  *  gcc -Wall -Werror -g -o bin/physfshttpd extras/physfshttpd.c -lphysfs
18  *
19  * License: this code is public domain. I make no warranty that it is useful,
20  *  correct, harmless, or environmentally safe.
21  *
22  * This particular file may be used however you like, including copying it
23  *  verbatim into a closed-source project, exploiting it commercially, and
24  *  removing any trace of my name from the source (although I hope you won't
25  *  do that). I welcome enhancements and corrections to this file, but I do
26  *  not require you to send me patches if you make changes. This code has
27  *  NO WARRANTY.
28  *
29  * Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
30  *  Please see LICENSE.txt in the root of the source tree.
31  *
32  *  This file was written by Ryan C. Gordon. (icculus@icculus.org).
33  */
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <errno.h>
40 #include <ctype.h>
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45
46 #ifndef LACKING_SIGNALS
47 #include <signal.h>
48 #endif
49
50 #ifndef LACKING_PROTOENT
51 #include <netdb.h>
52 #endif
53
54 #include "physfs.h"
55
56
57 #define DEFAULT_PORTNUM  6667
58
59 typedef struct
60 {
61     int sock;
62     struct sockaddr *addr;
63     socklen_t addrlen;
64 } http_args;
65
66
67 static char *txt404 =
68 "HTTP/1.0 404 Not Found\n"
69 "Connection: close\n"
70 "Content-Type: text/html; charset=utf-8\n"
71 "\n"
72 "<html><head><title>404 Not Found</title></head>\n"
73 "<body>Can't find that.</body></html>\n\n";
74
75
76 static void feed_file_http(const char *ipstr, int sock, const char *fname)
77 {
78     PHYSFS_File *in = PHYSFS_openRead(fname);
79     char buffer[1024];
80     printf("%s: requested [%s].\n", ipstr, fname);
81     if (in == NULL)
82     {
83         printf("%s: Can't open [%s]: %s.\n",
84                ipstr, fname, PHYSFS_getLastError());
85         write(sock, txt404, strlen(txt404));  /* !!! FIXME: Check retval */
86     } /* if */
87     else
88     {
89         do
90         {
91             PHYSFS_sint64 br = PHYSFS_read(in, buffer, 1, sizeof (buffer));
92             if (br == -1)
93             {
94                 printf("%s: Read error: %s.\n", ipstr, PHYSFS_getLastError());
95                 break;
96             } /* if */
97
98             write(sock, buffer, (int) br);   /* !!! FIXME: CHECK THIS RETVAL! */
99         } while (!PHYSFS_eof(in));
100
101         PHYSFS_close(in);
102     } /* else */
103 } /* feed_file_http */
104
105
106 static void *do_http(void *_args)
107 {
108     http_args *args = (http_args *) _args;
109     char ipstr[128];
110     char buffer[512];
111     char *ptr;
112     strncpy(ipstr, inet_ntoa(((struct sockaddr_in *) args->addr)->sin_addr),
113             sizeof (ipstr));
114     ipstr[sizeof (ipstr) - 1] = '\0';
115
116     printf("%s: connected.\n", ipstr);
117     read(args->sock, buffer, sizeof (buffer));
118     buffer[sizeof (buffer) - 1] = '\0';
119     ptr = strchr(buffer, '\n');
120     if (!ptr)
121         printf("%s: potentially bogus request.\n", ipstr);
122     else
123     {
124         *ptr = '\0';
125         ptr = strchr(buffer, '\r');
126         if (ptr != NULL)
127             *ptr = '\0';
128
129         if ((toupper(buffer[0]) == 'G') &&
130             (toupper(buffer[1]) == 'E') &&
131             (toupper(buffer[2]) == 'T') &&
132             (toupper(buffer[3]) == ' ') &&
133             (toupper(buffer[4]) == '/'))
134         {
135             ptr = strchr(buffer + 5, ' ');
136             if (ptr != NULL)
137                 *ptr = '\0';
138             feed_file_http(ipstr, args->sock, buffer + 4);
139         } /* if */
140     } /* else */
141
142     /* !!! FIXME: Time the transfer. */
143     printf("%s: closing connection.\n", ipstr);
144     close(args->sock);
145     free(args->addr);
146     free(args);
147     return(NULL);
148 } /* do_http */
149
150
151 static void serve_http_request(int sock, struct sockaddr *addr,
152                                socklen_t addrlen)
153 {
154     http_args *args = (http_args *) malloc(sizeof (http_args));
155     if (args == NULL)
156     {
157         printf("out of memory.\n");
158         return;
159     } /* if */
160     args->addr = (struct sockaddr *) malloc(addrlen);
161     if (args->addr == NULL)
162     {
163         free(args);
164         printf("out of memory.\n");
165         return;
166     } /* if */
167
168     args->sock = sock;
169     args->addrlen = addrlen;
170     memcpy(args->addr, addr, addrlen);
171
172     /* !!! FIXME: optionally spin a thread... */
173     do_http((void *) args);
174 } /* server_http_request */
175
176
177 static int create_listen_socket(short portnum)
178 {
179     int retval = -1;
180     int protocol = 0;  /* pray this is right. */
181
182 #ifndef LACKING_PROTOENT
183     struct protoent *prot;
184     setprotoent(0);
185     prot = getprotobyname("tcp");
186     if (prot != NULL)
187         protocol = prot->p_proto;
188 #endif
189
190     retval = socket(PF_INET, SOCK_STREAM, protocol);
191     if (retval >= 0)
192     {
193         struct sockaddr_in addr;
194         addr.sin_family = AF_INET;
195         addr.sin_port = htons(portnum);
196         addr.sin_addr.s_addr = INADDR_ANY;
197         if ((bind(retval, &addr, (socklen_t) sizeof (addr)) == -1) ||
198             (listen(retval, 5) == -1))
199         {
200             close(retval);
201             retval = -1;
202         } /* if */
203     } /* if */
204
205     return(retval);
206 } /* create_listen_socket */
207
208
209 static int listensocket = -1;
210
211 void at_exit_cleanup(void)
212 {
213     /*
214      * !!! FIXME: If thread support, signal threads to terminate and
215      * !!! FIXME:  wait for them to clean up.
216      */
217
218     if (listensocket >= 0)
219         close(listensocket);
220
221     if (!PHYSFS_deinit())
222         printf("PHYSFS_deinit() failed: %s\n", PHYSFS_getLastError());
223 } /* at_exit_cleanup */
224
225
226 int main(int argc, char **argv)
227 {
228     int i;
229     int portnum = DEFAULT_PORTNUM;
230
231     setbuf(stdout, NULL);
232     setbuf(stderr, NULL);
233
234 #ifndef LACKING_SIGNALS
235     /* I'm not sure if this qualifies as a cheap trick... */
236     signal(SIGTERM, exit);
237     signal(SIGINT, exit);
238     signal(SIGFPE, exit);
239     signal(SIGSEGV, exit);
240     signal(SIGPIPE, exit);
241     signal(SIGILL, exit);
242 #endif
243
244     if (argc == 1)
245     {
246         printf("USAGE: %s <archive1> [archive2 [... archiveN]]\n", argv[0]);
247         return(42);
248     } /* if */
249
250     if (!PHYSFS_init(argv[0]))
251     {
252         printf("PHYSFS_init() failed: %s\n", PHYSFS_getLastError());
253         return(42);
254     } /* if */
255
256     /* normally, this is bad practice, but oh well. */
257     atexit(at_exit_cleanup);
258
259     for (i = 1; i < argc; i++)
260     {
261         if (!PHYSFS_addToSearchPath(argv[i], 1))
262             printf(" WARNING: failed to add [%s] to search path.\n", argv[i]);
263     } /* else */
264
265     listensocket = create_listen_socket(portnum);
266     if (listensocket < 0)
267     {
268         printf("listen socket failed to create.\n");
269         return(42);
270     } /* if */
271
272     while (1)  /* infinite loop for now. */
273     {
274         struct sockaddr addr;
275         socklen_t len;
276         int s = accept(listensocket, &addr, &len);
277         if (s < 0)
278         {
279             printf("accept() failed: %s\n", strerror(errno));
280             close(listensocket);
281             return(42);
282         } /* if */
283
284         serve_http_request(s, &addr, len);
285     } /* while */
286
287     return(0);
288 } /* main */
289
290 /* end of physfshttpd.c ... */
291