1 /* <!-- copyright */
2 /*
3 * libmetalink
4 *
5 * Copyright (c) 2008 Tatsuhiro Tsujikawa
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 */
25 /* copyright --> */
26 #include "metalink_pstate_v4.h"
27
28 #include <time.h>
29 #ifdef HAVE_TIME64_H
30 # include <time64.h>
31 #endif
32 #include <string.h>
33 #include <stdlib.h>
34 #include <errno.h>
35 #include <limits.h>
36
37 #include "metalink_pstm.h"
38 #include "metalink_helper.h"
39 #ifndef HAVE_STRPTIME
40 # include "strptime.h"
41 #endif /* !HAVE_STRPTIME */
42 #ifndef HAVE_TIMEGM
43 # include "timegm.h"
44 #endif /* !HAVE_TIMEGM */
45
my_timegm(struct tm * tm)46 static time_t my_timegm(struct tm *tm)
47 {
48 #ifdef HAVE__MKGMTIME
49 /* WINE does not implement _mkgmtime() yet */
50 return _mkgmtime(tm);
51 #elif defined(HAVE_MKGMTIME)
52 return mkgmtime(tm);
53 #elif defined(HAVE_TIMEGM64)
54 return (time_t)timegm64(tm);
55 #else
56 return timegm(tm);
57 #endif
58 }
59
60 /* parse a RFC3339 formatted date, ie. 2010-05-01T12:15:02Z or
61 2010-05-01T12:16:02+01:00 */
parse_date(const char * date)62 static time_t parse_date(const char* date)
63 {
64 time_t t = 0;
65 int sign = 0;
66 struct tm tm = {0}, interval = {0};
67 char* rest;
68
69 if(strlen(date) < 20)
70 {
71 return t;
72 }
73
74 rest = strptime(date, "%Y-%m-%dT%H:%M:%S", &tm);
75
76 if(rest == NULL)
77 {
78 return t;
79 }
80
81 t = my_timegm(&tm);
82
83 while(*rest != 'Z' && *rest != '+' && *rest != '-' && *rest != '\0')
84 {
85 rest++;
86 }
87
88 if(*rest == '\0')
89 {
90 /* not a valid RFC3339 date, but we can still return what has already been
91 * parsed */
92 return t;
93 }
94 if(*rest == '+') {
95 sign = -1;
96 } else if(*rest == '-') {
97 sign = +1;
98 }
99
100 if(sign != 0) {
101 if(strptime(rest+1, "%H:%M", &interval) == NULL)
102 return t;
103 t += sign*(interval.tm_hour*3600+interval.tm_min*60);
104 }
105
106 return t;
107 }
108
109 /* metalink state <metalink> */
metalink_state_start_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char ** attrs)110 void metalink_state_start_fun_v4(metalink_pstm_t* stm,
111 const char* name, const char* ns_uri,
112 const char** attrs)
113 {
114 metalink_error_t r;
115
116 if(strcmp("file", name) == 0) {
117 const char* fname;
118 metalink_file_t* file;
119
120 fname = get_attribute_value(attrs, "name");
121 if(!metalink_check_safe_path(fname)) {
122 metalink_pstm_enter_skip_state(stm);
123 return;
124 }
125
126 file = metalink_pctrl_new_file_transaction(stm->ctrl);
127 if(!file) {
128 error_handler(stm, METALINK_ERR_BAD_ALLOC);
129 return;
130 }
131 r = metalink_pctrl_file_set_name(stm->ctrl, fname);
132 if(r != 0) {
133 error_handler(stm, r);
134 return;
135 }
136
137 metalink_pstm_enter_file_state_v4(stm);
138 } else if(strcmp("generator", name) == 0) {
139 metalink_pstm_enter_generator_state(stm);
140 } else if(strcmp("origin", name) == 0) {
141 const char* dynamic_attr;
142 dynamic_attr = get_attribute_value(attrs, "dynamic");
143 if(dynamic_attr && strcmp("true", dynamic_attr) == 0) {
144 metalink_pctrl_set_origin_dynamic(stm->ctrl, 1);
145 }
146 metalink_pstm_enter_origin_state(stm);
147 } else if(strcmp("published", name) == 0) {
148 metalink_pstm_enter_published_state_v4(stm);
149 } else if(strcmp("updated", name) == 0) {
150 metalink_pstm_enter_updated_state_v4(stm);
151 } else {
152 metalink_pstm_enter_skip_state(stm);
153 }
154 }
155
metalink_state_end_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char * characters)156 void metalink_state_end_fun_v4(metalink_pstm_t* stm,
157 const char* name, const char* ns_uri,
158 const char* characters)
159 {
160 metalink_error_t r;
161 r = metalink_pctrl_metalink_accumulate_files(stm->ctrl);
162 if (r != 0) {
163 error_handler(stm, r);
164 return;
165 }
166 metalink_pstm_enter_fin_state(stm);
167 }
168
169 /* generator state <generator> */
generator_state_start_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char ** attrs)170 void generator_state_start_fun_v4(metalink_pstm_t* stm,
171 const char* name, const char* ns_uri,
172 const char** attrs)
173 {
174 metalink_pstm_enter_skip_state(stm);
175 }
176
generator_state_end_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char * characters)177 void generator_state_end_fun_v4(metalink_pstm_t* stm,
178 const char* name, const char* ns_uri,
179 const char* characters)
180 {
181 if(strcmp("generator", name) == 0) {
182 metalink_pctrl_set_generator(stm->ctrl, characters);
183 } else {
184 metalink_pstm_enter_skip_state(stm);
185 return;
186 }
187 metalink_pstm_enter_metalink_state_v4(stm);
188 }
189
190 /* origin state <origin> */
origin_state_start_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char ** attrs)191 void origin_state_start_fun_v4(metalink_pstm_t* stm,
192 const char* name, const char* ns_uri,
193 const char** attrs)
194 {
195 metalink_pstm_enter_skip_state(stm);
196 }
197
origin_state_end_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char * characters)198 void origin_state_end_fun_v4(metalink_pstm_t* stm,
199 const char* name, const char* ns_uri,
200 const char* characters)
201 {
202 if(strcmp("origin", name) == 0) {
203 metalink_pctrl_set_origin(stm->ctrl, characters);
204 } else {
205 metalink_pstm_enter_skip_state(stm);
206 return;
207 }
208 metalink_pstm_enter_metalink_state_v4(stm);
209 }
210
211 /* file state <file> */
file_state_start_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char ** attrs)212 void file_state_start_fun_v4(metalink_pstm_t* stm,
213 const char* name, const char* ns_uri,
214 const char** attrs)
215 {
216 metalink_error_t r;
217
218 if(strcmp("url", name) == 0) {
219 const char* location;
220 const char* value;
221 long int priority = 999999;
222 metalink_resource_t* resource;
223
224 resource = metalink_pctrl_new_resource_transaction(stm->ctrl);
225 if(!resource) {
226 error_handler(stm, METALINK_ERR_BAD_ALLOC);
227 return;
228 }
229
230 location = get_attribute_value(attrs, "location");
231 if(location) {
232 r = metalink_pctrl_resource_set_location(stm->ctrl, location);
233 if(r != 0) {
234 error_handler(stm, r);
235 return;
236 }
237 }
238
239 value = get_attribute_value(attrs, "priority");
240 if(value) {
241 errno = 0;
242 priority = strtol(value, 0, 10);
243 if(errno == ERANGE || priority < 0 || priority > INT_MAX) {
244 priority = 999999;
245 }
246 }
247 metalink_pctrl_resource_set_priority(stm->ctrl, priority);
248
249 metalink_pstm_enter_url_state(stm);
250 } else if(strcmp("metaurl", name) == 0) {
251 const char* mediatype;
252 const char* name;
253 const char* value;
254 long int priority = 999999;
255 metalink_metaurl_t* metaurl;
256
257 metaurl = metalink_pctrl_new_metaurl_transaction(stm->ctrl);
258 if(!metaurl) {
259 error_handler(stm, METALINK_ERR_BAD_ALLOC);
260 return;
261 }
262
263 mediatype = get_attribute_value(attrs, "mediatype");
264 if(mediatype) {
265 r = metalink_pctrl_metaurl_set_mediatype(stm->ctrl, mediatype);
266 if(r != 0) {
267 error_handler(stm, r);
268 return;
269 }
270 } else {
271 /* mediatype argument is mandatory, skip if not present */
272 metalink_pstm_enter_skip_state(stm);
273 return;
274 }
275
276 name = get_attribute_value(attrs, "name");
277 if(name) {
278 r = metalink_pctrl_metaurl_set_name(stm->ctrl, name);
279 if(r != 0) {
280 error_handler(stm, r);
281 return;
282 }
283 }
284
285 value = get_attribute_value(attrs, "priority");
286 if(value) {
287 errno = 0;
288 priority = strtol(value, 0, 10);
289 if(errno == ERANGE || priority < 0 || priority > INT_MAX) {
290 priority = 999999;
291 }
292 }
293 metalink_pctrl_metaurl_set_priority(stm->ctrl, priority);
294
295 metalink_pstm_enter_metaurl_state_v4(stm);
296 } else if(strcmp("hash", name) == 0) {
297 const char* type;
298 metalink_checksum_t* checksum;
299
300 type = get_attribute_value(attrs, "type");
301 if(!type) {
302 metalink_pstm_enter_skip_state(stm);
303 return;
304 }
305 checksum = metalink_pctrl_new_checksum_transaction(stm->ctrl);
306 if(!checksum) {
307 error_handler(stm, METALINK_ERR_BAD_ALLOC);
308 return;
309 }
310 r = metalink_checksum_set_type(checksum, type);
311 if(r != 0) {
312 error_handler(stm, METALINK_ERR_BAD_ALLOC);
313 return;
314 }
315 metalink_pstm_enter_hash_state(stm);
316 } else if(strcmp("pieces", name) == 0) {
317 const char* type;
318 const char* value;
319 long int length;
320 metalink_chunk_checksum_t* chunk_checksum;
321
322 type = get_attribute_value(attrs, "type");
323 if(!type) {
324 metalink_pstm_enter_skip_state(stm);
325 return;
326 }
327
328 value = get_attribute_value(attrs, "length");
329 if(value) {
330 errno = 0;
331 length = strtol(value, 0, 10);
332 if(errno == ERANGE || length < 0 || length > INT_MAX) {
333 /* Invalid piece size: skip this tag */
334 metalink_pstm_enter_skip_state(stm);
335 return;
336 }
337 } else {
338 metalink_pstm_enter_skip_state(stm);
339 return;
340 }
341
342 chunk_checksum = metalink_pctrl_new_chunk_checksum_transaction(stm->ctrl);
343 if(!chunk_checksum) {
344 error_handler(stm, METALINK_ERR_BAD_ALLOC);
345 return;
346 }
347 r = metalink_chunk_checksum_set_type(chunk_checksum, type);
348 if(r != 0) {
349 error_handler(stm, METALINK_ERR_BAD_ALLOC);
350 return;
351 }
352 metalink_chunk_checksum_set_length(chunk_checksum, length);
353
354 metalink_pstm_enter_pieces_state_v4(stm);
355 } else if(strcmp("signature", name) == 0) {
356 const char* mediatype;
357 metalink_signature_t* signature;
358
359 mediatype = get_attribute_value(attrs, "mediatype");
360 if(!mediatype) {
361 metalink_pstm_enter_skip_state(stm);
362 return;
363 }
364 signature = metalink_pctrl_new_signature_transaction(stm->ctrl);
365 if(!signature) {
366 error_handler(stm, METALINK_ERR_BAD_ALLOC);
367 return;
368 }
369 r = metalink_signature_set_mediatype(signature, mediatype);
370 if(r != 0) {
371 error_handler(stm, METALINK_ERR_BAD_ALLOC);
372 return;
373 }
374 metalink_pstm_enter_signature_state_v4(stm);
375 } else if(strcmp("publisher", name) == 0) {
376 const char* name;
377 const char* url;
378
379 name = get_attribute_value(attrs, "name");
380 if(!name) {
381 /* name is mandatory */
382 metalink_pstm_enter_skip_state(stm);
383 return;
384 }
385 r = metalink_pctrl_file_set_publisher_name(stm->ctrl, name);
386 if(r != 0) {
387 error_handler(stm, METALINK_ERR_BAD_ALLOC);
388 return;
389 }
390
391 url = get_attribute_value(attrs, "url");
392 if(url) {
393 /* url is optional */
394 r = metalink_pctrl_file_set_publisher_url(stm->ctrl, url);
395 if(r != 0) {
396 error_handler(stm, METALINK_ERR_BAD_ALLOC);
397 return;
398 }
399 }
400 metalink_pstm_enter_skip_state(stm);
401 } else if(strcmp("description", name) == 0) {
402 metalink_pstm_enter_description_state_v4(stm);
403 } else if(strcmp("copyright", name) == 0) {
404 metalink_pstm_enter_copyright_state_v4(stm);
405 } else if(strcmp("identity", name) == 0) {
406 metalink_pstm_enter_identity_state_v4(stm);
407 } else if(strcmp("logo", name) == 0) {
408 metalink_pstm_enter_logo_state_v4(stm);
409 } else if(strcmp("language", name) == 0) {
410 metalink_pstm_enter_language_state(stm);
411 } else if(strcmp("os", name) == 0) {
412 metalink_pstm_enter_os_state(stm);
413 } else if(strcmp("size", name) == 0) {
414 metalink_pstm_enter_size_state(stm);
415 } else if(strcmp("version", name) == 0) {
416 metalink_pstm_enter_version_state(stm);
417 } else {
418 metalink_pstm_enter_skip_state(stm);
419 }
420 }
421
file_state_end_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char * characters)422 void file_state_end_fun_v4(metalink_pstm_t* stm,
423 const char* name, const char* ns_uri,
424 const char* characters)
425 {
426 metalink_error_t r;
427 r = metalink_pctrl_commit_file_transaction(stm->ctrl);
428 if(r != 0) {
429 error_handler(stm, r);
430 return;
431 }
432 metalink_pstm_enter_metalink_state_v4(stm);
433 }
434
435 /* description state <description> */
description_state_start_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char ** attrs)436 void description_state_start_fun_v4(metalink_pstm_t* stm,
437 const char* name, const char* ns_uri,
438 const char** attrs)
439 {
440 metalink_pstm_enter_skip_state(stm);
441 }
442
description_state_end_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char * characters)443 void description_state_end_fun_v4(metalink_pstm_t* stm,
444 const char* name, const char* ns_uri,
445 const char* characters)
446 {
447 metalink_error_t r;
448 r = metalink_pctrl_file_set_description(stm->ctrl, characters);
449 if(r != 0) {
450 error_handler(stm, r);
451 return;
452 }
453 metalink_pstm_enter_file_state_v4(stm);
454 }
455
456 /* copyright state <copyright> */
copyright_state_start_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char ** attrs)457 void copyright_state_start_fun_v4(metalink_pstm_t* stm,
458 const char* name, const char* ns_uri,
459 const char** attrs)
460 {
461 metalink_pstm_enter_skip_state(stm);
462 }
463
copyright_state_end_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char * characters)464 void copyright_state_end_fun_v4(metalink_pstm_t* stm,
465 const char* name, const char* ns_uri,
466 const char* characters)
467 {
468 metalink_error_t r;
469 r = metalink_pctrl_file_set_copyright(stm->ctrl, characters);
470 if(r != 0) {
471 error_handler(stm, r);
472 return;
473 }
474 metalink_pstm_enter_file_state_v4(stm);
475 }
476
477 /* identity state <identity> */
identity_state_start_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char ** attrs)478 void identity_state_start_fun_v4(metalink_pstm_t* stm,
479 const char* name, const char* ns_uri,
480 const char** attrs)
481 {
482 metalink_pstm_enter_skip_state(stm);
483 }
484
identity_state_end_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char * characters)485 void identity_state_end_fun_v4(metalink_pstm_t* stm,
486 const char* name, const char* ns_uri,
487 const char* characters)
488 {
489 metalink_error_t r;
490 r = metalink_pctrl_file_set_identity(stm->ctrl, characters);
491 if(r != 0) {
492 error_handler(stm, r);
493 return;
494 }
495 metalink_pstm_enter_file_state_v4(stm);
496 }
497
498 /* logo state <logo> */
logo_state_start_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char ** attrs)499 void logo_state_start_fun_v4(metalink_pstm_t* stm,
500 const char* name, const char* ns_uri,
501 const char** attrs)
502 {
503 metalink_pstm_enter_skip_state(stm);
504 }
505
logo_state_end_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char * characters)506 void logo_state_end_fun_v4(metalink_pstm_t* stm,
507 const char* name, const char* ns_uri,
508 const char* characters)
509 {
510 metalink_error_t r;
511 r = metalink_pctrl_file_set_logo(stm->ctrl, characters);
512 if(r != 0) {
513 error_handler(stm, r);
514 return;
515 }
516 metalink_pstm_enter_file_state_v4(stm);
517 }
518
519 /* signature state <signature> */
signature_state_start_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char ** attrs)520 void signature_state_start_fun_v4(metalink_pstm_t* stm,
521 const char* name, const char* ns_uri,
522 const char** attrs)
523 {
524 metalink_pstm_enter_skip_state(stm);
525 }
526
signature_state_end_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char * characters)527 void signature_state_end_fun_v4(metalink_pstm_t* stm,
528 const char* name, const char* ns_uri,
529 const char* characters)
530 {
531 metalink_error_t r;
532 r = metalink_pctrl_signature_set_signature(stm->ctrl, characters);
533 if(r != 0) {
534 error_handler(stm, r);
535 return;
536 }
537
538 r = metalink_pctrl_commit_signature_transaction(stm->ctrl);
539 if(r != 0) {
540 error_handler(stm, r);
541 return;
542 }
543
544 metalink_pstm_enter_file_state_v4(stm);
545 }
546
547 /* pieces state <pieces> */
pieces_state_start_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char ** attrs)548 void pieces_state_start_fun_v4(metalink_pstm_t* stm,
549 const char* name, const char* ns_uri,
550 const char** attrs)
551 {
552 if(strcmp("hash", name) == 0) {
553 metalink_piece_hash_t* piece_hash;
554
555
556 piece_hash = metalink_pctrl_new_piece_hash_transaction(stm->ctrl);
557 if(!piece_hash) {
558 error_handler(stm, METALINK_ERR_BAD_ALLOC);
559 return;
560 }
561
562 metalink_pstm_enter_piece_hash_state(stm);
563 } else {
564 metalink_pstm_enter_skip_state(stm);
565 }
566 }
567
pieces_state_end_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char * characters)568 void pieces_state_end_fun_v4(metalink_pstm_t* stm,
569 const char* name, const char* ns_uri,
570 const char* characters)
571 {
572 metalink_error_t r;
573 r = metalink_pctrl_commit_chunk_checksum_transaction(stm->ctrl);
574 if(r != 0) {
575 error_handler(stm, r);
576 return;
577 }
578
579 metalink_pstm_enter_file_state_v4(stm);
580 }
581
582 /* metaurl state <metaurl> */
metaurl_state_start_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char ** attrs)583 void metaurl_state_start_fun_v4(metalink_pstm_t* stm,
584 const char* name, const char* ns_uri,
585 const char** attrs)
586 {
587 metalink_pstm_enter_skip_state(stm);
588 }
589
metaurl_state_end_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char * characters)590 void metaurl_state_end_fun_v4(metalink_pstm_t* stm,
591 const char* name, const char* ns_uri,
592 const char* characters)
593 {
594 metalink_error_t r;
595 r = metalink_pctrl_metaurl_set_url(stm->ctrl, characters);
596 if(r != 0) {
597 error_handler(stm, r);
598 return;
599 }
600
601 r = metalink_pctrl_commit_metaurl_transaction(stm->ctrl);
602 if(r != 0) {
603 error_handler(stm, r);
604 return;
605 }
606
607 metalink_pstm_enter_file_state_v4(stm);
608 }
609
610 /* published state <published> */
published_state_start_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char ** attrs)611 void published_state_start_fun_v4(metalink_pstm_t* stm,
612 const char* name, const char* ns_uri,
613 const char** attrs)
614 {
615 metalink_pstm_enter_skip_state(stm);
616 }
617
published_state_end_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char * characters)618 void published_state_end_fun_v4(metalink_pstm_t* stm,
619 const char* name, const char* ns_uri,
620 const char* characters)
621 {
622 time_t t = parse_date(characters);
623 metalink_pctrl_set_published(stm->ctrl, t);
624 metalink_pstm_enter_metalink_state_v4(stm);
625 }
626
627 /* updated state <updated> */
updated_state_start_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char ** attrs)628 void updated_state_start_fun_v4(metalink_pstm_t* stm,
629 const char* name, const char* ns_uri,
630 const char** attrs)
631 {
632 metalink_pstm_enter_skip_state(stm);
633 }
634
updated_state_end_fun_v4(metalink_pstm_t * stm,const char * name,const char * ns_uri,const char * characters)635 void updated_state_end_fun_v4(metalink_pstm_t* stm,
636 const char* name, const char* ns_uri,
637 const char* characters)
638 {
639 time_t t = parse_date(characters);
640 metalink_pctrl_set_updated(stm->ctrl, t);
641 metalink_pstm_enter_metalink_state_v4(stm);
642 }
643