1 /*
2     Copyright (C) 2000 Masanao Izumo <mo@goice.co.jp>
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18 
19 #include "config.h"
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stdarg.h>
23 #ifndef NO_STRING_H
24 #include <string.h>
25 #else
26 #include <strings.h>
27 #endif
28 
29 #include "libarc/url.h"
30 
31 #ifdef HAVE_SAFE_MALLOC
32 extern void *safe_malloc(size_t count);
33 extern void *safe_realloc(void *old_ptr, size_t new_size);
34 #endif /* HAVE_SAFE_MALLOC */
35 
36 /* #define DEBUG */
37 
38 int url_errno;
39 static struct URL_module *url_mod_list = NULL;
40 char *user_mailaddr = NULL;
41 char *url_user_agent = NULL;
42 int url_newline_code = '\n';
43 char *url_lib_version = URL_LIB_VERSION;
44 int uudecode_unquote_html = 0;
45 
url_add_module(struct URL_module * m)46 void url_add_module(struct URL_module *m)
47 {
48     m->chain = url_mod_list;
49     url_mod_list = m;
50 }
51 
url_add_modules(struct URL_module * m,...)52 void url_add_modules(struct URL_module *m, ...)
53 {
54     va_list ap;
55     struct URL_module *mod;
56 
57     if(m == NULL)
58 	return;
59     url_add_module(m);
60     va_start(ap, m);
61     while((mod = va_arg(ap, struct URL_module *)) != NULL)
62 	url_add_module(mod);
63 }
64 
url_init_nop(void)65 static int url_init_nop(void)
66 {
67     /* Do nothing any more */
68     return 1;
69 }
70 
url_check_type(char * s)71 int url_check_type(char *s)
72 {
73     struct URL_module *m;
74 
75     for(m = url_mod_list; m; m = m->chain)
76 	if(m->type != URL_none_t && m->name_check && m->name_check(s))
77 	    return m->type;
78     return -1;
79 }
80 
url_open(char * s)81 URL url_open(char *s)
82 {
83     struct URL_module *m;
84 
85     for(m = url_mod_list; m; m = m->chain)
86     {
87 #ifdef DEBUG
88 	printf("Check URL type=%d\n", m->type);
89 #endif /* DEBUG */
90 	if(m->type != URL_none_t && m->name_check && m->name_check(s))
91 	{
92 #ifdef DEBUG
93 	    printf("open url (type=%d, name=%s)\n", m->type, s);
94 #endif /* DEBUG */
95 	    if(m->url_init != url_init_nop)
96 	    {
97 		if(m->url_init && m->url_init() < 0)
98 		    return NULL;
99 		m->url_init = url_init_nop;
100 	    }
101 
102 	    url_errno = URLERR_NONE;
103 	    errno = 0;
104 	    return m->url_open(s);
105 	}
106     }
107 
108     url_errno = URLERR_NOURL;
109     errno = ENOENT;
110     return NULL;
111 }
112 
url_read(URL url,void * buff,long n)113 long url_read(URL url, void *buff, long n)
114 {
115     if(n <= 0)
116 	return 0;
117     url_errno = URLERR_NONE;
118     errno = 0;
119     if(url->nread >= url->readlimit) {
120         url->eof = 1;
121 	return 0;
122     }
123     if(url->nread + n > url->readlimit)
124 	n = (long)(url->readlimit - url->nread);
125     n = url->url_read(url, buff, n);
126     if(n > 0)
127 	url->nread += n;
128     return n;
129 }
130 
url_safe_read(URL url,void * buff,long n)131 long url_safe_read(URL url, void *buff, long n)
132 {
133     long i;
134     if(n <= 0)
135 	return 0;
136 
137     do /* Ignore signal intruption */
138     {
139 	errno = 0;
140 	i = url_read(url, buff, n);
141     } while(i == -1 && errno == EINTR);
142 #if 0
143     /* Already done in url_read!! */
144     if(i > 0)
145 	url->nread += i;
146 #endif
147     return i;
148 }
149 
url_nread(URL url,void * buff,long n)150 long url_nread(URL url, void *buff, long n)
151 {
152     long insize = 0;
153     char *s = (char *)buff;
154 
155     do
156     {
157 	long i;
158 	i = url_safe_read(url, s + insize, n - insize);
159 	if(i <= 0)
160 	{
161 	    if(insize == 0)
162 		return i;
163 	    break;
164 	}
165 	insize += i;
166     } while(insize < n);
167 
168     return insize;
169 }
170 
url_gets(URL url,char * buff,int n)171 char *url_gets(URL url, char *buff, int n)
172 {
173     if(url->nread >= url->readlimit)
174 	return NULL;
175 
176     if(url->url_gets == NULL)
177     {
178 	int maxlen, i, c;
179 	int newline = url_newline_code;
180 
181 	maxlen = n - 1;
182 	if(maxlen == 0)
183 	    *buff = '\0';
184 	if(maxlen <= 0)
185 	    return buff;
186 	i = 0;
187 
188 	do
189 	{
190 	    if((c = url_getc(url)) == EOF)
191 		break;
192 	    buff[i++] = c;
193 	} while(c != newline && i < maxlen);
194 
195 	if(i == 0)
196 	    return NULL; /* EOF */
197 	buff[i] = '\0';
198 	return buff;
199     }
200 
201     url_errno = URLERR_NONE;
202     errno = 0;
203 
204     if(url->nread + n > url->readlimit)
205 	n = (long)(url->readlimit - url->nread) + 1;
206 
207     buff = url->url_gets(url, buff, n);
208     if(buff != NULL)
209 	url->nread += strlen(buff);
210     return buff;
211 }
212 
url_readline(URL url,char * buff,int n)213 int url_readline(URL url, char *buff, int n)
214 {
215     int maxlen, i, c;
216 
217     maxlen = n - 1;
218     if(maxlen == 0)
219 	*buff = '\0';
220     if(maxlen <= 0)
221 	return 0;
222     do
223     {
224 	i = 0;
225 	do
226 	{
227 	    if((c = url_getc(url)) == EOF)
228 		break;
229 	    buff[i++] = c;
230 	} while(c != '\r' && c != '\n' && i < maxlen);
231 	if(i == 0)
232 	    return 0; /* EOF */
233     } while(i == 1 && (c == '\r' || c == '\n'));
234 
235     if(c == '\r' || c == '\n')
236 	i--;
237     buff[i] = '\0';
238     return i;
239 }
240 
url_fgetc(URL url)241 int url_fgetc(URL url)
242 {
243     if(url->nread >= url->readlimit)
244 	return EOF;
245 
246     url->nread++;
247     if(url->url_fgetc == NULL)
248     {
249 	unsigned char c;
250 	if(url_read(url, &c, 1) <= 0)
251 	    return EOF;
252 	return (int)c;
253     }
254     url_errno = URLERR_NONE;
255     errno = 0;
256     return url->url_fgetc(url);
257 }
258 
url_seek(URL url,long offset,int whence)259 long url_seek(URL url, long offset, int whence)
260 {
261     long pos, savelimit;
262 
263     if(url->url_seek == NULL)
264     {
265 	if(whence == SEEK_CUR && offset >= 0)
266 	{
267 	    pos = url_tell(url);
268 	    if(offset == 0)
269 		return pos;
270 	    savelimit = (long)url->readlimit;
271 	    url->readlimit = URL_MAX_READLIMIT;
272 	    url_skip(url, offset);
273 	    url->readlimit = savelimit;
274 	    url->nread = 0;
275 	    return pos;
276 	}
277 
278 	if(whence == SEEK_SET)
279 	{
280 	    pos = url_tell(url);
281 	    if(pos != -1 && pos <= offset)
282 	    {
283 		if(pos == offset)
284 		    return pos;
285 		savelimit = (long)url->readlimit;
286 		url->readlimit = URL_MAX_READLIMIT;
287 		url_skip(url, offset - pos);
288 		url->readlimit = savelimit;
289 		url->nread = 0;
290 		return pos;
291 	    }
292 	}
293 
294 	url_errno = errno = EPERM;
295 	return -1;
296     }
297     url_errno = URLERR_NONE;
298     errno = 0;
299     url->nread = 0;
300     return url->url_seek(url, offset, whence);
301 }
302 
url_tell(URL url)303 long url_tell(URL url)
304 {
305     url_errno = URLERR_NONE;
306     errno = 0;
307     if(url->url_tell == NULL)
308 	return (long)url->nread;
309     return url->url_tell(url);
310 }
311 
url_skip(URL url,long n)312 void url_skip(URL url, long n)
313 {
314     char tmp[BUFSIZ];
315 
316     if(url->url_seek != NULL)
317     {
318 	long savenread;
319 
320 	savenread = (long)url->nread;
321 	if(savenread >= url->readlimit)
322 	    return;
323 	if(savenread + n > url->readlimit)
324 	    n = (long)(url->readlimit - savenread);
325 	if(url->url_seek(url, n, SEEK_CUR) != -1)
326 	{
327 	    url->nread = savenread + n;
328 	    return;
329 	}
330 	url->nread = savenread;
331     }
332 
333     while(n > 0)
334     {
335 	long c;
336 
337 	c = n;
338 	if(c > sizeof(tmp))
339 	    c = sizeof(tmp);
340 	c = url_read(url, tmp, c);
341 	if(c <= 0)
342 	    break;
343 	n -= c;
344     }
345 }
346 
url_rewind(URL url)347 void url_rewind(URL url)
348 {
349     if(url->url_seek != NULL)
350 	url->url_seek(url, 0, SEEK_SET);
351     url->nread = 0;
352 }
353 
url_set_readlimit(URL url,long readlimit)354 void url_set_readlimit(URL url, long readlimit)
355 {
356     if(readlimit < 0)
357 	url->readlimit = URL_MAX_READLIMIT;
358     else
359 	url->readlimit = (unsigned long)readlimit;
360     url->nread = 0;
361 }
362 
alloc_url(int size)363 URL alloc_url(int size)
364 {
365     URL url;
366 #ifdef HAVE_SAFE_MALLOC
367     url = (URL)safe_malloc(size);
368     memset(url, 0, size);
369 #else
370     url = (URL)malloc(size);
371     if(url != NULL)
372 	memset(url, 0, size);
373     else
374 	url_errno = errno;
375 #endif /* HAVE_SAFE_MALLOC */
376 
377     url->nread = 0;
378     url->readlimit = URL_MAX_READLIMIT;
379     url->eof = 0;
380     return url;
381 }
382 
url_close(URL url)383 void url_close(URL url)
384 {
385     int save_errno = errno;
386 
387     if(url == NULL)
388     {
389 	fprintf(stderr, "URL stream structure is NULL?\n");
390 #ifdef ABORT_AT_FATAL
391 	abort();
392 #endif /* ABORT_AT_FATAL */
393     }
394     else if(url->url_close == NULL)
395     {
396 	fprintf(stderr, "URL Error: Already URL is closed (type=%d)\n",
397 		url->type);
398 #ifdef ABORT_AT_FATAL
399 	abort();
400 #endif /* ABORT_AT_FATAL */
401     }
402     else
403     {
404 	url->url_close(url);
405 #if 0
406 	url->url_close = NULL;
407 #endif /* unix */
408     }
409     errno = save_errno;
410 }
411 
412 #if defined(TILD_SCHEME_ENABLE)
413 #include <pwd.h>
url_expand_home_dir(char * fname)414 char *url_expand_home_dir(char *fname)
415 {
416     static char path[BUFSIZ];
417     char *dir;
418     int dirlen;
419 
420     if(fname[0] != '~')
421 	return fname;
422 
423     if(IS_PATH_SEP(fname[1])) /* ~/... */
424     {
425 	fname++;
426 	if((dir = getenv("HOME")) == NULL)
427 	    if((dir = getenv("home")) == NULL)
428 		return fname;
429     }
430     else /* ~user/... */
431     {
432 	struct passwd *pw;
433 	int i;
434 
435 	fname++;
436 	for(i = 0; i < sizeof(path) - 1 && fname[i] && !IS_PATH_SEP(fname[i]); i++)
437 	    path[i] = fname[i];
438 	path[i] = '\0';
439 	if((pw = getpwnam(path)) == NULL)
440 	    return fname - 1;
441 	fname += i;
442 	dir = pw->pw_dir;
443     }
444     dirlen = strlen(dir);
445     strncpy(path, dir, sizeof(path) - 1);
446     if(sizeof(path) > dirlen)
447 	strncat(path, fname, sizeof(path) - dirlen - 1);
448     path[sizeof(path) - 1] = '\0';
449     return path;
450 }
url_unexpand_home_dir(char * fname)451 char *url_unexpand_home_dir(char *fname)
452 {
453     static char path[BUFSIZ];
454     char *dir, *p;
455     int dirlen;
456 
457     if(!IS_PATH_SEP(fname[0]))
458 	return fname;
459 
460     if((dir = getenv("HOME")) == NULL)
461 	if((dir = getenv("home")) == NULL)
462 	    return fname;
463     dirlen = strlen(dir);
464     if(dirlen == 0 || dirlen >= sizeof(path) - 2)
465 	return fname;
466     memcpy(path, dir, dirlen);
467     if(!IS_PATH_SEP(path[dirlen - 1]))
468 	path[dirlen++] = PATH_SEP;
469 
470 #ifndef __W32__
471     if(strncmp(path, fname, dirlen) != 0)
472 #else
473     if(strncasecmp(path, fname, dirlen) != 0)
474 #endif /* __W32__ */
475 	return fname;
476 
477     path[0] = '~';
478     path[1] = '/';
479     p = fname + dirlen;
480     if(strlen(p) >= sizeof(path) - 3)
481 	return fname;
482     path[2] = '\0';
483     strcat(path, p);
484     return path;
485 }
486 #else
url_expand_home_dir(char * fname)487 char *url_expand_home_dir(char *fname)
488 {
489     return fname;
490 }
url_unexpand_home_dir(char * fname)491 char *url_unexpand_home_dir(char *fname)
492 {
493     return fname;
494 }
495 #endif
496 
497 static char *url_strerror_txt[] =
498 {
499     "",				/* URLERR_NONE */
500     "Unknown URL",		/* URLERR_NOURL */
501     "Operation not permitted",	/* URLERR_OPERM */
502     "Can't open a URL",		/* URLERR_CANTOPEN */
503     "Invalid URL form",		/* URLERR_IURLF */
504     "URL too long",		/* URLERR_URLTOOLONG */
505     "No mail address",		/* URLERR_NOMAILADDR */
506     ""
507 };
508 
url_strerror(int no)509 char *url_strerror(int no)
510 {
511     if(no <= URLERR_NONE)
512 	return strerror(no);
513     if(no >= URLERR_MAXNO)
514 	return "Internal error";
515     return url_strerror_txt[no - URLERR_NONE];
516 }
517 
url_dump(URL url,long nbytes,long * read_size)518 void *url_dump(URL url, long nbytes, long *read_size)
519 {
520     long allocated, offset, read_len;
521     char *buff;
522 
523     if(read_size != NULL)
524       *read_size = 0;
525     if(nbytes == 0)
526 	return NULL;
527     if(nbytes >= 0)
528     {
529 #ifdef HAVE_SAFE_MALLOC
530 	buff = (char *)safe_malloc(nbytes);
531 #else
532 	if((buff = (char *)malloc(nbytes)) == NULL)
533 	    return NULL;
534 #endif
535 	if(nbytes == 0)
536 	    return buff;
537 	read_len = url_nread(url, buff, nbytes);
538 	if(read_size != NULL)
539 	  *read_size = read_len;
540 	if(read_len <= 0)
541 	{
542 	    free(buff);
543 	    return NULL;
544 	}
545 	return buff;
546     }
547 
548     allocated = 1024;
549 #ifdef HAVE_SAFE_MALLOC
550     buff = (char *)safe_malloc(allocated);
551 #else
552     if((buff = (char *)malloc(nbytes)) == NULL)
553 	return NULL;
554 #endif
555     offset = 0;
556     read_len = allocated;
557     while((nbytes = url_read(url, buff + offset, read_len)) > 0)
558     {
559 	offset += nbytes;
560 	read_len -= nbytes;
561 	if(offset == allocated)
562 	{
563 	    read_len = allocated;
564 	    allocated *= 2;
565 #ifdef HAVE_SAFE_MALLOC
566 	    buff = (char *)safe_realloc(buff, allocated);
567 #else
568 	    if((buff = (char *)realloc(buff, allocated)) == NULL)
569 		return NULL;
570 #endif
571 	}
572     }
573     if(offset == 0)
574     {
575 	free(buff);
576 	return NULL;
577     }
578     if(read_size != NULL)
579       *read_size = offset;
580     return buff;
581 }
582