The SWORD Project  1.9.0.svnversion
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
zipcomprs.cpp
Go to the documentation of this file.
1 /******************************************************************************
2  *
3  * zipcomprs.cpp - ZipCompress, a driver class that provides zlib
4  * compression
5  *
6  * $Id: zipcomprs.cpp 3837 2020-12-26 18:26:12Z scribe $
7  *
8  * Copyright 2000-2014 CrossWire Bible Society (http://www.crosswire.org)
9  * CrossWire Bible Society
10  * P. O. Box 2528
11  * Tempe, AZ 85280-2528
12  *
13  * This program is free software; you can redistribute it and/or modify it
14  * under the terms of the GNU General Public License as published by the
15  * Free Software Foundation version 2.
16  *
17  * This program is distributed in the hope that it will be useful, but
18  * WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20  * General Public License for more details.
21  *
22  */
23 
24 
25 #include <stdlib.h>
26 #include <string.h>
27 #include <filemgr.h>
28 #include <swlog.h>
29 #include <zipcomprs.h>
30 #include <zlib.h>
31 #include <utilstr.h>
32 #ifndef WIN32
33 #include <utime.h>
34 #else
35 #include <windows.h>
36 #include <time.h>
37 #endif
38 extern "C" {
39 #include "zlib.h"
40 }
41 
42 /* This untar code is largely lifted from zlib untgz.c
43  * written by "Pedro A. Aranda Gutirrez" <paag@tid.es>
44  * adaptation to Unix by Jean-loup Gailly <jloup@gzip.org>
45  * For conditions of distribution and use,
46  * see copyright code / notice / discussion at:
47  * https://github.com/madler/zlib/blob/master/contrib/untgz/untgz.c
48  * https://github.com/madler/zlib/issues/531
49  */
50 namespace {
51 
52 
53 #define BLOCKSIZE 512
54 #define REGTYPE '0' /* regular file */
55 #define AREGTYPE '\0' /* regular file */
56 #define DIRTYPE '5' /* directory */
57 
58 
59 struct tar_header { /* byte offset */
60  char name[100]; /* 0 */
61  char mode[8]; /* 100 */
62  char uid[8]; /* 108 */
63  char gid[8]; /* 116 */
64  char size[12]; /* 124 */
65  char mtime[12]; /* 136 */
66  char chksum[8]; /* 148 */
67  char typeflag; /* 156 */
68  char linkname[100]; /* 157 */
69  char magic[6]; /* 257 */
70  char version[2]; /* 263 */
71  char uname[32]; /* 265 */
72  char gname[32]; /* 297 */
73  char devmajor[8]; /* 329 */
74  char devminor[8]; /* 337 */
75  char prefix[155]; /* 345 */
76  /* 500 */
77 };
78 
79 
80 union tar_buffer {
81  char buffer[BLOCKSIZE];
82  struct tar_header header;
83 };
84 
85 
86 int getoct(char *p, int width) {
87  int result = 0;
88  char c;
89 
90  while (width--) {
91  c = *p++;
92  if (c == ' ') continue;
93  if (c == 0 ) break;
94  result = result * 8 + (c - '0');
95  }
96  return result;
97 }
98 
99 
100 int untar(gzFile in, const char *dest) {
101  union tar_buffer buffer;
102  int len;
103  int err;
104  int getheader = 1;
105  int remaining = 0;
106  int outFD = 0;
107  sword::SWBuf fname;
108  time_t tartime;
109 
110  while (1) {
111  len = gzread(in, &buffer, BLOCKSIZE);
112  if (len < 0)
113  sword::SWLog::getSystemLog()->logError(gzerror(in, &err));
114  /*
115  * Always expect complete blocks to process
116  * the tar information.
117  */
118  if (len != BLOCKSIZE)
119  sword::SWLog::getSystemLog()->logError("gzread: incomplete block read");
120 
121  /*
122  * If we have to get a tar header
123  */
124  if (getheader == 1) {
125  /*
126  * if we met the end of the tar
127  * or the end-of-tar block,
128  * we are done
129  */
130  if ((len == 0) || (buffer.header.name[0]== 0)) break;
131 
132  tartime = (time_t)getoct(buffer.header.mtime,12);
133  fname = dest;
134  if (!fname.endsWith("/") && !fname.endsWith("\\")) fname += '/';
135  fname += buffer.header.name;
136 
137  switch (buffer.header.typeflag) {
138  case DIRTYPE: {
139  sword::SWBuf dummyFile = fname + "dummyFile";
140  sword::FileMgr::createParent(dummyFile);
141  break;
142  }
143  case REGTYPE:
144  case AREGTYPE:
145  remaining = getoct(buffer.header.size,12);
146  if (remaining) {
147  outFD = sword::FileMgr::createPathAndFile(fname);
148  }
149  else {
150  if (outFD > 0) {
151  sword::FileMgr::closeFile(outFD);
152  outFD = 0;
153  }
154  }
155  /*
156  * could have no contents
157  */
158  getheader = (remaining) ? 0 : 1;
159  break;
160  default:
161  break;
162  }
163  }
164  else {
165  unsigned int bytes = (remaining > BLOCKSIZE) ? BLOCKSIZE : remaining;
166 
167  if (outFD > 0) {
168  if (sword::FileMgr::write(outFD, &buffer,sizeof(char)*bytes) != (int) bytes) {
169  sword::SWLog::getSystemLog()->logError("error writing %s skipping...", fname.c_str());
170  sword::FileMgr::closeFile(outFD);
171  sword::FileMgr::removeFile(fname);
172  }
173  }
174  remaining -= bytes;
175  if (remaining == 0) {
176  getheader = 1;
177  if (outFD > 0) {
178 
179  // All this logic is simply the set the file timestamp
180  // ugh
181  sword::FileMgr::closeFile(outFD);
182 #ifdef WIN32
183  HANDLE hFile;
184  FILETIME ftm,ftLocal;
185  SYSTEMTIME st;
186  struct tm localt;
187 
188  localt = *localtime(&tartime);
189 
190  hFile = CreateFileW((const wchar_t *)sword::utf8ToWChar(fname).getRawData(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
191 
192  st.wYear = (WORD)localt.tm_year+1900;
193  st.wMonth = (WORD)localt.tm_mon;
194  st.wDayOfWeek = (WORD)localt.tm_wday;
195  st.wDay = (WORD)localt.tm_mday;
196  st.wHour = (WORD)localt.tm_hour;
197  st.wMinute = (WORD)localt.tm_min;
198  st.wSecond = (WORD)localt.tm_sec;
199  st.wMilliseconds = 0;
200  SystemTimeToFileTime(&st,&ftLocal);
201  LocalFileTimeToFileTime(&ftLocal,&ftm);
202  SetFileTime(hFile,&ftm,NULL,&ftm);
203  CloseHandle(hFile);
204 #else
205  struct utimbuf settime;
206  settime.actime = settime.modtime = tartime;
207  utime(fname.c_str(), &settime);
208 #endif
209  outFD = 0;
210  }
211  }
212  }
213  }
214  return 0;
215 }
216 
217 } // end anon namespace
218 
220 
221 /******************************************************************************
222  * ZipCompress Constructor - Initializes data for instance of ZipCompress
223  *
224  */
225 
227 {
228 // fprintf(stderr, "init compress\n");
230 }
231 
232 
233 /******************************************************************************
234  * ZipCompress Destructor - Cleans up instance of ZipCompress
235  */
236 
238 }
239 
240 
241 /******************************************************************************
242  * ZipCompress::encode - This function "encodes" the input stream into the
243  * output stream.
244  * The getChars() and sendChars() functions are
245  * used to separate this method from the actual
246  * i/o.
247  * NOTE: must set zlen for parent class to know length of
248  * compressed buffer.
249  */
250 
252 {
253 /*
254 ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen,
255  const Bytef *source, uLong sourceLen,
256  int level));
257 
258  Compresses the source buffer into the destination buffer. The level
259  parameter has the same meaning as in deflateInit. sourceLen is the byte
260  length of the source buffer. Upon entry, destLen is the total size of the
261  destination buffer, which must be at least the value returned by
262  compressBound(sourceLen). Upon exit, destLen is the actual size of the
263  compressed buffer.
264 
265  compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
266  memory, Z_BUF_ERROR if there was not enough room in the output buffer,
267  Z_STREAM_ERROR if the level parameter is invalid.
268 */
269  direct = 0; // set direction needed by parent [get|send]Chars()
270 
271  // get buffer
272  char chunk[1024];
273  char *buf = (char *)calloc(1, 1024);
274  char *chunkbuf = buf;
275  unsigned long chunklen;
276  unsigned long len = 0;
277  while((chunklen = getChars(chunk, 1023))) {
278  memcpy(chunkbuf, chunk, chunklen);
279  len += chunklen;
280  if (chunklen < 1023)
281  break;
282  else buf = (char *)realloc(buf, len + 1024);
283  chunkbuf = buf+len;
284  }
285 
286 
287  zlen = (long) (len*1.001)+15;
288  char *zbuf = new char[zlen+1];
289  if (len) {
290  //printf("Doing compress\n");
291  if (compress2((Bytef*)zbuf, &zlen, (const Bytef*)buf, len, level) != Z_OK) {
292  SWLog::getSystemLog()->logError("ERROR in compression");
293  }
294  else {
295  sendChars(zbuf, zlen);
296  }
297  }
298  else {
299  SWLog::getSystemLog()->logError("ERROR: no buffer to compress");
300  }
301  delete [] zbuf;
302  free(buf);
303 }
304 
305 
306 /******************************************************************************
307  * ZipCompress::decode - This function "decodes" the input stream into the
308  * output stream.
309  * The getChars() and sendChars() functions are
310  * used to separate this method from the actual
311  * i/o.
312  */
313 
315 {
316 /*
317 ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,
318  const Bytef *source, uLong sourceLen));
319  Decompresses the source buffer into the destination buffer. sourceLen is
320  the byte length of the source buffer. Upon entry, destLen is the total
321  size of the destination buffer, which must be large enough to hold the
322  entire uncompressed data. (The size of the uncompressed data must have
323  been saved previously by the compressor and transmitted to the decompressor
324  by some mechanism outside the scope of this compression library.)
325  Upon exit, destLen is the actual size of the compressed buffer.
326  This function can be used to decompress a whole file at once if the
327  input file is mmap'ed.
328 
329  uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
330  enough memory, Z_BUF_ERROR if there was not enough room in the output
331  buffer, or Z_DATA_ERROR if the input data was corrupted.
332 */
333  direct = 1; // set direction needed by parent [get|send]Chars()
334 
335  // get buffer
336  char chunk[1024];
337  char *zbuf = (char *)calloc(1, 1024);
338  char *chunkbuf = zbuf;
339  int chunklen;
340  unsigned long zlen = 0;
341  while((chunklen = (int)getChars(chunk, 1023))) {
342  memcpy(chunkbuf, chunk, chunklen);
343  zlen += chunklen;
344  if (chunklen < 1023)
345  break;
346  else zbuf = (char *)realloc(zbuf, zlen + 1024);
347  chunkbuf = zbuf + zlen;
348  }
349 
350  //printf("Decoding complength{%ld} uncomp{%ld}\n", zlen, blen);
351  if (zlen) {
352  unsigned long blen = zlen*20; // trust compression is less than 1000%
353  char *buf = new char[blen];
354  //printf("Doing decompress {%s}\n", zbuf);
355  slen = 0;
356  switch (uncompress((Bytef*)buf, &blen, (Bytef*)zbuf, zlen)){
357  case Z_OK: sendChars(buf, blen); slen = blen; break;
358  case Z_MEM_ERROR: SWLog::getSystemLog()->logError("ERROR: not enough memory during decompression."); break;
359  case Z_BUF_ERROR: SWLog::getSystemLog()->logError("ERROR: not enough room in the out buffer during decompression."); break;
360  case Z_DATA_ERROR: SWLog::getSystemLog()->logError("ERROR: corrupt data during decompression."); break;
361  default: SWLog::getSystemLog()->logError("ERROR: an unknown error occurred during decompression."); break;
362  }
363  delete [] buf;
364  }
365  else {
366  SWLog::getSystemLog()->logError("ERROR: no buffer to decompress!");
367  }
368  //printf("Finished decoding\n");
369  free (zbuf);
370 }
371 
372 
373 char ZipCompress::unTarGZ(int fd, const char *destPath) {
374  gzFile f;
375 
376  f = gzdopen(fd, "rb");
377  if (f == NULL) {
378  SWLog::getSystemLog()->logError("Couldn't gzopen file");
379  return 1;
380  }
381 
382  return untar(f, destPath);
383 }
384 
385 
static char unTarGZ(int fd, const char *destPath)
Definition: zipcomprs.cpp:373
#define SWORD_NAMESPACE_START
Definition: defs.h:39
#define BLOCKSIZE
Definition: zipcomprs.cpp:53
static SWLog * getSystemLog()
Definition: swlog.cpp:53
virtual ~ZipCompress()
Definition: zipcomprs.cpp:237
virtual void encode(void)
Definition: zipcomprs.cpp:251
unsigned long zlen
Definition: swcomprs.h:39
preg buffer
Definition: regex.c:8089
SWBuf utf8ToWChar(const char *buf)
Definition: utilstr.cpp:239
#define REGTYPE
Definition: zipcomprs.cpp:54
unsigned long slen
Definition: swcomprs.h:39
unsigned char FAR Bytef
Definition: zconf.h:223
voidp gzFile
Definition: zlib.h:657
virtual void decode(void)
Definition: zipcomprs.cpp:314
#define AREGTYPE
Definition: zipcomprs.cpp:55
int level
Definition: swcomprs.h:40
return NULL
Definition: regex.c:7953
free(preg->fastmap)
char * realloc()
#define Z_DATA_ERROR
Definition: zlib.h:137
char * buf
Definition: swcomprs.h:38
#define DIRTYPE
Definition: zipcomprs.cpp:56
#define Z_MEM_ERROR
Definition: zlib.h:138
int size
Definition: regex.c:5043
virtual unsigned long getChars(char *buf, unsigned long len)
Definition: swcomprs.cpp:121
char direct
Definition: swcomprs.h:38
char * zbuf
Definition: swcomprs.h:38
void logError(const char *fmt,...) const
Definition: swlog.cpp:87
#define Z_BUF_ERROR
Definition: zlib.h:139
#define Z_OK
Definition: zlib.h:132
result
Definition: regex.c:5545
#define SWORD_NAMESPACE_END
Definition: defs.h:40
virtual unsigned long sendChars(char *buf, unsigned long len)
Definition: swcomprs.cpp:141
#define Z_DEFAULT_COMPRESSION
Definition: zlib.h:148