1 /*
2  * This file is part of the Advance project.
3  *
4  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004 Andrea Mazzoleni
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., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20 
21 #include "portable.h"
22 
23 #include "zip.h"
24 #include "siglock.h"
25 #include "file.h"
26 #include "data.h"
27 #include "lib/endianrw.h"
28 
29 #include <zlib.h>
30 
31 #include <iostream>
32 #include <iomanip>
33 #include <string>
34 #include <sstream>
35 #include <set>
36 
37 using namespace std;
38 
39 /**
40  * Enable pendantic checks on the zip integrity.
41  */
42 bool zip::pedantic = false;
43 
ecd_compare_sig(const unsigned char * buffer)44 bool ecd_compare_sig(const unsigned char *buffer)
45 {
46 	static char ecdsig[] = { 'P', 'K', 0x05, 0x06 };
47 	return memcmp(buffer, ecdsig, 4) == 0;
48 }
49 
50 /**
51  * Locate end-of-central-dir sig in buffer and return offset
52  * \param offset Resulting offset of cent dir start in buffer.
53  * \reutn If the end-of-central-dir is found.
54  */
ecd_find_sig(const unsigned char * buffer,unsigned buflen,unsigned & offset)55 bool ecd_find_sig (const unsigned char *buffer, unsigned buflen, unsigned& offset)
56 {
57 	for (int i=buflen-22; i>=0; i--) {
58 		if (ecd_compare_sig(buffer+i)) {
59 			offset = i;
60 			return true;
61 		}
62 	}
63 	return false;
64 }
65 
66 #define ECD_READ_BUFFER_SIZE 4096
67 
68 /**
69  * Read cent dir and end cent dir data
70  * \param f File to read.
71  * \param length Length of the file.
72  */
cent_read(FILE * f,unsigned length,unsigned char * & data,unsigned & size)73 bool cent_read(FILE* f, unsigned length, unsigned char*& data, unsigned& size)
74 {
75 	unsigned buf_length;
76 
77 	if (length <= ECD_READ_BUFFER_SIZE) {
78 		buf_length = length;
79 	} else {
80 		// align the read
81 		buf_length = length - ((length - ECD_READ_BUFFER_SIZE) & ~(ECD_READ_BUFFER_SIZE-1));
82 	}
83 
84 	while (true) {
85 		if (buf_length > length)
86 			buf_length = length;
87 
88 		if (fseek(f, length - buf_length, SEEK_SET) != 0) {
89 			return false;
90 		}
91 
92 		// allocate buffer
93 		unsigned char* buf = data_alloc(buf_length);
94 		assert(buf);
95 
96 		if (fread(buf, buf_length, 1, f) != 1) {
97 			data_free(buf);
98 			return false;
99 		}
100 
101 		unsigned offset = 0;
102 		if (ecd_find_sig(buf, buf_length, offset)) {
103 			unsigned start_of_cent_dir = le_uint32_read(buf + offset + ZIP_EO_offset_to_start_of_cent_dir);
104 			unsigned buf_pos = length - buf_length;
105 
106 			if (start_of_cent_dir >= length) {
107 				data_free(buf);
108 				return false;
109 			}
110 
111 			size = length - start_of_cent_dir;
112 
113 			data = data_alloc(size);
114 			assert(data);
115 
116 			if (buf_pos <= start_of_cent_dir) {
117 				memcpy(data, buf + (start_of_cent_dir - buf_pos), size);
118 				data_free(buf);
119 			} else {
120 				data_free(buf);
121 
122 				if (fseek(f, start_of_cent_dir, SEEK_SET) != 0) {
123 					data_free(data);
124 					data = 0;
125 					return false;
126 				}
127 
128 				if (fread(data, size, 1, f) != 1) {
129 					data_free(data);
130 					data = 0;
131 					return false;
132 				}
133 			}
134 			return true;
135 		}
136 
137 		data_free(buf);
138 
139 		if (buf_length < 8 * ECD_READ_BUFFER_SIZE && buf_length < length) {
140 			// grow buffer
141 			buf_length += ECD_READ_BUFFER_SIZE;
142 		} else {
143 			// aborting
144 			return false;
145 		}
146 	}
147 }
148 
149 /** Code used for disk entry. */
150 #define ZIP_UNIQUE_DISK 0
151 
152 /** Convert time_t to zip format. */
time2zip(time_t tod,unsigned & date,unsigned & time)153 void time2zip(time_t tod, unsigned& date, unsigned& time)
154 {
155 	struct tm* tm = gmtime(&tod);
156 	assert(tm);
157 	unsigned day = tm->tm_mday; // 1-31
158 	unsigned mon = tm->tm_mon + 1; // 1-12
159 	unsigned year = tm->tm_year - 80; // since 1980
160 	date = (day & 0x1F) | ((mon & 0xF) << 5) | ((year & 0x7F) << 9);
161 	unsigned sec = tm->tm_sec / 2; // 0-29, double to get real seconds
162 	unsigned min = tm->tm_min; // 0-59
163 	unsigned hour = tm->tm_hour; // 0-23
164 	time = (sec & 0x1F) | ((min & 0x3F) << 5) | ((hour & 0x1F) << 11);
165 }
166 
167 /** Convert zip time to to time_t. */
zip2time(unsigned date,unsigned time)168 time_t zip2time(unsigned date, unsigned time)
169 {
170 	struct tm tm;
171 	// reset all entry
172 	memset(&tm, 0, sizeof(tm));
173 	// set know entry
174 	tm.tm_mday = date & 0x1F; // 1-31
175 	tm.tm_mon = ((date >> 5) & 0xF) - 1; // 0-11
176 	tm.tm_year = ((date >> 9) & 0x7f) + 80; // since 1900
177 	tm.tm_sec = (time & 0x1F) * 2; // 0-59
178 	tm.tm_min = (time >> 5) & 0x3F; // 0-59
179 	tm.tm_hour = (time >> 11) & 0x1F; // 0-23
180 	return mktime(&tm);
181 }
182 
zip_entry(const zip & Aparent)183 zip_entry::zip_entry(const zip& Aparent)
184 {
185 	memset(&info, 0xFF, sizeof(info));
186 
187 	parent_name = Aparent.file_get();
188 
189 	info.filename_length = 0;
190 	file_name = 0;
191 
192 	info.local_extra_field_length = 0;
193 	local_extra_field = 0;
194 
195 	info.central_extra_field_length = 0;
196 	central_extra_field = 0;
197 
198 	info.file_comment_length = 0;
199 	file_comment = 0;
200 
201 	info.compressed_size = 0;
202 	data = 0;
203 }
204 
zip_entry(const zip_entry & A)205 zip_entry::zip_entry(const zip_entry& A)
206 {
207 	info = A.info;
208 	parent_name = A.parent_name;
209 	file_name = data_dup(A.file_name, info.filename_length);
210 	local_extra_field = data_dup(A.local_extra_field, info.local_extra_field_length);
211 	central_extra_field = data_dup(A.central_extra_field, info.central_extra_field_length);
212 	file_comment = data_dup(A.file_comment, info.file_comment_length);
213 	data = data_dup(A.data, A.info.compressed_size);
214 }
215 
~zip_entry()216 zip_entry::~zip_entry()
217 {
218 	data_free(file_name);
219 	data_free(local_extra_field);
220 	data_free(central_extra_field);
221 	data_free(file_comment);
222 	data_free(data);
223 }
224 
method_get() const225 zip_entry::method_t zip_entry::method_get() const
226 {
227 	switch (info.compression_method) {
228 		case ZIP_METHOD_STORE :
229 			return store;
230 		case ZIP_METHOD_SHRUNK :
231 			return shrunk;
232 		case ZIP_METHOD_REDUCE1 :
233 			return reduce1;
234 		case ZIP_METHOD_REDUCE2 :
235 			return reduce2;
236 		case ZIP_METHOD_REDUCE3 :
237 			return reduce3;
238 		case ZIP_METHOD_REDUCE4 :
239 			return reduce4;
240 		case ZIP_METHOD_IMPLODE :
241 			switch (info.general_purpose_bit_flag & ZIP_GEN_FLAGS_IMPLODE_MASK) {
242 				case ZIP_GEN_FLAGS_IMPLODE_4KD2T :
243 					return implode_4kdict_2tree;
244 				case ZIP_GEN_FLAGS_IMPLODE_8KD2T :
245 					return implode_8kdict_2tree;
246 				case ZIP_GEN_FLAGS_IMPLODE_4KD3T :
247 					return implode_4kdict_3tree;
248 				case ZIP_GEN_FLAGS_IMPLODE_8KD3T :
249 					return implode_8kdict_3tree;
250 			}
251 			return unknown;
252 		case ZIP_METHOD_DEFLATE :
253 			switch (info.general_purpose_bit_flag & ZIP_GEN_FLAGS_DEFLATE_MASK) {
254 				case ZIP_GEN_FLAGS_DEFLATE_NORMAL :
255 					return deflate6;
256 				case ZIP_GEN_FLAGS_DEFLATE_MAXIMUM :
257 					return deflate9;
258 				case ZIP_GEN_FLAGS_DEFLATE_FAST :
259 					return deflate3;
260 				case ZIP_GEN_FLAGS_DEFLATE_SUPERFAST :
261 					return deflate1;
262 			}
263 			return unknown;
264 		case ZIP_METHOD_BZIP2 :
265 			return bzip2;
266 		case ZIP_METHOD_LZMA :
267 			return lzma;
268 	}
269 
270 	return unknown;
271 }
272 
is_text() const273 bool zip_entry::is_text() const
274 {
275 	return (info.internal_file_attrib & ZIP_INT_ATTR_TEXT) != 0;
276 }
277 
compressed_seek(FILE * f) const278 void zip_entry::compressed_seek(FILE* f) const
279 {
280 	// seek to local header
281 	if (fseek(f, offset_get(), SEEK_SET) != 0) {
282 		throw error_invalid() << "Failed seek " << parentname_get();
283 	}
284 
285 	// read local header
286 	unsigned char buf[ZIP_LO_FIXED];
287 	if (fread(buf, ZIP_LO_FIXED, 1, f) != 1) {
288 		throw error() << "Failed read " << parentname_get();
289 	}
290 
291 	check_local(buf);
292 
293 	// use the local extra_field_length. It may be different than the
294 	// central directory version in some zips.
295 	unsigned local_extra_field_length = le_uint16_read(buf+ZIP_LO_extra_field_length);
296 
297 	// seek to data
298 	if (fseek(f, info.filename_length + local_extra_field_length, SEEK_CUR) != 0) {
299 		throw error_invalid() << "Failed seek " << parentname_get();
300 	}
301 }
302 
compressed_read(unsigned char * outdata) const303 void zip_entry::compressed_read(unsigned char* outdata) const
304 {
305 	if (data) {
306 		memcpy(outdata, data, compressed_size_get());
307 	} else {
308 		FILE* f = fopen(parentname_get().c_str(), "rb");
309 		if (!f) {
310 			throw error() << "Failed open for reading " << parentname_get();
311 		}
312 
313 		try {
314 			compressed_seek(f);
315 
316 			if (compressed_size_get() > 0) {
317 				if (fread(outdata, compressed_size_get(), 1, f) != 1) {
318 					throw error() << "Failed read " << parentname_get();
319 				}
320 			}
321 
322 		} catch (...) {
323 			fclose(f);
324 			throw;
325 		}
326 
327 		fclose(f);
328 	}
329 }
330 
time_get() const331 time_t zip_entry::time_get() const
332 {
333 	return zip2time(info.last_mod_file_date, info.last_mod_file_time);
334 }
335 
time_set(time_t tod)336 void zip_entry::time_set(time_t tod)
337 {
338 	time2zip(tod, info.last_mod_file_date, info.last_mod_file_time);
339 }
340 
set(method_t method,const string & Aname,const unsigned char * compdata,unsigned compsize,unsigned size,unsigned crc,unsigned date,unsigned time,bool is_text)341 void zip_entry::set(method_t method, const string& Aname, const unsigned char* compdata, unsigned compsize, unsigned size, unsigned crc, unsigned date, unsigned time, bool is_text)
342 {
343 	info.version_needed_to_extract = 20; // version 2.0
344 	info.os_needed_to_extract = 0;
345 	info.version_made_by = 20; // version 2.0
346 	info.host_os = 0;
347 	info.general_purpose_bit_flag = 0; // default
348 	info.last_mod_file_date = date;
349 	info.last_mod_file_time = time;
350 	info.crc32 = crc;
351 	info.compressed_size = compsize;
352 	info.uncompressed_size = size;
353 	info.internal_file_attrib = is_text ? ZIP_INT_ATTR_TEXT : 0;
354 	info.external_file_attrib = 0;
355 
356 	switch (method) {
357 		case store :
358 			if (size != compsize) {
359 				throw error_invalid() << "Zip entry size mismatch";
360 			}
361 			info.compression_method = ZIP_METHOD_STORE;
362 			info.version_needed_to_extract = 10; // Version 1.0
363 		break;
364 		case shrunk :
365 			info.compression_method = ZIP_METHOD_SHRUNK;
366 		break;
367 		case reduce1 :
368 			info.compression_method = ZIP_METHOD_REDUCE1;
369 		break;
370 		case reduce2 :
371 			info.compression_method = ZIP_METHOD_REDUCE2;
372 		break;
373 		case reduce3 :
374 			info.compression_method = ZIP_METHOD_REDUCE3;
375 		break;
376 		case reduce4 :
377 			info.compression_method = ZIP_METHOD_REDUCE4;
378 		break;
379 		case deflate1 :
380 		case deflate2 :
381 			info.compression_method = ZIP_METHOD_DEFLATE;
382 			info.general_purpose_bit_flag |= ZIP_GEN_FLAGS_DEFLATE_SUPERFAST;
383 		break;
384 		case deflate3 :
385 		case deflate4 :
386 		case deflate5 :
387 			info.compression_method = ZIP_METHOD_DEFLATE;
388 			info.general_purpose_bit_flag |= ZIP_GEN_FLAGS_DEFLATE_FAST;
389 		break;
390 		case deflate6 :
391 		case deflate7 :
392 		case deflate8 :
393 			info.compression_method = ZIP_METHOD_DEFLATE;
394 			info.general_purpose_bit_flag |= ZIP_GEN_FLAGS_DEFLATE_NORMAL;
395 		break;
396 		case deflate9 :
397 			info.compression_method = ZIP_METHOD_DEFLATE;
398 			info.general_purpose_bit_flag |= ZIP_GEN_FLAGS_DEFLATE_MAXIMUM;
399 		break;
400 		case implode_4kdict_2tree :
401 			info.compression_method = ZIP_METHOD_IMPLODE;
402 			info.general_purpose_bit_flag |= ZIP_GEN_FLAGS_IMPLODE_4KD2T;
403 		break;
404 		case implode_8kdict_2tree :
405 			info.compression_method = ZIP_METHOD_IMPLODE;
406 			info.general_purpose_bit_flag |= ZIP_GEN_FLAGS_IMPLODE_8KD2T;
407 		break;
408 		case implode_4kdict_3tree :
409 			info.compression_method = ZIP_METHOD_IMPLODE;
410 			info.general_purpose_bit_flag |= ZIP_GEN_FLAGS_IMPLODE_4KD3T;
411 		break;
412 		case implode_8kdict_3tree :
413 			info.compression_method = ZIP_METHOD_IMPLODE;
414 			info.general_purpose_bit_flag |= ZIP_GEN_FLAGS_IMPLODE_8KD3T;
415 		break;
416 		case bzip2 :
417 			info.compression_method = ZIP_METHOD_BZIP2;
418 		break;
419 		case lzma :
420 			info.compression_method = ZIP_METHOD_LZMA;
421 		break;
422 		default :
423 			throw error_invalid() << "Compression method not supported";
424 	}
425 
426 	data_free(data);
427 	info.compressed_size = compsize;
428 	data = data_dup(compdata, info.compressed_size);
429 
430 	name_set(Aname);
431 
432 	data_free(local_extra_field);
433 	info.local_extra_field_length = 0;
434 	local_extra_field = 0;
435 
436 	data_free(central_extra_field);
437 	info.central_extra_field_length = 0;
438 	central_extra_field = 0;
439 
440 	data_free(file_comment);
441 	info.file_comment_length = 0;
442 	file_comment = 0;
443 }
444 
name_set(const string & Aname)445 void zip_entry::name_set(const string& Aname)
446 {
447 	data_free(file_name);
448 	info.filename_length = Aname.length();
449 	file_name = data_alloc(info.filename_length);
450 	memcpy(file_name, Aname.c_str(), info.filename_length);
451 }
452 
name_get() const453 string zip_entry::name_get() const
454 {
455 	return string(file_name, file_name + info.filename_length);
456 }
457 
458 /** Check central directory entry. */
check_cent(const unsigned char * buf,unsigned buf_size) const459 void zip_entry::check_cent(const unsigned char* buf, unsigned buf_size) const
460 {
461 	if (buf_size < ZIP_CO_FIXED) {
462 		throw error_invalid() << "Invalid central directory data";
463 	}
464 	// check signature
465 	if (le_uint32_read(buf+ZIP_CO_central_file_header_signature) != ZIP_C_signature) {
466 		throw error_invalid() << "Invalid central directory signature";
467 	}
468 	// check filename_length > 0, can't exist a file without a name
469 	if (le_uint16_read(buf+ZIP_CO_filename_length) == 0) {
470 		throw error_invalid() << "Empty filename in central directory";
471 	}
472 }
473 
474 /** Check local file header comparing with internal information. */
check_local(const unsigned char * buf) const475 void zip_entry::check_local(const unsigned char* buf) const
476 {
477 	if (le_uint32_read(buf+ZIP_LO_local_file_header_signature) != ZIP_L_signature) {
478 		throw error_invalid() << "Invalid signature in local header";
479 	}
480 	if (info.general_purpose_bit_flag != le_uint16_read(buf+ZIP_LO_general_purpose_bit_flag)) {
481 		throw error_invalid() << "Invalid local purpose bit flag " << info.general_purpose_bit_flag << "/" << le_uint16_read(buf+ZIP_LO_general_purpose_bit_flag);
482 	}
483 	if (info.compression_method != le_uint16_read(buf+ZIP_LO_compression_method)) {
484 		throw error_invalid() << "Invalid method on local header";
485 	}
486 	if ((le_uint16_read(buf+ZIP_LO_general_purpose_bit_flag) & ZIP_GEN_FLAGS_DEFLATE_ZERO) != 0) {
487 		if (zip::pedantic) {
488 			if (le_uint32_read(buf+ZIP_LO_crc32) != 0) {
489 				throw error_invalid() << "Not zero crc on local header " << le_uint32_read(buf+ZIP_LO_crc32);
490 			}
491 			if (le_uint32_read(buf+ZIP_LO_compressed_size) != 0) {
492 				throw error_invalid() << "Not zero compressed size in local header " << le_uint32_read(buf+ZIP_LO_compressed_size);
493 			}
494 			if (le_uint32_read(buf+ZIP_LO_uncompressed_size) != 0) {
495 				throw error_invalid() << "Not zero uncompressed size in local header " << le_uint32_read(buf+ZIP_LO_uncompressed_size);
496 			}
497 		} else {
498 			// allow the entries to have the correct value instead of 0
499 			if (le_uint32_read(buf+ZIP_LO_crc32) != 0 && info.crc32 != le_uint32_read(buf+ZIP_LO_crc32)) {
500 				throw error_invalid() << "Not zero crc on local header " << le_uint32_read(buf+ZIP_LO_crc32);
501 			}
502 			if (le_uint32_read(buf+ZIP_LO_compressed_size) != 0 && info.compressed_size != le_uint32_read(buf+ZIP_LO_compressed_size)) {
503 				throw error_invalid() << "Not zero compressed size in local header " << le_uint32_read(buf+ZIP_LO_compressed_size);
504 			}
505 			if (le_uint32_read(buf+ZIP_LO_uncompressed_size) != 0 && info.uncompressed_size != le_uint32_read(buf+ZIP_LO_uncompressed_size)) {
506 				throw error_invalid() << "Not zero uncompressed size in local header " << le_uint32_read(buf+ZIP_LO_uncompressed_size);
507 			}
508 		}
509 	} else {
510 		if (info.crc32 != le_uint32_read(buf+ZIP_LO_crc32)) {
511 			throw error_invalid() << "Invalid crc on local header " << info.crc32 << "/" << le_uint32_read(buf+ZIP_LO_crc32);
512 		}
513 		if (info.compressed_size != le_uint32_read(buf+ZIP_LO_compressed_size)) {
514 			throw error_invalid() << "Invalid compressed size in local header " << info.compressed_size << "/" << le_uint32_read(buf+ZIP_LO_compressed_size);
515 		}
516 		if (info.uncompressed_size != le_uint32_read(buf+ZIP_LO_uncompressed_size)) {
517 			throw error_invalid() << "Invalid uncompressed size in local header " << info.uncompressed_size << "/" << le_uint32_read(buf+ZIP_LO_uncompressed_size);
518 		}
519 	}
520 	if (info.filename_length != le_uint16_read(buf+ZIP_LO_filename_length)) {
521 		throw error_invalid() << "Invalid filename in local header";
522 	}
523 	if (info.local_extra_field_length != le_uint16_read(buf+ZIP_LO_extra_field_length)
524 		&& info.local_extra_field_length != 0 // the .zip generated with the info-zip program have the extra field only on the local header
525 	) {
526 		throw error_invalid() << "Invalid extra field length in local header " << info.local_extra_field_length << "/" << le_uint16_read(buf+ZIP_LO_extra_field_length);
527 	}
528 }
529 
check_descriptor(const unsigned char * buf) const530 void zip_entry::check_descriptor(const unsigned char* buf) const
531 {
532 	if (0x08074b50 != le_uint32_read(buf+ZIP_DO_header_signature)) {
533 		throw error_invalid() << "Invalid header signature on data descriptor " << le_uint32_read(buf+ZIP_DO_crc32);
534 	}
535 	if (info.crc32 != le_uint32_read(buf+ZIP_DO_crc32)) {
536 		throw error_invalid() << "Invalid crc on data descriptor " << info.crc32 << "/" << le_uint32_read(buf+ZIP_DO_crc32);
537 	}
538 	if (info.compressed_size != le_uint32_read(buf+ZIP_DO_compressed_size)
539 		// allow a 0 size, GNU unzip also allow it
540 		&& 0 != le_uint32_read(buf+ZIP_DO_compressed_size)) {
541 		throw error_invalid() << "Invalid compressed size in data descriptor " << info.compressed_size << "/" << le_uint32_read(buf+ZIP_DO_compressed_size);
542 	}
543 	if (info.uncompressed_size != le_uint32_read(buf+ZIP_DO_uncompressed_size)
544 		// allow a 0 size, GNU unzip also allow it
545 		&& 0 != le_uint32_read(buf+ZIP_DO_uncompressed_size)) {
546 		throw error_invalid() << "Invalid uncompressed size in data descriptor " << info.uncompressed_size << "/" << le_uint32_read(buf+ZIP_DO_uncompressed_size);
547 	}
548 }
549 
550 /** Unload compressed/uncomressed data. */
unload()551 void zip_entry::unload()
552 {
553 	data_free(data);
554 	data = 0;
555 }
556 
557 /**
558  * Load local file header.
559  * \param buf Fixed size local header.
560  * \param f File seeked after the fixed size local header.
561  */
load_local(const unsigned char * buf,FILE * f,unsigned size)562 void zip_entry::load_local(const unsigned char* buf, FILE* f, unsigned size)
563 {
564 	check_local(buf);
565 
566 	// use the local extra_field_length. It may be different than the
567 	// central directory version in some zips.
568 	unsigned local_extra_field_length = le_uint16_read(buf+ZIP_LO_extra_field_length);
569 
570 	if (size < info.filename_length + local_extra_field_length) {
571 		throw error_invalid() << "Overflow of filename";
572 	}
573 	size -= info.filename_length + local_extra_field_length;
574 
575 	// skip filename and extra field
576 	if (fseek(f, info.filename_length + local_extra_field_length, SEEK_CUR) != 0) {
577 		throw error_invalid() << "Failed seek";
578 	}
579 
580 	data_free(data);
581 	data = data_alloc(info.compressed_size);
582 
583 	if (size < info.compressed_size) {
584 		throw error_invalid() << "Overflow of compressed data";
585 	}
586 	size -= info.compressed_size;
587 
588 	try {
589 		if (info.compressed_size > 0) {
590 			if (fread(data, info.compressed_size, 1, f) != 1) {
591 				throw error() << "Failed read";
592 			}
593 		}
594 	} catch (...) {
595 		data_free(data);
596 		data = 0;
597 		throw;
598 	}
599 
600 	// load the data descriptor
601 	if ((le_uint16_read(buf+ZIP_LO_general_purpose_bit_flag) & ZIP_GEN_FLAGS_DEFLATE_ZERO) != 0) {
602 		unsigned char data_desc[ZIP_DO_FIXED];
603 		unsigned offset;
604 
605 		// handle the case of the ZIP_DO_header_signature missing
606 		if (size == ZIP_DO_FIXED - 4) {
607 			le_uint32_write(data_desc+ZIP_DO_header_signature, 0x08074b50);
608 			offset = ZIP_DO_crc32;
609 		} else {
610 			offset = 0;
611 		}
612 
613 		if (size < ZIP_DO_FIXED - offset) {
614 			throw error_invalid() << "Overflow of data descriptor";
615 		}
616 
617 		if (fread(data_desc + offset, ZIP_DO_FIXED - offset, 1, f) != 1) {
618 			throw error() << "Failed read";
619 		}
620 		size -= ZIP_DO_FIXED - offset;
621 
622 		check_descriptor(data_desc);
623 	}
624 }
625 
626 /**
627  * Save local file header.
628  * \param f File seeked at correct position.
629  */
save_local(FILE * f)630 void zip_entry::save_local(FILE* f)
631 {
632 	long offset = ftell(f);
633 
634 	if (offset<0)
635 		throw error() << "Failed tell";
636 
637 	info.relative_offset_of_local_header = offset;
638 
639 	// write header
640 	unsigned char buf[ZIP_LO_FIXED];
641 	le_uint32_write(buf+ZIP_LO_local_file_header_signature, ZIP_L_signature);
642 	le_uint8_write(buf+ZIP_LO_version_needed_to_extract, info.version_needed_to_extract);
643 	le_uint8_write(buf+ZIP_LO_os_needed_to_extract, info.os_needed_to_extract);
644 	// clear the "data descriptor" bit
645 	le_uint16_write(buf+ZIP_LO_general_purpose_bit_flag, info.general_purpose_bit_flag & ~ZIP_GEN_FLAGS_DEFLATE_ZERO);
646 	le_uint16_write(buf+ZIP_LO_compression_method, info.compression_method);
647 	le_uint16_write(buf+ZIP_LO_last_mod_file_time, info.last_mod_file_time);
648 	le_uint16_write(buf+ZIP_LO_last_mod_file_date, info.last_mod_file_date);
649 	le_uint32_write(buf+ZIP_LO_crc32, info.crc32);
650 	le_uint32_write(buf+ZIP_LO_compressed_size, info.compressed_size);
651 	le_uint32_write(buf+ZIP_LO_uncompressed_size, info.uncompressed_size);
652 	le_uint16_write(buf+ZIP_LO_filename_length, info.filename_length);
653 	le_uint16_write(buf+ZIP_LO_extra_field_length, info.local_extra_field_length);
654 
655 	if (fwrite(buf, ZIP_LO_FIXED, 1, f) != 1) {
656 		throw error() << "Failed write";
657 	}
658 
659 	// write filename
660 	if (fwrite(file_name, info.filename_length, 1, f) != 1) {
661 		throw error() << "Failed write";
662 	}
663 
664 	// write the extra field
665 	if (info.local_extra_field_length && fwrite(local_extra_field, info.local_extra_field_length, 1, f) != 1) {
666 		throw error() << "Failed write";
667 	}
668 
669 	// write data, directories don't have data
670 	if (info.compressed_size) {
671 		assert(data);
672 
673 		if (fwrite(data, info.compressed_size, 1, f) != 1) {
674 			throw error() << "Failed write";
675 		}
676 	}
677 }
678 
679 /**
680  * Load cent dir.
681  * \param buf Fixed size cent dir.
682  * \param f File seeked after the fixed size cent dir.
683  */
load_cent(const unsigned char * buf,unsigned buf_size,unsigned & skip)684 void zip_entry::load_cent(const unsigned char* buf, unsigned buf_size, unsigned& skip)
685 {
686 	const unsigned char* o_buf = buf;
687 
688 	check_cent(buf, buf_size);
689 
690 	// read header
691 	info.version_made_by = le_uint8_read(buf+ZIP_CO_version_made_by);
692 	info.host_os = le_uint8_read(buf+ZIP_CO_host_os);
693 	info.version_needed_to_extract = le_uint8_read(buf+ZIP_CO_version_needed_to_extract);
694 	info.os_needed_to_extract = le_uint8_read(buf+ZIP_CO_os_needed_to_extract);
695 	info.general_purpose_bit_flag = le_uint16_read(buf+ZIP_CO_general_purpose_bit_flag);
696 	info.compression_method = le_uint16_read(buf+ZIP_CO_compression_method);
697 	info.last_mod_file_time = le_uint16_read(buf+ZIP_CO_last_mod_file_time);
698 	info.last_mod_file_date = le_uint16_read(buf+ZIP_CO_last_mod_file_date);
699 	info.crc32 = le_uint32_read(buf+ZIP_CO_crc32);
700 	info.compressed_size = le_uint32_read(buf+ZIP_CO_compressed_size);
701 	info.uncompressed_size = le_uint32_read(buf+ZIP_CO_uncompressed_size);
702 	info.filename_length = le_uint16_read(buf+ZIP_CO_filename_length);
703 	info.central_extra_field_length = le_uint16_read(buf+ZIP_CO_extra_field_length);
704 	info.file_comment_length = le_uint16_read(buf+ZIP_CO_file_comment_length);
705 	info.internal_file_attrib = le_uint16_read(buf+ZIP_CO_internal_file_attrib);
706 	info.external_file_attrib = le_uint32_read(buf+ZIP_CO_external_file_attrib);
707 	info.relative_offset_of_local_header = le_uint32_read(buf+ZIP_CO_relative_offset_of_local_header);
708 	buf += ZIP_CO_FIXED;
709 
710 	if (buf_size < info.filename_length
711 		|| buf_size < info.central_extra_field_length
712 		|| buf_size < info.file_comment_length
713 		|| buf_size < ZIP_CO_FIXED + info.filename_length + info.central_extra_field_length + info.file_comment_length
714 	) {
715 		throw error_invalid() << "Invalid central directory data";
716 	}
717 
718 	// read filename
719 	data_free(file_name);
720 	file_name = data_alloc(info.filename_length);
721 	memcpy(file_name, buf, info.filename_length);
722 	buf += info.filename_length;
723 
724 	// read extra field
725 	data_free(central_extra_field);
726 	central_extra_field = data_dup(buf, info.central_extra_field_length);
727 	buf += info.central_extra_field_length;
728 
729 	// read comment
730 	data_free(file_comment);
731 	file_comment = data_dup(buf, info.file_comment_length);
732 	buf += info.file_comment_length;
733 
734 	skip = buf - o_buf;
735 }
736 
737 /**
738  * Save cent dir.
739  * \param f File seeked at correct position.
740  */
save_cent(FILE * f)741 void zip_entry::save_cent(FILE* f)
742 {
743 	unsigned char buf[ZIP_CO_FIXED];
744 
745 	le_uint32_write(buf+ZIP_CO_central_file_header_signature, ZIP_C_signature);
746 	le_uint8_write(buf+ZIP_CO_version_made_by, info.version_made_by);
747 	le_uint8_write(buf+ZIP_CO_host_os, info.host_os);
748 	le_uint8_write(buf+ZIP_CO_version_needed_to_extract, info.version_needed_to_extract);
749 	le_uint8_write(buf+ZIP_CO_os_needed_to_extract, info.os_needed_to_extract);
750 	// clear the "data descriptor" bit
751 	le_uint16_write(buf+ZIP_CO_general_purpose_bit_flag, info.general_purpose_bit_flag & ~ZIP_GEN_FLAGS_DEFLATE_ZERO);
752 	le_uint16_write(buf+ZIP_CO_compression_method, info.compression_method);
753 	le_uint16_write(buf+ZIP_CO_last_mod_file_time, info.last_mod_file_time);
754 	le_uint16_write(buf+ZIP_CO_last_mod_file_date, info.last_mod_file_date);
755 	le_uint32_write(buf+ZIP_CO_crc32, info.crc32);
756 	le_uint32_write(buf+ZIP_CO_compressed_size, info.compressed_size);
757 	le_uint32_write(buf+ZIP_CO_uncompressed_size, info.uncompressed_size);
758 	le_uint16_write(buf+ZIP_CO_filename_length, info.filename_length);
759 	le_uint16_write(buf+ZIP_CO_extra_field_length, info.central_extra_field_length);
760 	le_uint16_write(buf+ZIP_CO_file_comment_length, info.file_comment_length);
761 	le_uint16_write(buf+ZIP_CO_disk_number_start, ZIP_UNIQUE_DISK);
762 	le_uint16_write(buf+ZIP_CO_internal_file_attrib, info.internal_file_attrib);
763 	le_uint32_write(buf+ZIP_CO_external_file_attrib, info.external_file_attrib);
764 	le_uint32_write(buf+ZIP_CO_relative_offset_of_local_header, info.relative_offset_of_local_header);
765 
766 	if (fwrite(buf, ZIP_CO_FIXED, 1, f) != 1) {
767 		throw error() << "Failed write";
768 	}
769 
770 	// write filename
771 	if (fwrite(file_name, info.filename_length, 1, f) != 1) {
772 		throw error() << "Failed write";
773 	}
774 
775 	// write extra field
776 	if (info.central_extra_field_length && fwrite(central_extra_field, info.central_extra_field_length, 1, f) != 1) {
777 		throw error() << "Failed write";
778 	}
779 
780 	// write comment
781 	if (info.file_comment_length && fwrite(file_comment, info.file_comment_length, 1, f) != 1) {
782 		throw error() << "Failed write";
783 	}
784 }
785 
zip(const std::string & Apath)786 zip::zip(const std::string& Apath) : path(Apath)
787 {
788 	flag.open = false;
789 	flag.read = false;
790 	flag.modify = false;
791 	zipfile_comment = 0;
792 }
793 
zip(const zip & A)794 zip::zip(const zip& A) : map(A.map), path(A.path)
795 {
796 	flag = A.flag;
797 	info = A.info;
798 	zipfile_comment = data_dup(A.zipfile_comment, A.info.zipfile_comment_length);
799 }
800 
~zip()801 zip::~zip()
802 {
803 	if (is_open())
804 		close();
805 }
806 
create()807 void zip::create()
808 {
809 	assert(!flag.open);
810 
811 	info.offset_to_start_of_cent_dir = 0;
812 	info.zipfile_comment_length = 0;
813 	data_free(zipfile_comment);
814 	zipfile_comment = 0;
815 
816 	flag.read = true;
817 	flag.open = true;
818 	flag.modify = false;
819 }
820 
open()821 void zip::open()
822 {
823 	assert(!flag.open);
824 
825 	struct stat s;
826 	if (stat(path.c_str(), &s) != 0) {
827 		if (errno != ENOENT)
828 			throw error() << "Failed stat";
829 
830 		// create the file if it's missing
831 		create();
832 		return;
833 	}
834 
835 	unsigned length = s.st_size;
836 
837 	// open file
838 	FILE* f = fopen(path.c_str(), "rb");
839 	if (!f)
840 		throw error() << "Failed open for reading";
841 
842 	unsigned char* data = 0;
843 	unsigned data_size = 0;
844 
845 	try {
846 		if (!cent_read(f, length, data, data_size))
847 			throw error_invalid() << "Failed read end of central directory";
848 	} catch (...) {
849 		fclose(f);
850 		throw;
851 	}
852 
853 	fclose(f);
854 
855 	// position in data
856 	unsigned data_pos = 0;
857 
858 	try {
859 		// central dir
860 		while (le_uint32_read(data+data_pos) == ZIP_C_signature) {
861 
862 			iterator i = map.insert(map.end(), zip_entry(path));
863 
864 			unsigned skip = 0;
865 			try {
866 				i->load_cent(data + data_pos, data_size - data_pos, skip);
867 			} catch (...) {
868 				map.erase(i);
869 				throw;
870 			}
871 
872 			data_pos += skip;
873 		}
874 
875 		// end of central dir
876 		if (le_uint32_read(data+data_pos) != ZIP_E_signature)
877 			throw error_invalid() << "Invalid end of central dir signature";
878 
879 		info.offset_to_start_of_cent_dir = le_uint32_read(data+data_pos+ZIP_EO_offset_to_start_of_cent_dir);
880 		info.zipfile_comment_length = le_uint16_read(data+data_pos+ZIP_EO_zipfile_comment_length);
881 		data_pos += ZIP_EO_FIXED;
882 
883 		if (info.offset_to_start_of_cent_dir != length - data_size)
884 			throw error_invalid() << "Invalid end of central directory start address";
885 
886 		// comment
887 		data_free(zipfile_comment);
888 		zipfile_comment = data_dup(data+data_pos, info.zipfile_comment_length);
889 		data_pos += info.zipfile_comment_length;
890 
891 	} catch (...) {
892 		data_free(data);
893 		throw;
894 	}
895 
896 	// delete cent data
897 	data_free(data);
898 
899 	if (pedantic) {
900 		// don't accept garbage at the end of file
901 		if (data_pos != data_size)
902 			throw error_invalid() << data_size - data_pos << " unused bytes at the end of the central directory";
903 	}
904 
905 	flag.open = true;
906 	flag.read = false;
907 	flag.modify = false;
908 }
909 
910 /**
911  * Close a zip file.
912  */
close()913 void zip::close()
914 {
915 	// deinizialize
916 	flag.open = false;
917 	flag.read = false;
918 	flag.modify = false;
919 	data_free(zipfile_comment);
920 	zipfile_comment = 0;
921 	path = "";
922 	map.erase(map.begin(), map.end());
923 }
924 
925 /**
926  * Discarge compressed data read.
927  * \note You cannot call unload() if zip is modified, you must call reopen().
928  */
unload()929 void zip::unload()
930 {
931 	assert(flag.open && flag.read && !flag.modify);
932 
933 	for(iterator i=begin();i!=end();++i)
934 		i->unload();
935 
936 	flag.read = false;
937 }
938 
939 /**
940  * Reopen from zip on disk, lose any modify.
941  * \note Equivalent close and open.
942  */
reopen()943 void zip::reopen()
944 {
945 	assert(flag.open);
946 
947 	close();
948 	open();
949 }
950 
951 /**
952  * Load a zip file.
953  */
load()954 void zip::load()
955 {
956 	assert(flag.open && !flag.read);
957 
958 	flag.modify = false;
959 
960 	FILE* f = fopen(path.c_str(), "rb");
961 	if (!f)
962 		throw error() << "Failed open for reading";
963 
964 	try {
965 		long offset = 0;
966 		unsigned count = 0;
967 
968 		while (static_cast<unsigned long>(offset) < info.offset_to_start_of_cent_dir) {
969 			unsigned char buf[ZIP_LO_FIXED];
970 
971 			// search the next item, assume entries in random order
972 			iterator next;
973 			bool next_set = false;
974 			for(iterator i=begin();i!=end();++i) {
975 				if (i->offset_get() >= static_cast<unsigned long>(offset)) {
976 					if (!next_set || i->offset_get() < next->offset_get()) {
977 						next_set = true;
978 						next = i;
979 					}
980 				}
981 			}
982 
983 			// if not found exit
984 			if (!next_set) {
985 				if (pedantic)
986 					throw error_invalid() << info.offset_to_start_of_cent_dir - static_cast<unsigned long>(offset) << " unused bytes after the last local header at offset " << offset;
987 				else
988 					break;
989 			}
990 
991 			// check for invalid start
992 			if (next->offset_get() >= info.offset_to_start_of_cent_dir) {
993 				throw error_invalid() << "Overflow in central directory";
994 			}
995 
996 			// check for a data hole
997 			if (next->offset_get() > static_cast<unsigned long>(offset)) {
998 				if (pedantic)
999 					throw error_invalid() << next->offset_get() - static_cast<unsigned long>(offset) << " unused bytes at offset " << offset;
1000 				else {
1001 					// set the correct position
1002 					if (fseek(f, next->offset_get(), SEEK_SET) != 0)
1003 						throw error() << "Failed fseek";
1004 					offset = next->offset_get();
1005 				}
1006 			}
1007 
1008 			// search the next item, assume entries in random order
1009 			iterator next_next;
1010 			bool next_next_set = false;
1011 			for(iterator i=begin();i!=end();++i) {
1012 				if (i->offset_get() > static_cast<unsigned long>(offset)) {
1013 					if (!next_next_set || i->offset_get() < next_next->offset_get()) {
1014 						next_next_set = true;
1015 						next_next = i;
1016 					}
1017 				}
1018 			}
1019 
1020 			unsigned long end_offset;
1021 			if (next_next_set)
1022 				end_offset = next_next->offset_get();
1023 			else
1024 				end_offset = info.offset_to_start_of_cent_dir;
1025 
1026 			if (end_offset < next->offset_get() + ZIP_LO_FIXED) {
1027 				throw error_invalid() << "Invalid local header size at offset " << offset;
1028 			}
1029 
1030 			if (fread(buf, ZIP_LO_FIXED, 1, f) != 1)
1031 				throw error() << "Failed read";
1032 
1033 			next->load_local(buf, f, end_offset - next->offset_get() - ZIP_LO_FIXED);
1034 
1035 			++count;
1036 
1037 			offset = ftell(f);
1038 			if (offset < 0)
1039 				throw error() << "Failed tell";
1040 		}
1041 
1042 		if (static_cast<unsigned long>(offset) != info.offset_to_start_of_cent_dir) {
1043 			if (pedantic)
1044 				throw error_invalid() << "Invalid central directory start";
1045 		}
1046 
1047 		if (count != size())
1048 			throw error_invalid() << "Invalid central directory, expected " << size() << " local headers, got " << count;
1049 
1050 	} catch (...) {
1051 		fclose(f);
1052 		throw;
1053 	}
1054 
1055 	fclose(f);
1056 
1057 	flag.read = true;
1058 }
1059 
size_not_zero() const1060 unsigned zip::size_not_zero() const
1061 {
1062 	unsigned count = 0;
1063 
1064 	// check if it contains at least one file with size > 0 (directories are excluded)
1065 	for(const_iterator i=begin();i!=end();++i) {
1066 		if (i->uncompressed_size_get() > 0) {
1067 			++count;
1068 		}
1069 	}
1070 
1071 	return count;
1072 }
1073 
1074 /**
1075  * Save a zip file.
1076  */
save()1077 void zip::save()
1078 {
1079 	assert(flag.open && flag.read);
1080 
1081 	flag.modify = false;
1082 
1083 	if (!empty()) {
1084 		// prevent external signal
1085 		sig_auto_lock sal;
1086 
1087 		// temp name of the saved file
1088 		string save_path = file_temp(path);
1089 
1090 		FILE* f = fopen(save_path.c_str(), "wb");
1091 		if (!f)
1092 			throw error() << "Failed open for writing of " << save_path;
1093 
1094 		try {
1095 			// write local header
1096 			for(iterator i=begin();i!=end();++i)
1097 				i->save_local(f);
1098 
1099 			long cent_offset = ftell(f);
1100 			if (cent_offset<0)
1101 				throw error() << "Failed tell";
1102 
1103 			// new cent start
1104 			info.offset_to_start_of_cent_dir = cent_offset;
1105 
1106 			// write cent dir
1107 			for(iterator i=begin();i!=end();++i)
1108 				i->save_cent(f);
1109 
1110 			long end_cent_offset = ftell(f);
1111 			if (end_cent_offset<0)
1112 				throw error() << "Failed tell";
1113 
1114 			// write end of cent dir
1115 			unsigned char buf[ZIP_EO_FIXED];
1116 			le_uint32_write(buf+ZIP_EO_end_of_central_dir_signature, ZIP_E_signature);
1117 			le_uint16_write(buf+ZIP_EO_number_of_this_disk, ZIP_UNIQUE_DISK);
1118 			le_uint16_write(buf+ZIP_EO_number_of_disk_start_cent_dir, ZIP_UNIQUE_DISK);
1119 			le_uint16_write(buf+ZIP_EO_total_entries_cent_dir_this_disk, size());
1120 			le_uint16_write(buf+ZIP_EO_total_entries_cent_dir, size());
1121 			le_uint32_write(buf+ZIP_EO_size_of_cent_dir, end_cent_offset - cent_offset);
1122 			le_uint32_write(buf+ZIP_EO_offset_to_start_of_cent_dir, cent_offset);
1123 			le_uint16_write(buf+ZIP_EO_zipfile_comment_length, info.zipfile_comment_length);
1124 
1125 			if (fwrite(buf, ZIP_EO_FIXED, 1, f) != 1)
1126 				throw error() << "Failed write";
1127 
1128 			// write comment
1129 			if (info.zipfile_comment_length && fwrite(zipfile_comment, info.zipfile_comment_length, 1, f) != 1)
1130 				throw error() << "Failed write";
1131 
1132 		} catch (...) {
1133 			fclose(f);
1134 			remove(save_path.c_str());
1135 			throw;
1136 		}
1137 
1138 		fclose(f);
1139 
1140 		// delete the file if exists
1141 		if (access(path.c_str(), F_OK) == 0) {
1142 			if (remove(path.c_str()) != 0) {
1143 				remove(save_path.c_str());
1144 				throw error() << "Failed delete of " << path;
1145 			}
1146 		}
1147 
1148 		// rename the new version with the correct name
1149 		if (::rename(save_path.c_str(), path.c_str()) != 0) {
1150 			throw error() << "Failed rename of " << save_path << " to " << path;
1151 		}
1152 
1153 	} else {
1154 		// reset the cent start
1155 		info.offset_to_start_of_cent_dir = 0;
1156 
1157 		// delete the file if exists
1158 		if (access(path.c_str(), F_OK) == 0) {
1159 			if (remove(path.c_str()) != 0)
1160 				throw error() << "Failed delete of " << path;
1161 		}
1162 	}
1163 }
1164 
1165 /**
1166  * Remove a compressed file.
1167  */
erase(iterator i)1168 void zip::erase(iterator i)
1169 {
1170 	assert(flag.read);
1171 
1172 	flag.modify = true;
1173 
1174 	map.erase(i);
1175 }
1176 
1177 /**
1178  * Rename a compressed file.
1179  * \note No filename overwrite check.
1180  */
rename(iterator i,const string & Aname)1181 void zip::rename(iterator i, const string& Aname)
1182 {
1183 	assert(flag.read);
1184 
1185 	flag.modify = true;
1186 
1187 	i->name_set(Aname);
1188 }
1189 
1190 /**
1191  * Insert a zip entry.
1192  */
insert(const zip_entry & A,const string & Aname)1193 zip::iterator zip::insert(const zip_entry& A, const string& Aname)
1194 {
1195 	iterator i;
1196 
1197 	assert(flag.read);
1198 
1199 	unsigned char* data = data_alloc(A.compressed_size_get());
1200 	assert(data);
1201 
1202 	try {
1203 		A.compressed_read(data);
1204 
1205 		i = map.insert(map.end(), zip_entry(path));
1206 
1207 		try {
1208 			i->set(A.method_get(), Aname, data, A.compressed_size_get(), A.uncompressed_size_get(), A.crc_get(), A.zipdate_get(), A.ziptime_get(), A.is_text());
1209 		} catch (...) {
1210 			map.erase(i);
1211 			throw;
1212 		}
1213 
1214 		flag.modify = true;
1215 
1216 	} catch (...) {
1217 		data_free(data);
1218 		throw;
1219 	}
1220 
1221 	data_free(data);
1222 
1223 	return i;
1224 }
1225 
1226 /**
1227  * Add data to a zip file.
1228  * The data is deflated or stored. No filename overwrite check.
1229  */
insert_uncompressed(const string & Aname,const unsigned char * data,unsigned size,unsigned crc,time_t tod,bool is_text)1230 zip::iterator zip::insert_uncompressed(const string& Aname, const unsigned char* data, unsigned size, unsigned crc, time_t tod, bool is_text)
1231 {
1232 	iterator i;
1233 
1234 	assert(flag.read);
1235 	assert(crc == crc32(0, (const unsigned char*)data, size));
1236 
1237 	unsigned date = 0;
1238 	unsigned time = 0;
1239 	time2zip(tod, date, time);
1240 
1241 	i = map.insert(map.end(), zip_entry(path));
1242 
1243 	try {
1244 		i->set(zip_entry::store, Aname, data, size, size, crc, date, time, is_text);
1245 	} catch (...) {
1246 		map.erase(i);
1247 		throw;
1248 	}
1249 
1250 	flag.modify = true;
1251 
1252 	return i;
1253 }
1254 
1255