:pserver:anonymous@cvs.middle-man.sourceforge.net:/cvsroot/middle-man middleman
[middleman.git] / src / misc.c
1 /*
2     MiddleMan filtering proxy server
3     Copyright (C) 2002  Jason McLaughlin
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
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <stdarg.h>
27 #include <sys/types.h>
28 #include "proto.h"
29
30 #ifdef HAVE_ZLIB
31 #include <zlib.h>
32 #endif /* HAVE_ZLIB */
33
34
35 /*
36 strncpy which always NULL terminates
37 */
38 char *s_strncpy(char *d, char *s, size_t len)
39 {
40         char *dest = d;
41
42         for (; len && (*dest = *s); s++, dest++, len--);
43         *dest = '\0';
44
45         return d;
46 }
47
48 /*
49 strncat which always NULL terminates
50 */
51 char *s_strncat(char *d, char *s, size_t len)
52 {
53         char *dest = d;
54
55         for (; len && *dest; dest++, len--);
56         for (; len && (*dest = *s); s++, dest++, len--);
57
58         *dest = '\0';
59
60         return d;
61 }
62
63 /*
64 case-insensitive strstr
65 */
66 char *s_strcasestr(char *haystack, char *needle) {
67         char *n, *tmp;
68
69         for (; *haystack; haystack++) {
70                 for (tmp = haystack, n = needle; *tmp && *n && tolower(*tmp) == tolower(*n); tmp++, n++);
71                 if (!*n) return haystack;
72         }
73
74         return NULL;
75 }
76
77 /*
78 free memory used by FILEBUF-type structure
79 */
80 void filebuf_free(FILEBUF * filebuf)
81 {
82         if (!filebuf)
83                 return;
84
85         if (filebuf->data != NULL)
86                 xfree(filebuf->data);
87         if (filebuf->type != NULL)
88                 xfree(filebuf->type);
89
90         xfree(filebuf);
91 }
92
93 /*
94 create empty filebuf
95 */
96 FILEBUF *filebuf_new()
97 {
98         FILEBUF *filebuf;
99
100         filebuf = xmalloc(sizeof(FILEBUF));
101
102         filebuf->data = NULL;
103         filebuf->type = NULL;
104         filebuf->size = filebuf->realsize = 0;
105         filebuf->code = -1;
106
107         return filebuf;
108 }
109
110 /*
111 reduce filebuf size to actual size of contents
112 */
113 void filebuf_shorten(FILEBUF * filebuf)
114 {
115         if (filebuf->realsize > filebuf->size) {
116                 filebuf->realsize = filebuf->size;
117                 filebuf->data = xrealloc(filebuf->data, filebuf->size);
118         }
119 }
120
121 /*
122 append data to a file buffer
123 */
124 void filebuf_add(FILEBUF * filebuf, const char *data, int len)
125 {
126         int x;
127
128         x = filebuf->size;
129
130         filebuf_resize(filebuf, filebuf->size + len);
131
132         memcpy(&filebuf->data[x], data, len);
133 }
134
135
136 /*
137 add formatted data to filebuf
138 */
139 void filebuf_addf(FILEBUF * filebuf, const char *fmt, ...)
140 {
141         int ret;
142         char buf[8096];
143         va_list valist;
144
145         va_start(valist, fmt);
146         ret = vsnprintf(buf, sizeof(buf), fmt, valist);
147         va_end(valist);
148
149         filebuf_add(filebuf, buf, ret);
150 }
151
152 /*
153 resize a filebuf (without adding anything)
154 */
155 void filebuf_resize(FILEBUF * filebuf, int newsize)
156 {
157         if (filebuf->realsize >= newsize && filebuf->realsize <= newsize + FILEBUF_ALIGNMENT && filebuf->data != NULL)
158                 filebuf->size = newsize;
159         else if (newsize != 0) {
160                 filebuf->realsize = ALIGN2(newsize, FILEBUF_ALIGNMENT);
161                 filebuf->data = xrealloc(filebuf->data, filebuf->realsize);
162                 filebuf->size = newsize;
163         }
164 }
165
166 /*
167 return descriptive error for a code
168 */
169 char *code_to_error(int code)
170 {
171         switch (code) {
172         case 200:
173                 return "OK";
174         case 301:
175                 return "Document Moved";
176         case 302:
177                 return "Found";
178         case 304:
179                 return "Not Modified";
180         case 400:
181                 return "Bad Request";
182         case 403:
183                 return "Forbidden";
184         case 404:
185                 return "Not Found";
186         case 407:
187                 return "Authenticaton required";
188         case 500:
189                 return "Internal Error";
190         case 501:
191                 return "Not Implemented";
192         case 502:
193                 return "Service Temporarily Unavailable";
194         case 503:
195                 return "Connection Failed";
196         default:
197                 return "Unknown";
198         }
199
200         return NULL;
201 }
202
203 /*
204 strdup which uses xmalloc
205 */
206 char *xstrdup(char *s)
207 {
208         return strcpy(xmalloc(strlen(s) + 1), s);
209 }
210
211 /*
212 strndup which uses xmalloc
213 */
214 char *xstrndup(char *s, int len)
215 {
216         return s_strncpy(xmalloc(((strlen(s) < len) ? strlen(s) : len) + 1), s, len);
217 }
218
219 /*
220 convert an error flag into a template name
221 */
222 char *error_to_template(int error)
223 {
224         switch (error) {
225         case ERROR_CONNECT:
226                 return "noconnect";
227         case ERROR_DNS:
228                 return "nodns";
229         case ERROR_AUTH:
230                 return "badauth";
231         case ERROR_UNKNOWN:
232         default:
233                 return "unknown";
234         }
235
236         return NULL;
237 }
238
239 /*
240 free HOSTENT structure
241 */
242 void hostent_free(HOSTENT * hostent)
243 {
244         xfree(hostent->addr);
245         xfree(hostent);
246 }
247
248 /*
249 return TRUE if a string appears to be an IP address
250 */
251 int isip(char *string)
252 {
253         int dots = 0;
254
255         for (; *string; string++) {
256                 if (*string == '.')
257                         dots++;
258                 else if (!isdigit((int) *string))
259                         return FALSE;
260         }
261
262         return (dots == 3) ? TRUE : FALSE;
263 }
264
265 /*
266 convert string to uppercase
267 */
268 void string_tolower(char *string)
269 {
270         for (; *string; string++)
271                 *string = tolower(*string);
272 }
273
274 /*
275 replace characters that have a special meaning in html with their respective entity
276 */
277 char *string_to_html(char *s, int newlines)
278 {
279         char *ret;
280         FILEBUF *filebuf;
281
282         filebuf = filebuf_new();
283
284         for (; *s; s++) {
285                 if (*s == '"')
286                         filebuf_add(filebuf, "&quot;", 6);
287                 else if (*s == '<')
288                         filebuf_add(filebuf, "&lt;", 4);
289                 else if (*s == '>')
290                         filebuf_add(filebuf, "&gt;", 4);
291                 else if (*s == '&')
292                         filebuf_add(filebuf, "&amp;", 5);
293                 else if (newlines == TRUE && *s == '\n')
294                         filebuf_add(filebuf, "<br>", 4);
295                 else
296                         filebuf_add(filebuf, s, 1);
297         }
298
299         filebuf_add(filebuf, "", 1);
300         filebuf_shorten(filebuf);
301
302         ret = filebuf->data;
303
304         filebuf->data = NULL;
305         filebuf_free(filebuf);
306
307         return ret;
308 }
309
310 /*
311 decode all hex characters in a url
312 */
313 char *url_decode(char *url, int len)
314 {
315         char *ret, *u = url, tmp[3];
316         FILEBUF *filebuf;
317
318         tmp[2] = '\0';
319
320         filebuf = filebuf_new();
321
322         while (*u && u - url < len) {
323                 if (*u == '%' && u[1] != '\0') {
324                         tmp[0] = u[1];
325                         tmp[1] = u[2];
326
327                         filebuf_addf(filebuf, "%c", strtol(tmp, NULL, 16));
328
329                         u += 3;
330                 } else if (*u == '+') {
331                         filebuf_add(filebuf, " ", 1);
332
333                         u++;
334                 } else {
335                         filebuf_add(filebuf, u, 1);
336
337                         u++;
338                 }
339         }
340
341         filebuf_add(filebuf, "", 1);
342         filebuf_shorten(filebuf);
343
344         ret = filebuf->data;
345
346         filebuf->data = NULL;
347         filebuf_free(filebuf);
348
349         return ret;
350 }
351
352 /*
353 encode special characters in a url
354 */
355 char *url_encode(char *url, int len)
356 {
357         char *ret, *u = url;
358         FILEBUF *filebuf;
359
360         filebuf = filebuf_new();
361
362         for (; *u && u - url < len; u++) {
363                 if (*u == ' ')
364                         filebuf_add(filebuf, "+", 1);
365                 else if ((*u >= 0 && *u <= 42) || (*u >= 58 && *u <= 64) || (*u >= 91 && *u <= 96) || *u >= 123)
366                         filebuf_addf(filebuf, "%%%.2x", *u);
367                 else
368                         filebuf_add(filebuf, u, 1);
369         }
370
371         filebuf_add(filebuf, "", 1);
372         filebuf_shorten(filebuf);
373
374         ret = filebuf->data;
375
376         filebuf->data = NULL;
377         filebuf_free(filebuf);
378
379         return ret;
380 }
381
382 /*
383 write a filebuf to a file
384 */
385 int filebuf_save(FILEBUF * filebuf, char *filename)
386 {
387         int fd, ret;
388
389         fd = open(filename, O_CREAT | O_WRONLY);
390         if (fd == -1)
391                 return -1;
392
393         ret = write(fd, filebuf->data, filebuf->size);
394
395         close(fd);
396
397         return ret;
398 }
399
400 /*
401 escape all <, >, and \'s in a string destined for the xml file
402 */
403 char *string_to_xml(char *s)
404 {
405         char *ret;
406         FILEBUF *filebuf;
407
408         filebuf = filebuf_new();
409
410         for (; *s; s++) {
411                 if (*s == '\\')
412                         filebuf_add(filebuf, "\\\\", 2);
413                 else if (*s == '<')
414                         filebuf_add(filebuf, "\\<", 2);
415                 else if (*s == '>')
416                         filebuf_add(filebuf, "\\>", 2);
417                 else
418                         filebuf_add(filebuf, s, 1);
419         }
420
421         filebuf_add(filebuf, "", 1);
422         filebuf_shorten(filebuf);
423
424         ret = filebuf->data;
425
426         filebuf->data = NULL;
427         filebuf_free(filebuf);
428
429         return ret;
430 }
431
432 /*
433 tokenize a string and return a NULL-terminated char **
434 */
435 char **string_break(char *s, char d)
436 {
437         int count = 0;
438         char **ret = NULL, *start = s;
439
440         for (; *s; s++) {
441                 if (*s == d) {
442                         ret = xrealloc(ret, sizeof(char *) * (count + 2));
443                         ret[count] = xstrndup(start, s - start);
444
445                         start = s + 1;
446
447                         count++;
448                 }
449         }
450
451         if (*start) {
452                 ret = xrealloc(ret, sizeof(char *) * (count + 2));
453                 ret[count] = xstrndup(start, s - start);
454
455                 count++;
456         }
457
458         if (count > 0)
459                 ret[count] = NULL;
460
461         return ret;
462 }
463
464 /*
465 read a file into a filebuf
466 */
467 int filebuf_read(FILEBUF * filebuf, char *filename)
468 {
469         int x, ret = 0, fd;
470         char buf[BLOCKSIZE];
471
472         fd = open(filename, O_RDONLY);
473         if (fd == -1)
474                 return -1;
475
476         while ((x = read(fd, buf, BLOCKSIZE)) > 0) {
477                 filebuf_add(filebuf, buf, x);
478
479                 ret += x;
480         }
481
482         close(fd);
483
484         return ret;
485 }
486
487 /*
488 replace area of filebuf at specified offset and length with something else
489 */
490 void filebuf_replace(FILEBUF * filebuf, int so, int eo, char *replace)
491 {
492         int len1 = eo - so;
493         int len2 = strlen(replace);
494
495         if (len2 > len1)
496                 filebuf_resize(filebuf, filebuf->size + (len2 - len1));
497
498         if (len1 != len2)
499                 memmove(&filebuf->data[eo + (len2 - len1)], &filebuf->data[eo], filebuf->size - eo);
500
501         if (len2 < len1)
502                 filebuf_resize(filebuf, filebuf->size + (len2 - len1));
503
504         memcpy(&filebuf->data[so], replace, len2);
505 }
506
507 /*
508 gzip compress contents of a FILEBUF
509 note: zlib doesn't provide any api's for doing in-memory compression/decompression in gzip format, 
510 so a temp file has to be used. 
511 */
512 int filebuf_gzip(FILEBUF *filebuf) {
513 #ifdef HAVE_ZLIB
514         int fd, x = 0, ret;
515         char tmpfile[64];
516         gzFile *gzf;
517
518         s_strncpy(tmpfile, TEMPFILE, sizeof(tmpfile));
519
520         fd = mkstemp(tmpfile);
521         if (fd == -1) return FALSE;
522
523         gzf = gzdopen(fd, "w");
524         if (gzf == NULL) {
525                 close(fd);
526                 unlink(tmpfile);
527
528                 return FALSE;
529         }
530
531         while (x < filebuf->size) {
532                 ret = gzwrite(gzf, &filebuf->data[x], filebuf->size - x);
533                 if (ret == -1) {
534                         gzclose(gzf);
535                         unlink(tmpfile);
536
537                         return FALSE;
538                 } else if (ret == 0)
539                         break;
540
541                 x += ret;
542         }
543
544         gzclose(gzf);
545
546         FREE_AND_NULL(filebuf->data);
547         filebuf->size = 0;
548
549         filebuf_read(filebuf, tmpfile);
550
551         unlink(tmpfile);
552
553         return TRUE;
554 #endif /* HAVE_ZLIB */
555
556         return FALSE;
557 }               
558
559 /*
560 ungzip the contents of a filebuf using zlib and a temporary file
561 */
562 int filebuf_ungzip(FILEBUF *filebuf) {
563 #ifdef HAVE_ZLIB
564         int fd, x = 0, ret;
565         char tmpfile[64], buf[BLOCKSIZE];
566         FILEBUF *new_filebuf;
567         gzFile *gzf;
568
569         s_strncpy(tmpfile, TEMPFILE, sizeof(tmpfile));
570
571         fd = mkstemp(tmpfile);
572         if (fd == -1) return FALSE;
573
574         while (x < filebuf->size) {
575                 ret = write(fd, &filebuf->data[x], filebuf->size - x);
576                 if (ret == -1) {
577                         close(fd);
578                         unlink(tmpfile);
579
580                         return FALSE;
581                 }
582
583                 x += ret;
584         }
585
586         close(fd);
587
588         gzf = gzopen(tmpfile, "r");
589         if (gzf == NULL) {
590                 unlink(tmpfile);
591
592                 return FALSE;
593         }
594
595         new_filebuf = filebuf_new();
596
597         while (1) {
598                 ret = gzread(gzf, buf, sizeof(buf));
599
600                 if (ret == -1) {
601                         gzclose(gzf);
602                         unlink(tmpfile);
603
604                         filebuf_free(new_filebuf);
605
606                         return FALSE;
607                 } else if (ret == 0) break;
608
609                 filebuf_add(new_filebuf, buf, ret);
610         }
611
612         gzclose(gzf);
613         unlink(tmpfile);
614
615         xfree(filebuf->data);
616         filebuf->data = new_filebuf->data;
617         filebuf->size = new_filebuf->size;
618
619         new_filebuf->data = NULL;
620         filebuf_free(new_filebuf);
621
622         return TRUE;
623 #endif /* HAVE_ZLIB */
624
625         return FALSE;
626 }