1 /*
2     TiMidity++ -- MIDI to WAVE converter and player
3     Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
4     Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif /* HAVE_CONFIG_H */
24 #include <stdio.h>
25 #include <stdlib.h>
26 #ifndef NO_STRING_H
27 #include <string.h>
28 #else
29 #include <strings.h>
30 #endif
31 #include "timidity.h"
32 #include "mblock.h"
33 #include "zip.h"
34 #include "arc.h"
35 
36 extern char *safe_strdup(const char *);
37 
38 #ifndef MAX_CHECK_LINES
39 #define MAX_CHECK_LINES 1024
40 #endif /* MAX_CHECK_LINES */
41 
42 struct StringStackElem
43 {
44     struct StringStackElem *next;
45     char str[1];		/* variable length */
46 };
47 
48 struct StringStack
49 {
50     struct StringStackElem *elem;
51     MBlockList pool;
52 };
53 
54 static void init_string_stack(struct StringStack *stk);
55 static void push_string_stack(struct StringStack *stk, char *str, int len);
56 static char *top_string_stack(struct StringStack *stk);
57 static void pop_string_stack(struct StringStack *stk);
58 static void delete_string_stack(struct StringStack *stk);
59 
60 struct MIMEHeaderStream
61 {
62     URL url;
63     char *field;
64     char *value;
65     char *line;
66     int bufflen;
67     int eof;
68     MBlockList pool;
69 };
70 
71 static void init_mime_stream(struct MIMEHeaderStream *hdr, URL url);
72 static int  next_mime_header(struct MIMEHeaderStream *hdr);
73 static void end_mime_stream(struct MIMEHeaderStream *hdr);
74 static int seek_next_boundary(URL url, char *boundary, long *endpoint);
75 static int whole_read_line(URL url, char *buff, int bufsiz);
76 static void *arc_mime_decode(void *data, long size,
77 			     int comptype, long *newsize);
78 
next_mime_entry(void)79 ArchiveEntryNode *next_mime_entry(void)
80 {
81     ArchiveEntryNode *head, *tail;
82     URL url;
83     int part;
84     struct StringStack boundary;
85     struct MIMEHeaderStream hdr;
86     int c;
87 
88     if(arc_handler.counter != 0)
89 	return NULL;
90 
91     head = tail = NULL;
92     url = arc_handler.url; /* url_seek must be safety */
93 
94     init_string_stack(&boundary);
95     url_rewind(url);
96     c = url_getc(url);
97     if(c != '\0')
98 	url_rewind(url);
99     else
100 	url_skip(url, 128-1);	/* skip macbin header */
101 
102     part = 1;
103     for(;;)
104     {
105 	char *new_boundary, *encoding, *name, *filename;
106 	char *p;
107 	MBlockList pool;
108 	long data_start, data_end, savepoint;
109 	int last_check, comptype, arctype;
110 	void *part_data;
111 	long part_data_size;
112 
113 	new_boundary = encoding = name = filename = NULL;
114 	init_mblock(&pool);
115 	init_mime_stream(&hdr, url);
116 	while(next_mime_header(&hdr))
117 	{
118 	    if(strncmp(hdr.field, "Content-", 8) != 0)
119 		continue;
120 	    if(strcmp(hdr.field + 8, "Type") == 0)
121 	    {
122 		if((p = strchr(hdr.value, ';')) == NULL)
123 		    continue;
124 		*p++ = '\0';
125 		while(*p == ' ')
126 		    p++;
127 		if(strncasecmp(hdr.value, "multipart/mixed", 15) == 0)
128 		{
129 		    /* Content-Type: multipart/mixed; boundary="XXXX" */
130 		    if(strncasecmp(p, "boundary=", 9) == 0)
131 		    {
132 			p += 9;
133 			if(*p == '"')
134 			{
135 			    p++;
136 			    new_boundary = p;
137 			    if((p = strchr(p, '"')) == NULL)
138 				continue;
139 			}
140 			else
141 			{
142 			    new_boundary = p;
143 			    while(*p > '"' && *p < 0x7f)
144 				p++;
145 			}
146 
147 			*p = '\0';
148 			new_boundary = strdup_mblock(&pool, new_boundary);
149 		    }
150 		}
151 		else if(strcasecmp(hdr.value, "multipart/mixed") == 0)
152 		{
153 		    /* Content-Type: XXXX/YYYY; name="ZZZZ" */
154 		    if(strncasecmp(p, "name=\"", 6) == 0)
155 		    {
156 			p += 6;
157 			name = p;
158 			if((p = strchr(p, '"')) == NULL)
159 			    continue;
160 			*p = '\0';
161 			name = strdup_mblock(&pool, name);
162 		    }
163 		}
164 	    }
165 	    else if(strcmp(hdr.field + 8, "Disposition") == 0)
166 	    {
167 		if((p = strchr(hdr.value, ';')) == NULL)
168 		    continue;
169 		*p++ = '\0';
170 		while(*p == ' ')
171 		    p++;
172 		if((p = strstr(p, "filename=\"")) == NULL)
173 		    continue;
174 		p += 10;
175 		filename = p;
176 		if((p = strchr(p, '"')) == NULL)
177 		    continue;
178 		*p = '\0';
179 		filename = strdup_mblock(&pool, filename);
180 	    }
181 	    else if(strcmp(hdr.field + 8, "Transfer-Encoding") == 0)
182 	    {
183 		/* Content-Transfer-Encoding: X */
184 		/* X := X-uuencode, base64, quoted-printable, ... */
185 		encoding = strdup_mblock(&pool, hdr.value);
186 	    }
187 	}
188 
189 	if(hdr.eof)
190 	{
191 	    reuse_mblock(&pool);
192 	    end_mime_stream(&hdr);
193 	    delete_string_stack(&boundary);
194 	    return head;
195 	}
196 
197 	if(filename == NULL)
198 	    filename = name;
199 
200 	if(new_boundary)
201 	    push_string_stack(&boundary, new_boundary, strlen(new_boundary));
202 
203 	data_start = url_tell(url);
204 	last_check = seek_next_boundary(url, top_string_stack(&boundary),
205 					&data_end);
206 
207 	savepoint = url_tell(url);
208 
209 	/* find data type */
210 	comptype = -1;
211 	if(encoding != NULL)
212 	{
213 	    if(strcmp("base64", encoding) == 0)
214 		comptype = ARCHIVEC_B64;
215 	    else if(strcmp("quoted-printable", encoding) == 0)
216 		comptype = ARCHIVEC_QS;
217 	    else if(strcmp("X-uuencode", encoding) == 0)
218 	    {
219 		char buff[BUFSIZ];
220 		int i;
221 
222 		comptype = ARCHIVEC_UU;
223 		url_seek(url, data_start, SEEK_SET);
224 		url_set_readlimit(url, data_end - data_start);
225 
226 		/* find '^begin \d\d\d \S+' */
227 		for(i = 0; i < MAX_CHECK_LINES; i++)
228 		{
229 		    if(whole_read_line(url, buff, sizeof(buff)) == -1)
230 			break; /* ?? */
231 		    if(strncmp(buff, "begin ", 6) == 0)
232 		    {
233 			data_start = url_tell(url);
234 			p = strchr(buff + 6, ' ');
235 			if(p != NULL)
236 			    filename = strdup_mblock(&pool, p + 1);
237 			break;
238 		    }
239 		}
240 		url_set_readlimit(url, -1);
241 	    }
242 	}
243 
244 	if(comptype == -1)
245 	{
246 	    char buff[BUFSIZ];
247 	    int i;
248 
249 	    url_seek(url, data_start, SEEK_SET);
250 	    url_set_readlimit(url, data_end - data_start);
251 
252 	    for(i = 0; i < MAX_CHECK_LINES; i++)
253 	    {
254 		if(whole_read_line(url, buff, sizeof(buff)) == -1)
255 		    break; /* ?? */
256 		if(strncmp(buff, "begin ", 6) == 0)
257 		{
258 		    comptype = ARCHIVEC_UU;
259 		    data_start = url_tell(url);
260 		    p = strchr(buff + 6, ' ');
261 		    if(p != NULL)
262 			filename = strdup_mblock(&pool, p + 1);
263 		    break;
264 		}
265 		else if((strncmp(buff, "(This file", 10) == 0) ||
266 			(strncmp(buff, "(Convert with", 13) == 0))
267 		{
268 		    int c;
269 		    while((c = url_getc(url)) != EOF)
270 		    {
271 			if(c == ':')
272 			{
273 			    comptype = ARCHIVEC_HQX;
274 			    data_start = url_tell(url);
275 			    break;
276 			}
277 			else if(c == '\n')
278 			{
279 			    if(++i >= MAX_CHECK_LINES)
280 				break;
281 			}
282 		    }
283 		    if(comptype != -1)
284 			break;
285 		}
286 	    }
287 	    url_set_readlimit(url, -1);
288 	}
289 
290 	if(comptype == -1)
291 	    comptype = ARCHIVEC_STORED;
292 
293 	if(filename == NULL)
294 	{
295 	    char buff[32];
296 	    sprintf(buff, "part%d", part);
297 	    filename = strdup_mblock(&pool, buff);
298 	    arctype = -1;
299 	}
300 	else
301 	{
302 	    arctype = get_archive_type(filename);
303 	    switch(arctype)
304 	      {
305 	      case ARCHIVE_TAR:
306 	      case ARCHIVE_TGZ:
307 	      case ARCHIVE_ZIP:
308 	      case ARCHIVE_LZH:
309 		break;
310 	      default:
311 		arctype = -1;
312 		break;
313 	      }
314 	}
315 
316 	if(data_start == data_end)
317 	  {
318 	    ArchiveEntryNode *entry;
319 	    entry = new_entry_node(filename, strlen(filename));
320 	    entry->comptype = ARCHIVEC_STORED;
321 	    entry->compsize = 0;
322 	    entry->origsize = 0;
323 	    entry->start = 0;
324 	    entry->cache = safe_strdup("");
325 	    if(head == NULL)
326 		head = tail = entry;
327 	    else
328 		tail = tail->next = entry;
329 	    goto next_entry;
330 	  }
331 
332 	url_seek(url, data_start, SEEK_SET);
333 	part_data = url_dump(url, data_end - data_start, &part_data_size);
334 	part_data = arc_mime_decode(part_data, part_data_size,
335 				    comptype, &part_data_size);
336 	if(part_data == NULL)
337 	  goto next_entry;
338 
339 	if(arctype == -1)
340 	{
341 	  int gzmethod, gzhdrsiz, len, gz;
342 	  ArchiveEntryNode *entry;
343 
344 	  len = strlen(filename);
345 	  if(len >= 3 && strcasecmp(filename + len - 3, ".gz") == 0)
346 	    {
347 	      gz = 1;
348 	      filename[len - 3] = '\0';
349 	    }
350 	  else
351 	    gz = 0;
352 	  entry = new_entry_node(filename, strlen(filename));
353 
354 	  if(gz)
355 	    gzmethod = parse_gzip_header_bytes(part_data, part_data_size,
356 					       &gzhdrsiz);
357 	  else
358 	    gzmethod = -1;
359 	  if(gzmethod == ARCHIVEC_DEFLATED)
360 	    {
361 	      entry->comptype = ARCHIVEC_DEFLATED;
362 	      entry->compsize = part_data_size - gzhdrsiz;
363 	      entry->origsize = -1;
364 	      entry->start = gzhdrsiz;
365 	      entry->cache = part_data;
366 	    }
367 	  else
368 	    {
369 	      entry->comptype = ARCHIVEC_DEFLATED;
370 	      entry->origsize = part_data_size;
371 	      entry->start = 0;
372 	      entry->cache = arc_compress(part_data, part_data_size,
373 					 ARC_DEFLATE_LEVEL, &entry->compsize);
374 	      free(part_data);
375 	      if(entry->cache == NULL)
376 		{
377 		  free_entry_node(entry);
378 		  goto next_entry;
379 		}
380 	    }
381 	    if(head == NULL)
382 		head = tail = entry;
383 	    else
384 		tail = tail->next = entry;
385 	}
386 	else
387 	{
388 	    URL arcurl;
389 	    ArchiveEntryNode *entry;
390 	    ArchiveHandler orig;
391 
392 	    arcurl = url_mem_open(part_data, part_data_size, 1);
393 	    orig = arc_handler; /* save */
394 	    entry = arc_parse_entry(arcurl, arctype);
395 	    arc_handler = orig; /* restore */
396 	    if(head == NULL)
397 		head = tail = entry;
398 	    else
399 		tail = tail->next = entry;
400 	    while(tail->next)
401 	      tail = tail->next;
402 	}
403 
404       next_entry:
405 	url_seek(url, savepoint, SEEK_SET);
406 	part++;
407 	reuse_mblock(&pool);
408 	end_mime_stream(&hdr);
409 
410 	if(last_check)
411 	{
412 	    pop_string_stack(&boundary);
413 	    if(top_string_stack(&boundary) == NULL)
414 		break;
415 	}
416     }
417     delete_string_stack(&boundary);
418     return head;
419 }
420 
init_string_stack(struct StringStack * stk)421 static void init_string_stack(struct StringStack *stk)
422 {
423     stk->elem = NULL;
424     init_mblock(&stk->pool);
425 }
426 
push_string_stack(struct StringStack * stk,char * str,int len)427 static void push_string_stack(struct StringStack *stk, char *str, int len)
428 {
429     struct StringStackElem *elem;
430 
431     elem = (struct StringStackElem *)
432 	new_segment(&stk->pool, sizeof(struct StringStackElem) + len + 1);
433     memcpy(elem->str, str, len);
434     elem->str[len] = '\0';
435     elem->next = stk->elem;
436     stk->elem = elem;
437 }
438 
top_string_stack(struct StringStack * stk)439 static char *top_string_stack(struct StringStack *stk)
440 {
441     if(stk->elem == NULL)
442 	return NULL;
443     return stk->elem->str;
444 }
445 
pop_string_stack(struct StringStack * stk)446 static void pop_string_stack(struct StringStack *stk)
447 {
448     if(stk->elem == NULL)
449 	return;
450     stk->elem = stk->elem->next;
451 }
452 
delete_string_stack(struct StringStack * stk)453 static void delete_string_stack(struct StringStack *stk)
454 {
455     reuse_mblock(&stk->pool);
456 }
457 
init_mime_stream(struct MIMEHeaderStream * hdr,URL url)458 static void init_mime_stream(struct MIMEHeaderStream *hdr, URL url)
459 {
460     hdr->url = url;
461     hdr->field = hdr->value = hdr->line = NULL;
462     hdr->eof = 0;
463     init_mblock(&hdr->pool);
464 }
465 
whole_read_line(URL url,char * buff,int bufsiz)466 static int whole_read_line(URL url, char *buff, int bufsiz)
467 {
468     int len;
469 
470     if(url_gets(url, buff, bufsiz) == NULL)
471 	return -1;
472     len = strlen(buff);
473     if(len == 0)
474 	return 0;
475     if(buff[len - 1] == '\n')
476     {
477 	buff[--len] = '\0';
478 	if(len > 0 && buff[len - 1] == '\r')
479 	    buff[--len] = '\0';
480     }
481     else
482     {
483 	/* skip line */
484 	int c;
485 	do
486 	{
487 	    c = url_getc(url);
488 	} while(c != EOF && c != '\n');
489     }
490 
491     return len;
492 }
493 
next_mime_header(struct MIMEHeaderStream * hdr)494 static int next_mime_header(struct MIMEHeaderStream *hdr)
495 {
496     int len, c, n;
497     char *p;
498 
499     if(hdr->eof)
500 	return 0;
501 
502     if(hdr->line == NULL)
503     {
504 	hdr->line = (char *)new_segment(&hdr->pool, MIN_MBLOCK_SIZE);
505 	len = whole_read_line(hdr->url, hdr->line, MIN_MBLOCK_SIZE);
506 	if(len <= 0)
507 	{
508 	    if(len == -1)
509 		hdr->eof = 1;
510 	    return 0;
511 	}
512 	hdr->field = (char *)new_segment(&hdr->pool, MIN_MBLOCK_SIZE);
513 	hdr->bufflen = 0;
514     }
515 
516     if((hdr->bufflen = strlen(hdr->line)) == 0)
517 	return 0;
518 
519     memcpy(hdr->field, hdr->line, hdr->bufflen);
520     hdr->field[hdr->bufflen] = '\0';
521 
522     for(;;)
523     {
524 	len = whole_read_line(hdr->url, hdr->line, MIN_MBLOCK_SIZE);
525 	if(len <= 0)
526 	{
527 	    if(len == -1)
528 		hdr->eof = 1;
529 	    break;
530 	}
531 	c = *hdr->line;
532 	if(c == '>' || ('A' <= c && c <= 'Z') ||  ('a' <= c && c <= 'z'))
533 	    break;
534 	if(c != ' ' && c != '\t')
535 	    return 0; /* ?? */
536 
537 	n = MIN_MBLOCK_SIZE - 1 - hdr->bufflen;
538 	if(n > 0)
539 	{
540 	    int i;
541 
542 	    if(len > n)
543 		len = n;
544 
545 	    /* s/\t/ /g; */
546 	    p = hdr->line;
547 	    for(i = 0; i < len; i++)
548 		if(p[i] == '\t')
549 		    p[i] = ' ';
550 
551 	    memcpy(hdr->field + hdr->bufflen, p, len);
552 	    hdr->bufflen += len;
553 	    hdr->field[hdr->bufflen] = '\0';
554 	}
555     }
556     p = hdr->field;
557     while(*p && *p != ':')
558 	p++;
559     if(!*p)
560 	return 0;
561     *p++ = '\0';
562     while(*p && *p == ' ')
563 	p++;
564     hdr->value = p;
565     return 1;
566 }
567 
end_mime_stream(struct MIMEHeaderStream * hdr)568 static void end_mime_stream(struct MIMEHeaderStream *hdr)
569 {
570     reuse_mblock(&hdr->pool);
571 }
572 
seek_next_boundary(URL url,char * boundary,long * endpoint)573 static int seek_next_boundary(URL url, char *boundary, long *endpoint)
574 {
575     MBlockList pool;
576     char *buff;
577     int blen, ret;
578 
579     if(boundary == NULL)
580     {
581 	url_seek(url, 0, SEEK_END);
582 	*endpoint = url_tell(url);
583 	return 0;
584     }
585 
586     init_mblock(&pool);
587     buff = (char *)new_segment(&pool, MIN_MBLOCK_SIZE);
588     blen = strlen(boundary);
589     ret = 0;
590     for(;;)
591     {
592 	int len;
593 
594 	*endpoint = url_tell(url);
595 	if((len = whole_read_line(url, buff, MIN_MBLOCK_SIZE)) < 0)
596 	    break;
597 	if(len < blen + 2)
598 	    continue;
599 
600 	if(buff[0] == '-' && buff[1] == '-' &&
601 	   strncmp(buff + 2, boundary, blen) == 0)
602 	{
603 	    if(buff[blen + 2] == '-' && buff[blen + 3] == '-')
604 		ret = 1;
605 	    break;
606 	}
607     }
608     reuse_mblock(&pool);
609     return ret;
610 }
611 
arc_mime_decode(void * data,long size,int comptype,long * newsize)612 static void *arc_mime_decode(void *data, long size,
613 			     int comptype, long *newsize)
614 {
615   URL url;
616 
617   if(comptype == ARCHIVEC_STORED)
618     return data;
619 
620   if(data == NULL)
621     return NULL;
622 
623   if((url = url_mem_open(data, size, 1)) == NULL)
624     return NULL;
625 
626   switch(comptype)
627     {
628     case ARCHIVEC_UU:		/* uu encoded */
629       url = url_uudecode_open(url, 1);
630       break;
631     case ARCHIVEC_B64:		/* base64 encoded */
632       url = url_b64decode_open(url, 1);
633       break;
634     case ARCHIVEC_QS:		/* quoted string encoded */
635       url = url_hqxdecode_open(url, 1, 1);
636       break;
637     case ARCHIVEC_HQX:		/* HQX encoded */
638       url = url_qsdecode_open(url, 1);
639       break;
640     default:
641       url_close(url);
642       return NULL;
643     }
644   data = url_dump(url, -1, newsize);
645   url_close(url);
646   return data;
647 }
648