/[hydra]/hydra/src/util.c
ViewVC logotype

Contents of /hydra/src/util.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.10 - (show annotations)
Sun Sep 29 11:00:04 2002 UTC (21 years, 6 months ago) by nmav
Branch: MAIN
Changes since 1.9: +3 -3 lines
File MIME type: text/plain
Added support for If-Range, If-Match, If-None-Match HTTP/1.1 header
fields. The server also generates ETag headers for static content using
the last modified field of the file, and the file size.

Fixed the behaviour of the range parser, when a bogus range was received.
Now it does not send any message, it silently ignores the bogus range.

1 /*
2 * Hydra, an http server
3 * Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
4 * Some changes Copyright (C) 1996,97 Larry Doolittle <ldoolitt@boa.org>
5 * Some changes Copyright (C) 1996 Charles F. Randall <crandall@goldsys.com>
6 * Some changes Copyright (C) 1996-99 Jon Nelson <jnelson@boa.org>
7 * Portions Copyright (C) 2002 Nikos Mavroyanopoulos <nmav@gnutls.org>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 1, or (at your option)
12 * any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 *
23 */
24
25 /* $Id: util.c,v 1.9 2002/09/29 08:02:56 nmav Exp $ */
26
27 #include "boa.h"
28
29 #define HEX_TO_DECIMAL(char1, char2) \
30 (((char1 >= 'A') ? (((char1 & 0xdf) - 'A') + 10) : (char1 - '0')) * 16) + \
31 (((char2 >= 'A') ? (((char2 & 0xdf) - 'A') + 10) : (char2 - '0')))
32
33 const char month_tab[48] =
34 "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ";
35 const char day_tab[] = "Sun,Mon,Tue,Wed,Thu,Fri,Sat,";
36
37 /*
38 * Name: clean_pathname
39 *
40 * Description: Replaces unsafe/incorrect instances of:
41 * //[...] with /
42 * /./ with /
43 * /../ with / (technically not what we want, but browsers should deal
44 * with this, not servers)
45 */
46
47 void clean_pathname(char *pathname)
48 {
49 char *cleanpath, c;
50
51 cleanpath = pathname;
52 while ((c = *pathname++)) {
53 if (c == '/') {
54 while (1) {
55 if (*pathname == '/')
56 pathname++;
57 else if (*pathname == '.' && *(pathname + 1) == '/')
58 pathname += 2;
59 else if (*pathname == '.' && *(pathname + 1) == '.' &&
60 *(pathname + 2) == '/') {
61 pathname += 3;
62 } else
63 break;
64 }
65 c = '/';
66 }
67 *cleanpath++ = c;
68 }
69
70 *cleanpath = '\0';
71 }
72
73 /*
74 * Name: get_commonlog_time
75 *
76 * Description: Returns the current time in common log format in a static
77 * char buffer.
78 *
79 * commonlog time is exactly 25 characters long
80 * because this is only used in logging, we add " [" before and "] " after
81 * making 29 characters
82 * "[27/Feb/1998:20:20:04 +0000] "
83 *
84 * Constrast with rfc822 time:
85 * "Sun, 06 Nov 1994 08:49:37 GMT"
86 *
87 * Altered 10 Jan 2000 by Jon Nelson ala Drew Streib for non UTC logging
88 *
89 */
90
91 void get_commonlog_time(char buf[30])
92 {
93 struct tm *t;
94 char *p;
95 unsigned int a;
96 int time_offset;
97
98 if (use_localtime) {
99 t = localtime(&current_time);
100 time_offset = TIMEZONE_OFFSET(t);
101 } else {
102 t = gmtime(&current_time);
103 time_offset = 0;
104 }
105
106 p = buf + 29;
107 *p-- = '\0';
108 *p-- = ' ';
109 *p-- = ']';
110 a = abs(time_offset / 60);
111 *p-- = '0' + a % 10;
112 a /= 10;
113 *p-- = '0' + a % 6;
114 a /= 6;
115 *p-- = '0' + a % 10;
116 *p-- = '0' + a / 10;
117 *p-- = (time_offset >= 0) ? '+' : '-';
118 *p-- = ' ';
119
120 a = t->tm_sec;
121 *p-- = '0' + a % 10;
122 *p-- = '0' + a / 10;
123 *p-- = ':';
124 a = t->tm_min;
125 *p-- = '0' + a % 10;
126 *p-- = '0' + a / 10;
127 *p-- = ':';
128 a = t->tm_hour;
129 *p-- = '0' + a % 10;
130 *p-- = '0' + a / 10;
131 *p-- = ':';
132 a = 1900 + t->tm_year;
133 while (a) {
134 *p-- = '0' + a % 10;
135 a /= 10;
136 }
137 /* p points to an unused spot */
138 *p-- = '/';
139 p -= 2;
140 memcpy(p--, month_tab + 4 * (t->tm_mon), 3);
141 *p-- = '/';
142 a = t->tm_mday;
143 *p-- = '0' + a % 10;
144 *p-- = '0' + a / 10;
145 *p = '[';
146 return; /* should be same as returning buf */
147 }
148
149 /*
150 * Name: month2int
151 *
152 * Description: Turns a three letter month into a 0-11 int
153 *
154 * Note: This function is from wn-v1.07 -- it's clever and fast
155 */
156
157 int month2int(char *monthname)
158 {
159 switch (*monthname) {
160 case 'A':
161 return (*++monthname == 'p' ? 3 : 7);
162 case 'D':
163 return (11);
164 case 'F':
165 return (1);
166 case 'J':
167 if (*++monthname == 'a')
168 return (0);
169 return (*++monthname == 'n' ? 5 : 6);
170 case 'M':
171 return (*(monthname + 2) == 'r' ? 2 : 4);
172 case 'N':
173 return (10);
174 case 'O':
175 return (9);
176 case 'S':
177 return (8);
178 default:
179 return (-1);
180 }
181 }
182
183 /*
184 * Name: modified_since
185 * Description: Decides whether a file's mtime is newer than the
186 * If-Modified-Since header of a request.
187 *
188
189 Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
190 Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
191 Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
192 31 September 2000 23:59:59 GMT ; non-standard
193
194 * RETURN VALUES:
195 * 0: File has not been modified since specified time.
196 * 1: File has been.
197 * -1: Error!
198 */
199
200 int modified_since(time_t mtime, char *if_modified_since)
201 {
202 struct tm *file_gmt;
203 char *ims_info;
204 char monthname[10 + 1];
205 int day, month, year, hour, minute, second;
206 int comp;
207
208 ims_info = if_modified_since;
209 while (*ims_info != ' ' && *ims_info != '\0')
210 ++ims_info;
211 if (*ims_info != ' ')
212 return -1;
213
214 /* the pre-space in the third scanf skips whitespace for the string */
215 if (sscanf(ims_info, "%d %3s %d %d:%d:%d GMT", /* RFC 1123 */
216 &day, monthname, &year, &hour, &minute, &second) == 6);
217 else if (sscanf(ims_info, "%d-%3s-%d %d:%d:%d GMT", /* RFC 1036 */
218 &day, monthname, &year, &hour, &minute, &second) == 6)
219 year += 1900;
220 else if (sscanf(ims_info, " %3s %d %d:%d:%d %d", /* asctime() format */
221 monthname, &day, &hour, &minute, &second, &year) == 6);
222 /* allow this non-standard date format: 31 September 2000 23:59:59 GMT */
223 /* NOTE: Use if_modified_since here, because the date *starts*
224 * with the day, versus a throwaway item
225 */
226 else if (sscanf(if_modified_since, "%d %10s %d %d:%d:%d GMT",
227 &day, monthname, &year, &hour, &minute, &second) == 6);
228 else {
229 log_error_time();
230 fprintf(stderr, "Error in %s, line %d: Unable to sscanf \"%s\"\n",
231 __FILE__, __LINE__, ims_info);
232 return -1; /* error */
233 }
234
235 file_gmt = gmtime(&mtime);
236 month = month2int(monthname);
237
238 /* Go through from years to seconds -- if they are ever unequal,
239 we know which one is newer and can return */
240
241 if ((comp = 1900 + file_gmt->tm_year - year))
242 return (comp > 0);
243 if ((comp = file_gmt->tm_mon - month))
244 return (comp > 0);
245 if ((comp = file_gmt->tm_mday - day))
246 return (comp > 0);
247 if ((comp = file_gmt->tm_hour - hour))
248 return (comp > 0);
249 if ((comp = file_gmt->tm_min - minute))
250 return (comp > 0);
251 if ((comp = file_gmt->tm_sec - second))
252 return (comp > 0);
253
254 return 0; /* this person must really be into the latest/greatest */
255 }
256
257
258 /*
259 * Name: to_upper
260 *
261 * Description: Turns a string into all upper case (for HTTP_ header forming)
262 * AND changes - into _
263 */
264
265 char *to_upper(char *str)
266 {
267 char *start = str;
268
269 while (*str) {
270 if (*str == '-')
271 *str = '_';
272 else
273 *str = toupper(*str);
274
275 str++;
276 }
277
278 return start;
279 }
280
281 /*
282 * Name: unescape_uri
283 *
284 * Description: Decodes a uri, changing %xx encodings with the actual
285 * character. The query_string should already be gone.
286 *
287 * Return values:
288 * 1: success
289 * 0: illegal string
290 */
291
292 int unescape_uri(char *uri, char ** query_string)
293 {
294 char c, d;
295 char *uri_old;
296
297 uri_old = uri;
298
299 while ((c = *uri_old)) {
300 if (c == '%') {
301 uri_old++;
302 if ((c = *uri_old++) && (d = *uri_old++))
303 *uri++ = HEX_TO_DECIMAL(c, d);
304 else
305 return 0; /* NULL in chars to be decoded */
306 } else if (c == '?') { /* query string */
307 if (query_string)
308 *query_string = ++uri_old;
309 /* stop here */
310 *uri = '\0';
311 return(1);
312 break;
313 } else if (c == '#') { /* fragment */
314 /* legal part of URL, but we do *not* care.
315 * However, we still have to look for the query string */
316 if (query_string) {
317 ++uri_old;
318 while((c = *uri_old)) {
319 if (c == '?') {
320 *query_string = ++uri_old;
321 break;
322 }
323 ++uri_old;
324 }
325 }
326 break;
327 } else {
328 *uri++ = c;
329 uri_old++;
330 }
331 }
332
333 *uri = '\0';
334 return 1;
335 }
336
337 /* rfc822 (1123) time is exactly 29 characters long
338 * "Sun, 06 Nov 1994 08:49:37 GMT"
339 */
340
341 void rfc822_time_buf(char *buf, time_t s)
342 {
343 struct tm *t;
344 char *p;
345 unsigned int a;
346
347 if (!s) {
348 t = gmtime(&current_time);
349 } else
350 t = gmtime(&s);
351
352 p = buf + 28;
353 /* p points to the last char in the buf */
354
355 p -= 3;
356 /* p points to where the ' ' will go */
357 memcpy(p--, " GMT", 4);
358
359 a = t->tm_sec;
360 *p-- = '0' + a % 10;
361 *p-- = '0' + a / 10;
362 *p-- = ':';
363 a = t->tm_min;
364 *p-- = '0' + a % 10;
365 *p-- = '0' + a / 10;
366 *p-- = ':';
367 a = t->tm_hour;
368 *p-- = '0' + a % 10;
369 *p-- = '0' + a / 10;
370 *p-- = ' ';
371 a = 1900 + t->tm_year;
372 while (a) {
373 *p-- = '0' + a % 10;
374 a /= 10;
375 }
376 /* p points to an unused spot to where the space will go */
377 p -= 3;
378 /* p points to where the first char of the month will go */
379 memcpy(p--, month_tab + 4 * (t->tm_mon), 4);
380 *p-- = ' ';
381 a = t->tm_mday;
382 *p-- = '0' + a % 10;
383 *p-- = '0' + a / 10;
384 *p-- = ' ';
385 p -= 3;
386 memcpy(p, day_tab + t->tm_wday * 4, 4);
387 }
388
389 void simple_itoa(unsigned long int i, char buf[22])
390 {
391 /* 21 digits plus null terminator, good for 64-bit or smaller ints
392 * for bigger ints, use a bigger buffer!
393 *
394 * 4294967295 is, incidentally, MAX_UINT (on 32bit systems at this time)
395 * and is 10 bytes long
396 */
397 char *p = &buf[21];
398 int digits = 1; /* include null char */
399
400 *p-- = '\0';
401 do {
402 digits++;
403 *p-- = '0' + i % 10;
404 i /= 10;
405 } while (i > 0);
406
407 p++;
408 if (p!=buf) memmove( buf, p, digits);
409
410 return;
411 }
412
413 /* I don't "do" negative conversions
414 * Therefore, -1 indicates error
415 */
416
417 int boa_atoi(const char *s)
418 {
419 int retval;
420 char reconv[22];
421
422 if (!isdigit(*s))
423 return -1;
424
425 retval = atoi( s);
426 if (retval < 0)
427 return -1;
428
429 simple_itoa(retval, reconv);
430 if (memcmp(s,reconv,strlen(s)) != 0) {
431 return -1;
432 }
433 return retval;
434 }
435
436 long boa_atol(const char *s)
437 {
438 long int retval;
439 char reconv[22];
440
441 if (!isdigit(*s))
442 return -1;
443
444 retval = atol( s);
445 if (retval < 0)
446 return -1;
447
448 simple_itoa(retval, reconv);
449 if (memcmp(s,reconv,strlen(s)) != 0) {
450 return -1;
451 }
452 return retval;
453 }
454
455 #define TEMP_FILE_TEMPLATE "/hydra.temp.XXXXXX"
456 #define TEMP_FILE_TEMPLATE_LEN sizeof(TEMP_FILE_TEMPLATE)-1
457
458 /* returns -1 on error */
459 int create_temporary_file(short want_unlink, char *storage, int size)
460 {
461 char boa_tempfile[MAX_PATH_LENGTH + 1];
462 int fd, total_len;
463
464 total_len = tempdir_len + TEMP_FILE_TEMPLATE_LEN;
465 if (total_len > MAX_PATH_LENGTH) {
466 log_error_time();
467 fprintf(stderr, "Temporary file length (%d) is too long\n", total_len);
468 return -1;
469 }
470
471 memcpy( boa_tempfile, tempdir, tempdir_len);
472 memcpy( &boa_tempfile[tempdir_len], TEMP_FILE_TEMPLATE, TEMP_FILE_TEMPLATE_LEN);
473 boa_tempfile[total_len] = 0; /* null terminated */
474
475 /* open temp file
476 */
477 fd = mkstemp(boa_tempfile);
478 if (fd == -1) {
479 log_error_time();
480 perror("mkstemp");
481 return -1;
482 }
483
484 if (storage != NULL) {
485 if (total_len < size) {
486 memcpy(storage, boa_tempfile, total_len + 1);
487 } else {
488 close(fd);
489 fd = -1;
490 log_error_time();
491 fprintf(stderr, "not enough memory for memcpy in storage\n");
492 want_unlink = 1;
493 }
494 }
495
496 if (want_unlink) {
497 if (unlink(boa_tempfile) == -1) {
498 close(fd);
499 fd = -1;
500 log_error_time();
501 fprintf(stderr, "unlink temp file\n");
502 }
503 }
504
505 return (fd);
506 }
507
508 /*
509 * Name: normalize_path
510 *
511 * Description: Makes sure relative paths are made absolute
512 *
513 */
514
515 #define DIRBUF_SIZE MAX_PATH_LENGTH * 2 + 1
516 char * normalize_path(char *path)
517 {
518 char dirbuf[DIRBUF_SIZE];
519 int len1, len2;
520 char *endpath;
521
522 if (path[0] == '/') {
523 endpath = strdup(path);
524 } else {
525
526 #ifndef HAVE_GETCWD
527 perror("%s: getcwd() not defined. Aborting.", SERVER_NAME);
528 exit(1);
529 #endif
530 if (getcwd(dirbuf, DIRBUF_SIZE) == NULL) {
531 if (errno == ERANGE)
532 perror
533 (SERVER_NAME": getcwd() failed - unable to get working directory. "
534 "Aborting.");
535 else if (errno == EACCES)
536 perror(SERVER_NAME": getcwd() failed - No read access in current "
537 "directory. Aborting.");
538 else
539 perror(SERVER_NAME": getcwd() failed - unknown error. Aborting.");
540 exit(1);
541 }
542
543 /* OK, now the hard part. */
544 len1 = strlen(dirbuf);
545 len2 = strlen(path);
546 if (len1 + len2 > MAX_PATH_LENGTH * 2) {
547 perror(SERVER_NAME": eek. unable to normalize pathname");
548 exit(1);
549 }
550 if (strcmp(path,".") != 0) {
551 memcpy(dirbuf + len1, "/", 1);
552 memcpy(dirbuf + len1 + 1, path, len2 + 1);
553 }
554 /* fprintf(stderr, "%s: normalize gets \"%s\"\n", SERVER_NAME, dirbuf); */
555
556 endpath = strdup(dirbuf);
557 }
558
559 if (endpath == NULL) {
560 fprintf(stderr,
561 "%s: Cannot strdup path. Aborting.\n", SERVER_NAME);
562 exit(1);
563 }
564 return endpath;
565 }
566
567 int real_set_block_fd(int fd)
568 {
569 int flags;
570
571 flags = fcntl(fd, F_GETFL);
572 if (flags == -1)
573 return -1;
574
575 flags &= ~O_NONBLOCK;
576 flags = fcntl(fd, F_SETFL, flags);
577 return flags;
578 }
579
580 int real_set_nonblock_fd(int fd)
581 {
582 int flags;
583
584 flags = fcntl(fd, F_GETFL);
585 if (flags == -1)
586 return -1;
587
588 flags |= O_NONBLOCK;
589 flags = fcntl(fd, F_SETFL, flags);
590 return flags;
591 }
592
593 void create_url( char * buffer, int buffer_size, int secure,
594 const char* hostname, int port, const char* request_uri)
595 {
596 char * proto;
597 char str_port[23];
598 int do_port = 0;
599
600 buffer[0] = 0; /* in case we fail */
601
602 if (secure) {
603 proto = "https";
604 if (port != 443)
605 do_port = 1;
606 } else {
607 proto = "http";
608 if (port!=80)
609 do_port = 1;
610 }
611
612 if (do_port) {
613 str_port[0] = ':';
614 simple_itoa( port, &str_port[1]);
615 }
616
617 if ((strlen(proto) + strlen(str_port) + strlen(hostname) +
618 strlen(request_uri) + 5) > buffer_size) {
619 /* This is more than impossible. The buffer is long enough */
620 log_error_time();
621 fprintf(stderr, "Could not create URL. Buffer was not enough.\n");
622 return;
623 }
624
625 sprintf( buffer, "%s://%s%s%s/", proto, hostname, str_port, request_uri);
626
627 return;
628 }

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26