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