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