xref: /sqlite-3.40.0/ext/misc/zipfile.c (revision d5c35f6d)
19ebfaad2Sdan /*
29ebfaad2Sdan ** 2017-12-26
39ebfaad2Sdan **
49ebfaad2Sdan ** The author disclaims copyright to this source code.  In place of
59ebfaad2Sdan ** a legal notice, here is a blessing:
69ebfaad2Sdan **
79ebfaad2Sdan **    May you do good and not evil.
89ebfaad2Sdan **    May you find forgiveness for yourself and forgive others.
99ebfaad2Sdan **    May you share freely, never taking more than you give.
109ebfaad2Sdan **
119ebfaad2Sdan ******************************************************************************
129ebfaad2Sdan **
13e4185bdaSdrh ** This file implements a virtual table for reading and writing ZIP archive
14e4185bdaSdrh ** files.
15e4185bdaSdrh **
16e4185bdaSdrh ** Usage example:
17e4185bdaSdrh **
18e4185bdaSdrh **     SELECT name, sz, datetime(mtime,'unixepoch') FROM zipfile($filename);
19e4185bdaSdrh **
20e4185bdaSdrh ** Current limitations:
21e4185bdaSdrh **
22e4185bdaSdrh **    *  No support for encryption
23e4185bdaSdrh **    *  No support for ZIP archives spanning multiple files
24e4185bdaSdrh **    *  No support for zip64 extensions
25e4185bdaSdrh **    *  Only the "inflate/deflate" (zlib) compression method is supported
269ebfaad2Sdan */
279ebfaad2Sdan #include "sqlite3ext.h"
289ebfaad2Sdan SQLITE_EXTENSION_INIT1
299ebfaad2Sdan #include <stdio.h>
309ebfaad2Sdan #include <string.h>
319ebfaad2Sdan #include <assert.h>
329ebfaad2Sdan 
33373dc3bbSdan #include <zlib.h>
34373dc3bbSdan 
359ebfaad2Sdan #ifndef SQLITE_OMIT_VIRTUALTABLE
369ebfaad2Sdan 
379ebfaad2Sdan #ifndef SQLITE_AMALGAMATION
382f7260deSdan 
391da9c97bSdrh #ifndef UINT32_TYPE
401da9c97bSdrh # ifdef HAVE_UINT32_T
411da9c97bSdrh #  define UINT32_TYPE uint32_t
421da9c97bSdrh # else
431da9c97bSdrh #  define UINT32_TYPE unsigned int
441da9c97bSdrh # endif
451da9c97bSdrh #endif
461da9c97bSdrh #ifndef UINT16_TYPE
471da9c97bSdrh # ifdef HAVE_UINT16_T
481da9c97bSdrh #  define UINT16_TYPE uint16_t
491da9c97bSdrh # else
501da9c97bSdrh #  define UINT16_TYPE unsigned short int
511da9c97bSdrh # endif
521da9c97bSdrh #endif
539ebfaad2Sdan typedef sqlite3_int64 i64;
549ebfaad2Sdan typedef unsigned char u8;
551da9c97bSdrh typedef UINT32_TYPE u32;           /* 4-byte unsigned integer */
561da9c97bSdrh typedef UINT16_TYPE u16;           /* 2-byte unsigned integer */
579ebfaad2Sdan #define MIN(a,b) ((a)<(b) ? (a) : (b))
582f7260deSdan 
592f7260deSdan #if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
6011a9ad56Sdrh # define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1
6111a9ad56Sdrh #endif
6211a9ad56Sdrh #if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS)
632f7260deSdan # define ALWAYS(X)      (1)
642f7260deSdan # define NEVER(X)       (0)
652f7260deSdan #elif !defined(NDEBUG)
662f7260deSdan # define ALWAYS(X)      ((X)?1:(assert(0),0))
672f7260deSdan # define NEVER(X)       ((X)?(assert(0),1):0)
682f7260deSdan #else
692f7260deSdan # define ALWAYS(X)      (X)
702f7260deSdan # define NEVER(X)       (X)
719ebfaad2Sdan #endif
729ebfaad2Sdan 
732f7260deSdan #endif   /* SQLITE_AMALGAMATION */
742f7260deSdan 
751dff3281Sdan /*
761dff3281Sdan ** Definitions for mode bitmasks S_IFDIR, S_IFREG and S_IFLNK.
771dff3281Sdan **
781dff3281Sdan ** In some ways it would be better to obtain these values from system
791dff3281Sdan ** header files. But, the dependency is undesirable and (a) these
801dff3281Sdan ** have been stable for decades, (b) the values are part of POSIX and
811dff3281Sdan ** are also made explicit in [man stat], and (c) are part of the
821dff3281Sdan ** file format for zip archives.
831dff3281Sdan */
841dff3281Sdan #ifndef S_IFDIR
851dff3281Sdan # define S_IFDIR 0040000
861dff3281Sdan #endif
871dff3281Sdan #ifndef S_IFREG
881dff3281Sdan # define S_IFREG 0100000
891dff3281Sdan #endif
901dff3281Sdan #ifndef S_IFLNK
911dff3281Sdan # define S_IFLNK 0120000
921dff3281Sdan #endif
931dff3281Sdan 
94e4185bdaSdrh static const char ZIPFILE_SCHEMA[] =
95e4185bdaSdrh   "CREATE TABLE y("
9666a3a91aSdan     "name PRIMARY KEY,"  /* 0: Name of file in zip archive */
97e4185bdaSdrh     "mode,"              /* 1: POSIX mode for file */
98e4185bdaSdrh     "mtime,"             /* 2: Last modification time (secs since 1970)*/
99e4185bdaSdrh     "sz,"                /* 3: Size of object */
100e4185bdaSdrh     "rawdata,"           /* 4: Raw data */
101e4185bdaSdrh     "data,"              /* 5: Uncompressed data */
102e4185bdaSdrh     "method,"            /* 6: Compression method (integer) */
10389fa7469Sdan     "z HIDDEN"           /* 7: Name of zip file */
10466a3a91aSdan   ") WITHOUT ROWID;";
1059ebfaad2Sdan 
106e4185bdaSdrh #define ZIPFILE_F_COLUMN_IDX 7    /* Index of column "file" in the above */
1079ebfaad2Sdan #define ZIPFILE_BUFFER_SIZE (64*1024)
1089ebfaad2Sdan 
109b5090e48Sdan 
110373dc3bbSdan /*
111373dc3bbSdan ** Magic numbers used to read and write zip files.
112373dc3bbSdan **
113373dc3bbSdan ** ZIPFILE_NEWENTRY_MADEBY:
114373dc3bbSdan **   Use this value for the "version-made-by" field in new zip file
115373dc3bbSdan **   entries. The upper byte indicates "unix", and the lower byte
116373dc3bbSdan **   indicates that the zip file matches pkzip specification 3.0.
117373dc3bbSdan **   This is what info-zip seems to do.
118373dc3bbSdan **
119373dc3bbSdan ** ZIPFILE_NEWENTRY_REQUIRED:
120373dc3bbSdan **   Value for "version-required-to-extract" field of new entries.
121373dc3bbSdan **   Version 2.0 is required to support folders and deflate compression.
122373dc3bbSdan **
123373dc3bbSdan ** ZIPFILE_NEWENTRY_FLAGS:
124373dc3bbSdan **   Value for "general-purpose-bit-flags" field of new entries. Bit
125373dc3bbSdan **   11 means "utf-8 filename and comment".
126373dc3bbSdan **
127373dc3bbSdan ** ZIPFILE_SIGNATURE_CDS:
128373dc3bbSdan **   First 4 bytes of a valid CDS record.
129373dc3bbSdan **
130373dc3bbSdan ** ZIPFILE_SIGNATURE_LFH:
131373dc3bbSdan **   First 4 bytes of a valid LFH record.
132a07aa8d3Sdan **
133a07aa8d3Sdan ** ZIPFILE_SIGNATURE_EOCD
134a07aa8d3Sdan **   First 4 bytes of a valid EOCD record.
135373dc3bbSdan */
136b5090e48Sdan #define ZIPFILE_EXTRA_TIMESTAMP   0x5455
137373dc3bbSdan #define ZIPFILE_NEWENTRY_MADEBY   ((3<<8) + 30)
138373dc3bbSdan #define ZIPFILE_NEWENTRY_REQUIRED 20
139373dc3bbSdan #define ZIPFILE_NEWENTRY_FLAGS    0x800
140373dc3bbSdan #define ZIPFILE_SIGNATURE_CDS     0x02014b50
141373dc3bbSdan #define ZIPFILE_SIGNATURE_LFH     0x04034b50
142373dc3bbSdan #define ZIPFILE_SIGNATURE_EOCD    0x06054b50
14326333ee3Sdan 
1449ebfaad2Sdan /*
145a07aa8d3Sdan ** The sizes of the fixed-size part of each of the three main data
146a07aa8d3Sdan ** structures in a zip archive.
1479ebfaad2Sdan */
148a07aa8d3Sdan #define ZIPFILE_LFH_FIXED_SZ      30
149a07aa8d3Sdan #define ZIPFILE_EOCD_FIXED_SZ     22
150a07aa8d3Sdan #define ZIPFILE_CDS_FIXED_SZ      46
1519ebfaad2Sdan 
1529ebfaad2Sdan /*
1539ebfaad2Sdan *** 4.3.16  End of central directory record:
1549ebfaad2Sdan ***
1559ebfaad2Sdan ***   end of central dir signature    4 bytes  (0x06054b50)
1569ebfaad2Sdan ***   number of this disk             2 bytes
1579ebfaad2Sdan ***   number of the disk with the
1589ebfaad2Sdan ***   start of the central directory  2 bytes
1599ebfaad2Sdan ***   total number of entries in the
1609ebfaad2Sdan ***   central directory on this disk  2 bytes
1619ebfaad2Sdan ***   total number of entries in
1629ebfaad2Sdan ***   the central directory           2 bytes
1639ebfaad2Sdan ***   size of the central directory   4 bytes
1649ebfaad2Sdan ***   offset of start of central
1659ebfaad2Sdan ***   directory with respect to
1669ebfaad2Sdan ***   the starting disk number        4 bytes
1679ebfaad2Sdan ***   .ZIP file comment length        2 bytes
1689ebfaad2Sdan ***   .ZIP file comment       (variable size)
1699ebfaad2Sdan */
1709ebfaad2Sdan typedef struct ZipfileEOCD ZipfileEOCD;
1719ebfaad2Sdan struct ZipfileEOCD {
1729ebfaad2Sdan   u16 iDisk;
1739ebfaad2Sdan   u16 iFirstDisk;
1749ebfaad2Sdan   u16 nEntry;
1759ebfaad2Sdan   u16 nEntryTotal;
1769ebfaad2Sdan   u32 nSize;
1779ebfaad2Sdan   u32 iOffset;
1789ebfaad2Sdan };
1799ebfaad2Sdan 
1809ebfaad2Sdan /*
1819ebfaad2Sdan *** 4.3.12  Central directory structure:
1829ebfaad2Sdan ***
1839ebfaad2Sdan *** ...
1849ebfaad2Sdan ***
1859ebfaad2Sdan ***   central file header signature   4 bytes  (0x02014b50)
1869ebfaad2Sdan ***   version made by                 2 bytes
1879ebfaad2Sdan ***   version needed to extract       2 bytes
1889ebfaad2Sdan ***   general purpose bit flag        2 bytes
1899ebfaad2Sdan ***   compression method              2 bytes
1909ebfaad2Sdan ***   last mod file time              2 bytes
1919ebfaad2Sdan ***   last mod file date              2 bytes
1929ebfaad2Sdan ***   crc-32                          4 bytes
1939ebfaad2Sdan ***   compressed size                 4 bytes
1949ebfaad2Sdan ***   uncompressed size               4 bytes
1959ebfaad2Sdan ***   file name length                2 bytes
1969ebfaad2Sdan ***   extra field length              2 bytes
1979ebfaad2Sdan ***   file comment length             2 bytes
1989ebfaad2Sdan ***   disk number start               2 bytes
1999ebfaad2Sdan ***   internal file attributes        2 bytes
2009ebfaad2Sdan ***   external file attributes        4 bytes
2019ebfaad2Sdan ***   relative offset of local header 4 bytes
2029ebfaad2Sdan */
2039ebfaad2Sdan typedef struct ZipfileCDS ZipfileCDS;
2049ebfaad2Sdan struct ZipfileCDS {
2059ebfaad2Sdan   u16 iVersionMadeBy;
2069ebfaad2Sdan   u16 iVersionExtract;
2079ebfaad2Sdan   u16 flags;
2089ebfaad2Sdan   u16 iCompression;
2099ebfaad2Sdan   u16 mTime;
2109ebfaad2Sdan   u16 mDate;
2119ebfaad2Sdan   u32 crc32;
2129ebfaad2Sdan   u32 szCompressed;
2139ebfaad2Sdan   u32 szUncompressed;
2149ebfaad2Sdan   u16 nFile;
2159ebfaad2Sdan   u16 nExtra;
2169ebfaad2Sdan   u16 nComment;
2179ebfaad2Sdan   u16 iDiskStart;
2189ebfaad2Sdan   u16 iInternalAttr;
2199ebfaad2Sdan   u32 iExternalAttr;
2209ebfaad2Sdan   u32 iOffset;
2219ebfaad2Sdan   char *zFile;                    /* Filename (sqlite3_malloc()) */
2229ebfaad2Sdan };
2239ebfaad2Sdan 
2249ebfaad2Sdan /*
2259ebfaad2Sdan *** 4.3.7  Local file header:
2269ebfaad2Sdan ***
2279ebfaad2Sdan ***   local file header signature     4 bytes  (0x04034b50)
2289ebfaad2Sdan ***   version needed to extract       2 bytes
2299ebfaad2Sdan ***   general purpose bit flag        2 bytes
2309ebfaad2Sdan ***   compression method              2 bytes
2319ebfaad2Sdan ***   last mod file time              2 bytes
2329ebfaad2Sdan ***   last mod file date              2 bytes
2339ebfaad2Sdan ***   crc-32                          4 bytes
2349ebfaad2Sdan ***   compressed size                 4 bytes
2359ebfaad2Sdan ***   uncompressed size               4 bytes
2369ebfaad2Sdan ***   file name length                2 bytes
2379ebfaad2Sdan ***   extra field length              2 bytes
2389ebfaad2Sdan ***
2399ebfaad2Sdan */
2409ebfaad2Sdan typedef struct ZipfileLFH ZipfileLFH;
2419ebfaad2Sdan struct ZipfileLFH {
2429ebfaad2Sdan   u16 iVersionExtract;
2439ebfaad2Sdan   u16 flags;
2449ebfaad2Sdan   u16 iCompression;
2459ebfaad2Sdan   u16 mTime;
2469ebfaad2Sdan   u16 mDate;
2479ebfaad2Sdan   u32 crc32;
2489ebfaad2Sdan   u32 szCompressed;
2499ebfaad2Sdan   u32 szUncompressed;
2509ebfaad2Sdan   u16 nFile;
2519ebfaad2Sdan   u16 nExtra;
2529ebfaad2Sdan };
2539ebfaad2Sdan 
254373dc3bbSdan typedef struct ZipfileEntry ZipfileEntry;
255373dc3bbSdan struct ZipfileEntry {
2568558ef2eSdan   ZipfileCDS cds;            /* Parsed CDS record */
2578558ef2eSdan   u32 mUnixTime;             /* Modification time, in UNIX format */
2588558ef2eSdan   u8 *aExtra;                /* cds.nExtra+cds.nComment bytes of extra data */
259a07aa8d3Sdan   i64 iDataOff;              /* Offset to data in file (if aData==0) */
2608005d605Sdan   u8 *aData;                 /* cds.szCompressed bytes of compressed data */
261db0cb303Sdan   ZipfileEntry *pNext;       /* Next element in in-memory CDS */
262373dc3bbSdan };
263373dc3bbSdan 
2640cde0c62Sdan /*
265a07aa8d3Sdan ** Cursor type for zipfile tables.
2660cde0c62Sdan */
2670cde0c62Sdan typedef struct ZipfileCsr ZipfileCsr;
2680cde0c62Sdan struct ZipfileCsr {
2690cde0c62Sdan   sqlite3_vtab_cursor base;  /* Base class - must be first */
27089fa7469Sdan   i64 iId;                   /* Cursor ID */
2718558ef2eSdan   u8 bEof;                   /* True when at EOF */
2728558ef2eSdan   u8 bNoop;                  /* If next xNext() call is no-op */
2730cde0c62Sdan 
2740cde0c62Sdan   /* Used outside of write transactions */
2750cde0c62Sdan   FILE *pFile;               /* Zip file */
2760cde0c62Sdan   i64 iNextOff;              /* Offset of next record in central directory */
2770cde0c62Sdan   ZipfileEOCD eocd;          /* Parse of central directory record */
2780cde0c62Sdan 
279a07aa8d3Sdan   ZipfileEntry *pFreeEntry;  /* Free this list when cursor is closed or reset */
2808558ef2eSdan   ZipfileEntry *pCurrent;    /* Current entry */
2810cde0c62Sdan   ZipfileCsr *pCsrNext;      /* Next cursor on same virtual table */
2820cde0c62Sdan };
2830cde0c62Sdan 
2849ebfaad2Sdan typedef struct ZipfileTab ZipfileTab;
2859ebfaad2Sdan struct ZipfileTab {
2869ebfaad2Sdan   sqlite3_vtab base;         /* Base class - must be first */
287373dc3bbSdan   char *zFile;               /* Zip file this table accesses (may be NULL) */
28842f3c5ffSdrh   sqlite3 *db;               /* Host database connection */
289373dc3bbSdan   u8 *aBuffer;               /* Temporary buffer used for various tasks */
290373dc3bbSdan 
2910cde0c62Sdan   ZipfileCsr *pCsrList;      /* List of cursors */
29289fa7469Sdan   i64 iNextCsrid;
29389fa7469Sdan 
29489fa7469Sdan   /* The following are used by write transactions only */
295db0cb303Sdan   ZipfileEntry *pFirstEntry; /* Linked list of all files (if pWriteFd!=0) */
296db0cb303Sdan   ZipfileEntry *pLastEntry;  /* Last element in pFirstEntry list */
297373dc3bbSdan   FILE *pWriteFd;            /* File handle open on zip archive */
298373dc3bbSdan   i64 szCurrent;             /* Current size of zip archive */
299373dc3bbSdan   i64 szOrig;                /* Size of archive at start of transaction */
3009ebfaad2Sdan };
3019ebfaad2Sdan 
302a07aa8d3Sdan /*
303a07aa8d3Sdan ** Set the error message contained in context ctx to the results of
304a07aa8d3Sdan ** vprintf(zFmt, ...).
305a07aa8d3Sdan */
zipfileCtxErrorMsg(sqlite3_context * ctx,const char * zFmt,...)306a07aa8d3Sdan static void zipfileCtxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){
307a07aa8d3Sdan   char *zMsg = 0;
308a07aa8d3Sdan   va_list ap;
309a07aa8d3Sdan   va_start(ap, zFmt);
310a07aa8d3Sdan   zMsg = sqlite3_vmprintf(zFmt, ap);
311a07aa8d3Sdan   sqlite3_result_error(ctx, zMsg, -1);
312a07aa8d3Sdan   sqlite3_free(zMsg);
313a07aa8d3Sdan   va_end(ap);
314a07aa8d3Sdan }
315a07aa8d3Sdan 
316a07aa8d3Sdan /*
317a07aa8d3Sdan ** If string zIn is quoted, dequote it in place. Otherwise, if the string
318a07aa8d3Sdan ** is not quoted, do nothing.
319a07aa8d3Sdan */
zipfileDequote(char * zIn)320373dc3bbSdan static void zipfileDequote(char *zIn){
321373dc3bbSdan   char q = zIn[0];
322373dc3bbSdan   if( q=='"' || q=='\'' || q=='`' || q=='[' ){
323373dc3bbSdan     int iIn = 1;
324373dc3bbSdan     int iOut = 0;
325373dc3bbSdan     if( q=='[' ) q = ']';
3262f7260deSdan     while( ALWAYS(zIn[iIn]) ){
3272f7260deSdan       char c = zIn[iIn++];
3282f7260deSdan       if( c==q && zIn[iIn++]!=q ) break;
329373dc3bbSdan       zIn[iOut++] = c;
330373dc3bbSdan     }
331373dc3bbSdan     zIn[iOut] = '\0';
332373dc3bbSdan   }
333373dc3bbSdan }
334373dc3bbSdan 
3359ebfaad2Sdan /*
3369ebfaad2Sdan ** Construct a new ZipfileTab virtual table object.
337373dc3bbSdan **
338373dc3bbSdan **   argv[0]   -> module name  ("zipfile")
339373dc3bbSdan **   argv[1]   -> database name
340373dc3bbSdan **   argv[2]   -> table name
341373dc3bbSdan **   argv[...] -> "column name" and other module argument fields.
3429ebfaad2Sdan */
zipfileConnect(sqlite3 * db,void * pAux,int argc,const char * const * argv,sqlite3_vtab ** ppVtab,char ** pzErr)3439ebfaad2Sdan static int zipfileConnect(
3449ebfaad2Sdan   sqlite3 *db,
3459ebfaad2Sdan   void *pAux,
3469ebfaad2Sdan   int argc, const char *const*argv,
3479ebfaad2Sdan   sqlite3_vtab **ppVtab,
3489ebfaad2Sdan   char **pzErr
3499ebfaad2Sdan ){
350373dc3bbSdan   int nByte = sizeof(ZipfileTab) + ZIPFILE_BUFFER_SIZE;
351373dc3bbSdan   int nFile = 0;
352373dc3bbSdan   const char *zFile = 0;
3539ebfaad2Sdan   ZipfileTab *pNew = 0;
3549ebfaad2Sdan   int rc;
3559ebfaad2Sdan 
356fdcd9d4eSdan   /* If the table name is not "zipfile", require that the argument be
357fdcd9d4eSdan   ** specified. This stops zipfile tables from being created as:
358fdcd9d4eSdan   **
359fdcd9d4eSdan   **   CREATE VIRTUAL TABLE zzz USING zipfile();
360fdcd9d4eSdan   **
361fdcd9d4eSdan   ** It does not prevent:
362fdcd9d4eSdan   **
363fdcd9d4eSdan   **   CREATE VIRTUAL TABLE zipfile USING zipfile();
364fdcd9d4eSdan   */
365fdcd9d4eSdan   assert( 0==sqlite3_stricmp(argv[0], "zipfile") );
366fdcd9d4eSdan   if( (0!=sqlite3_stricmp(argv[2], "zipfile") && argc<4) || argc>4 ){
367fdcd9d4eSdan     *pzErr = sqlite3_mprintf("zipfile constructor requires one argument");
368fdcd9d4eSdan     return SQLITE_ERROR;
369fdcd9d4eSdan   }
370fdcd9d4eSdan 
371373dc3bbSdan   if( argc>3 ){
372373dc3bbSdan     zFile = argv[3];
3739a5efdecSmistachkin     nFile = (int)strlen(zFile)+1;
374373dc3bbSdan   }
375373dc3bbSdan 
3769ebfaad2Sdan   rc = sqlite3_declare_vtab(db, ZIPFILE_SCHEMA);
3779ebfaad2Sdan   if( rc==SQLITE_OK ){
3782d77d80aSdrh     pNew = (ZipfileTab*)sqlite3_malloc64((sqlite3_int64)nByte+nFile);
3799ebfaad2Sdan     if( pNew==0 ) return SQLITE_NOMEM;
380373dc3bbSdan     memset(pNew, 0, nByte+nFile);
38142f3c5ffSdrh     pNew->db = db;
382373dc3bbSdan     pNew->aBuffer = (u8*)&pNew[1];
383373dc3bbSdan     if( zFile ){
384373dc3bbSdan       pNew->zFile = (char*)&pNew->aBuffer[ZIPFILE_BUFFER_SIZE];
385373dc3bbSdan       memcpy(pNew->zFile, zFile, nFile);
386373dc3bbSdan       zipfileDequote(pNew->zFile);
387373dc3bbSdan     }
3889ebfaad2Sdan   }
3892b1c2aadSdrh   sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
3909ebfaad2Sdan   *ppVtab = (sqlite3_vtab*)pNew;
3919ebfaad2Sdan   return rc;
3929ebfaad2Sdan }
3939ebfaad2Sdan 
394a07aa8d3Sdan /*
395a07aa8d3Sdan ** Free the ZipfileEntry structure indicated by the only argument.
396a07aa8d3Sdan */
zipfileEntryFree(ZipfileEntry * p)3978558ef2eSdan static void zipfileEntryFree(ZipfileEntry *p){
3988558ef2eSdan   if( p ){
3998558ef2eSdan     sqlite3_free(p->cds.zFile);
4008558ef2eSdan     sqlite3_free(p);
4018558ef2eSdan   }
4028558ef2eSdan }
4038558ef2eSdan 
404a07aa8d3Sdan /*
405a07aa8d3Sdan ** Release resources that should be freed at the end of a write
406a07aa8d3Sdan ** transaction.
407a07aa8d3Sdan */
zipfileCleanupTransaction(ZipfileTab * pTab)4088558ef2eSdan static void zipfileCleanupTransaction(ZipfileTab *pTab){
4098558ef2eSdan   ZipfileEntry *pEntry;
4108558ef2eSdan   ZipfileEntry *pNext;
4118558ef2eSdan 
4128558ef2eSdan   if( pTab->pWriteFd ){
4138558ef2eSdan     fclose(pTab->pWriteFd);
4148558ef2eSdan     pTab->pWriteFd = 0;
4158558ef2eSdan   }
4168558ef2eSdan   for(pEntry=pTab->pFirstEntry; pEntry; pEntry=pNext){
4178558ef2eSdan     pNext = pEntry->pNext;
4188558ef2eSdan     zipfileEntryFree(pEntry);
4198558ef2eSdan   }
4208558ef2eSdan   pTab->pFirstEntry = 0;
4218558ef2eSdan   pTab->pLastEntry = 0;
4228558ef2eSdan   pTab->szCurrent = 0;
4238558ef2eSdan   pTab->szOrig = 0;
4248558ef2eSdan }
4258558ef2eSdan 
4269ebfaad2Sdan /*
4279ebfaad2Sdan ** This method is the destructor for zipfile vtab objects.
4289ebfaad2Sdan */
zipfileDisconnect(sqlite3_vtab * pVtab)4299ebfaad2Sdan static int zipfileDisconnect(sqlite3_vtab *pVtab){
4308558ef2eSdan   zipfileCleanupTransaction((ZipfileTab*)pVtab);
4319ebfaad2Sdan   sqlite3_free(pVtab);
4329ebfaad2Sdan   return SQLITE_OK;
4339ebfaad2Sdan }
4349ebfaad2Sdan 
4359ebfaad2Sdan /*
4369ebfaad2Sdan ** Constructor for a new ZipfileCsr object.
4379ebfaad2Sdan */
zipfileOpen(sqlite3_vtab * p,sqlite3_vtab_cursor ** ppCsr)4389ebfaad2Sdan static int zipfileOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCsr){
43989fa7469Sdan   ZipfileTab *pTab = (ZipfileTab*)p;
4409ebfaad2Sdan   ZipfileCsr *pCsr;
441373dc3bbSdan   pCsr = sqlite3_malloc(sizeof(*pCsr));
442373dc3bbSdan   *ppCsr = (sqlite3_vtab_cursor*)pCsr;
443373dc3bbSdan   if( pCsr==0 ){
444373dc3bbSdan     return SQLITE_NOMEM;
445373dc3bbSdan   }
4469ebfaad2Sdan   memset(pCsr, 0, sizeof(*pCsr));
44789fa7469Sdan   pCsr->iId = ++pTab->iNextCsrid;
44889fa7469Sdan   pCsr->pCsrNext = pTab->pCsrList;
44989fa7469Sdan   pTab->pCsrList = pCsr;
4509ebfaad2Sdan   return SQLITE_OK;
4519ebfaad2Sdan }
4529ebfaad2Sdan 
4539ebfaad2Sdan /*
4549ebfaad2Sdan ** Reset a cursor back to the state it was in when first returned
4559ebfaad2Sdan ** by zipfileOpen().
4569ebfaad2Sdan */
zipfileResetCursor(ZipfileCsr * pCsr)4579ebfaad2Sdan static void zipfileResetCursor(ZipfileCsr *pCsr){
4588005d605Sdan   ZipfileEntry *p;
4598005d605Sdan   ZipfileEntry *pNext;
4608005d605Sdan 
4619ebfaad2Sdan   pCsr->bEof = 0;
4629ebfaad2Sdan   if( pCsr->pFile ){
4639ebfaad2Sdan     fclose(pCsr->pFile);
4649ebfaad2Sdan     pCsr->pFile = 0;
4658558ef2eSdan     zipfileEntryFree(pCsr->pCurrent);
4668558ef2eSdan     pCsr->pCurrent = 0;
4679ebfaad2Sdan   }
4688005d605Sdan 
4698005d605Sdan   for(p=pCsr->pFreeEntry; p; p=pNext){
4708005d605Sdan     pNext = p->pNext;
4718005d605Sdan     zipfileEntryFree(p);
4728005d605Sdan   }
4739ebfaad2Sdan }
4749ebfaad2Sdan 
4759ebfaad2Sdan /*
4769ebfaad2Sdan ** Destructor for an ZipfileCsr.
4779ebfaad2Sdan */
zipfileClose(sqlite3_vtab_cursor * cur)4789ebfaad2Sdan static int zipfileClose(sqlite3_vtab_cursor *cur){
4799ebfaad2Sdan   ZipfileCsr *pCsr = (ZipfileCsr*)cur;
48089fa7469Sdan   ZipfileTab *pTab = (ZipfileTab*)(pCsr->base.pVtab);
48189fa7469Sdan   ZipfileCsr **pp;
4829ebfaad2Sdan   zipfileResetCursor(pCsr);
48389fa7469Sdan 
48489fa7469Sdan   /* Remove this cursor from the ZipfileTab.pCsrList list. */
4852f7260deSdan   for(pp=&pTab->pCsrList; *pp!=pCsr; pp=&((*pp)->pCsrNext));
48689fa7469Sdan   *pp = pCsr->pCsrNext;
48789fa7469Sdan 
4889ebfaad2Sdan   sqlite3_free(pCsr);
4899ebfaad2Sdan   return SQLITE_OK;
4909ebfaad2Sdan }
4919ebfaad2Sdan 
4929ebfaad2Sdan /*
4939ebfaad2Sdan ** Set the error message for the virtual table associated with cursor
4949ebfaad2Sdan ** pCsr to the results of vprintf(zFmt, ...).
4959ebfaad2Sdan */
zipfileTableErr(ZipfileTab * pTab,const char * zFmt,...)49641a6f2cbSdrh static void zipfileTableErr(ZipfileTab *pTab, const char *zFmt, ...){
4979ebfaad2Sdan   va_list ap;
4989ebfaad2Sdan   va_start(ap, zFmt);
49941a6f2cbSdrh   sqlite3_free(pTab->base.zErrMsg);
50041a6f2cbSdrh   pTab->base.zErrMsg = sqlite3_vmprintf(zFmt, ap);
50141a6f2cbSdrh   va_end(ap);
50241a6f2cbSdrh }
zipfileCursorErr(ZipfileCsr * pCsr,const char * zFmt,...)50341a6f2cbSdrh static void zipfileCursorErr(ZipfileCsr *pCsr, const char *zFmt, ...){
50441a6f2cbSdrh   va_list ap;
50541a6f2cbSdrh   va_start(ap, zFmt);
50641a6f2cbSdrh   sqlite3_free(pCsr->base.pVtab->zErrMsg);
5079ebfaad2Sdan   pCsr->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap);
5089ebfaad2Sdan   va_end(ap);
5099ebfaad2Sdan }
5109ebfaad2Sdan 
511a07aa8d3Sdan /*
512a07aa8d3Sdan ** Read nRead bytes of data from offset iOff of file pFile into buffer
513a07aa8d3Sdan ** aRead[]. Return SQLITE_OK if successful, or an SQLite error code
514a07aa8d3Sdan ** otherwise.
515a07aa8d3Sdan **
516a07aa8d3Sdan ** If an error does occur, output variable (*pzErrmsg) may be set to point
517a07aa8d3Sdan ** to an English language error message. It is the responsibility of the
518a07aa8d3Sdan ** caller to eventually free this buffer using
519a07aa8d3Sdan ** sqlite3_free().
520a07aa8d3Sdan */
zipfileReadData(FILE * pFile,u8 * aRead,int nRead,i64 iOff,char ** pzErrmsg)521373dc3bbSdan static int zipfileReadData(
522373dc3bbSdan   FILE *pFile,                    /* Read from this file */
523373dc3bbSdan   u8 *aRead,                      /* Read into this buffer */
524373dc3bbSdan   int nRead,                      /* Number of bytes to read */
525373dc3bbSdan   i64 iOff,                       /* Offset to read from */
526373dc3bbSdan   char **pzErrmsg                 /* OUT: Error message (from sqlite3_malloc) */
527373dc3bbSdan ){
5289ebfaad2Sdan   size_t n;
529dfdfd8c7Smistachkin   fseek(pFile, (long)iOff, SEEK_SET);
530373dc3bbSdan   n = fread(aRead, 1, nRead, pFile);
531dfdfd8c7Smistachkin   if( (int)n!=nRead ){
532373dc3bbSdan     *pzErrmsg = sqlite3_mprintf("error in fread()");
5339ebfaad2Sdan     return SQLITE_ERROR;
5349ebfaad2Sdan   }
5359ebfaad2Sdan   return SQLITE_OK;
5369ebfaad2Sdan }
5379ebfaad2Sdan 
zipfileAppendData(ZipfileTab * pTab,const u8 * aWrite,int nWrite)538373dc3bbSdan static int zipfileAppendData(
539373dc3bbSdan   ZipfileTab *pTab,
540373dc3bbSdan   const u8 *aWrite,
541373dc3bbSdan   int nWrite
542373dc3bbSdan ){
543acd3c8a9Sdan   if( nWrite>0 ){
544acd3c8a9Sdan     size_t n = nWrite;
545dfdfd8c7Smistachkin     fseek(pTab->pWriteFd, (long)pTab->szCurrent, SEEK_SET);
546373dc3bbSdan     n = fwrite(aWrite, 1, nWrite, pTab->pWriteFd);
547dfdfd8c7Smistachkin     if( (int)n!=nWrite ){
548373dc3bbSdan       pTab->base.zErrMsg = sqlite3_mprintf("error in fwrite()");
549373dc3bbSdan       return SQLITE_ERROR;
550373dc3bbSdan     }
551373dc3bbSdan     pTab->szCurrent += nWrite;
552acd3c8a9Sdan   }
553373dc3bbSdan   return SQLITE_OK;
554373dc3bbSdan }
555373dc3bbSdan 
556a07aa8d3Sdan /*
557a07aa8d3Sdan ** Read and return a 16-bit little-endian unsigned integer from buffer aBuf.
558a07aa8d3Sdan */
zipfileGetU16(const u8 * aBuf)5599ebfaad2Sdan static u16 zipfileGetU16(const u8 *aBuf){
5609ebfaad2Sdan   return (aBuf[1] << 8) + aBuf[0];
5619ebfaad2Sdan }
562a07aa8d3Sdan 
563a07aa8d3Sdan /*
564a07aa8d3Sdan ** Read and return a 32-bit little-endian unsigned integer from buffer aBuf.
565a07aa8d3Sdan */
zipfileGetU32(const u8 * aBuf)5669ebfaad2Sdan static u32 zipfileGetU32(const u8 *aBuf){
56776fc88feSdrh   if( aBuf==0 ) return 0;
5689ebfaad2Sdan   return ((u32)(aBuf[3]) << 24)
5699ebfaad2Sdan        + ((u32)(aBuf[2]) << 16)
5709ebfaad2Sdan        + ((u32)(aBuf[1]) <<  8)
5719ebfaad2Sdan        + ((u32)(aBuf[0]) <<  0);
5729ebfaad2Sdan }
5739ebfaad2Sdan 
574a07aa8d3Sdan /*
575a07aa8d3Sdan ** Write a 16-bit little endiate integer into buffer aBuf.
576a07aa8d3Sdan */
zipfilePutU16(u8 * aBuf,u16 val)577373dc3bbSdan static void zipfilePutU16(u8 *aBuf, u16 val){
578373dc3bbSdan   aBuf[0] = val & 0xFF;
579373dc3bbSdan   aBuf[1] = (val>>8) & 0xFF;
580373dc3bbSdan }
581a07aa8d3Sdan 
582a07aa8d3Sdan /*
583a07aa8d3Sdan ** Write a 32-bit little endiate integer into buffer aBuf.
584a07aa8d3Sdan */
zipfilePutU32(u8 * aBuf,u32 val)585373dc3bbSdan static void zipfilePutU32(u8 *aBuf, u32 val){
586373dc3bbSdan   aBuf[0] = val & 0xFF;
587373dc3bbSdan   aBuf[1] = (val>>8) & 0xFF;
588373dc3bbSdan   aBuf[2] = (val>>16) & 0xFF;
589373dc3bbSdan   aBuf[3] = (val>>24) & 0xFF;
590373dc3bbSdan }
591373dc3bbSdan 
5929ebfaad2Sdan #define zipfileRead32(aBuf) ( aBuf+=4, zipfileGetU32(aBuf-4) )
5939ebfaad2Sdan #define zipfileRead16(aBuf) ( aBuf+=2, zipfileGetU16(aBuf-2) )
5949ebfaad2Sdan 
595373dc3bbSdan #define zipfileWrite32(aBuf,val) { zipfilePutU32(aBuf,val); aBuf+=4; }
596373dc3bbSdan #define zipfileWrite16(aBuf,val) { zipfilePutU16(aBuf,val); aBuf+=2; }
597373dc3bbSdan 
598373dc3bbSdan /*
599373dc3bbSdan ** Magic numbers used to read CDS records.
600373dc3bbSdan */
601373dc3bbSdan #define ZIPFILE_CDS_NFILE_OFF        28
6028005d605Sdan #define ZIPFILE_CDS_SZCOMPRESSED_OFF 20
603373dc3bbSdan 
604f42884c3Sdan /*
605f42884c3Sdan ** Decode the CDS record in buffer aBuf into (*pCDS). Return SQLITE_ERROR
606f42884c3Sdan ** if the record is not well-formed, or SQLITE_OK otherwise.
607f42884c3Sdan */
zipfileReadCDS(u8 * aBuf,ZipfileCDS * pCDS)608f42884c3Sdan static int zipfileReadCDS(u8 *aBuf, ZipfileCDS *pCDS){
609f42884c3Sdan   u8 *aRead = aBuf;
610f42884c3Sdan   u32 sig = zipfileRead32(aRead);
611f42884c3Sdan   int rc = SQLITE_OK;
612f42884c3Sdan   if( sig!=ZIPFILE_SIGNATURE_CDS ){
613f42884c3Sdan     rc = SQLITE_ERROR;
614f42884c3Sdan   }else{
615f42884c3Sdan     pCDS->iVersionMadeBy = zipfileRead16(aRead);
616f42884c3Sdan     pCDS->iVersionExtract = zipfileRead16(aRead);
617f42884c3Sdan     pCDS->flags = zipfileRead16(aRead);
618f42884c3Sdan     pCDS->iCompression = zipfileRead16(aRead);
619f42884c3Sdan     pCDS->mTime = zipfileRead16(aRead);
620f42884c3Sdan     pCDS->mDate = zipfileRead16(aRead);
621f42884c3Sdan     pCDS->crc32 = zipfileRead32(aRead);
622f42884c3Sdan     pCDS->szCompressed = zipfileRead32(aRead);
623f42884c3Sdan     pCDS->szUncompressed = zipfileRead32(aRead);
624f42884c3Sdan     assert( aRead==&aBuf[ZIPFILE_CDS_NFILE_OFF] );
625f42884c3Sdan     pCDS->nFile = zipfileRead16(aRead);
626f42884c3Sdan     pCDS->nExtra = zipfileRead16(aRead);
627f42884c3Sdan     pCDS->nComment = zipfileRead16(aRead);
628f42884c3Sdan     pCDS->iDiskStart = zipfileRead16(aRead);
629f42884c3Sdan     pCDS->iInternalAttr = zipfileRead16(aRead);
630f42884c3Sdan     pCDS->iExternalAttr = zipfileRead32(aRead);
631f42884c3Sdan     pCDS->iOffset = zipfileRead32(aRead);
632f42884c3Sdan     assert( aRead==&aBuf[ZIPFILE_CDS_FIXED_SZ] );
633f42884c3Sdan   }
634f42884c3Sdan 
635f42884c3Sdan   return rc;
636f42884c3Sdan }
637f42884c3Sdan 
638f42884c3Sdan /*
639a07aa8d3Sdan ** Decode the LFH record in buffer aBuf into (*pLFH). Return SQLITE_ERROR
640a07aa8d3Sdan ** if the record is not well-formed, or SQLITE_OK otherwise.
641a07aa8d3Sdan */
zipfileReadLFH(u8 * aBuffer,ZipfileLFH * pLFH)642a07aa8d3Sdan static int zipfileReadLFH(
643a07aa8d3Sdan   u8 *aBuffer,
644a07aa8d3Sdan   ZipfileLFH *pLFH
645a07aa8d3Sdan ){
646a07aa8d3Sdan   u8 *aRead = aBuffer;
647a07aa8d3Sdan   int rc = SQLITE_OK;
648a07aa8d3Sdan 
649a07aa8d3Sdan   u32 sig = zipfileRead32(aRead);
650a07aa8d3Sdan   if( sig!=ZIPFILE_SIGNATURE_LFH ){
651a07aa8d3Sdan     rc = SQLITE_ERROR;
652a07aa8d3Sdan   }else{
653a07aa8d3Sdan     pLFH->iVersionExtract = zipfileRead16(aRead);
654a07aa8d3Sdan     pLFH->flags = zipfileRead16(aRead);
655a07aa8d3Sdan     pLFH->iCompression = zipfileRead16(aRead);
656a07aa8d3Sdan     pLFH->mTime = zipfileRead16(aRead);
657a07aa8d3Sdan     pLFH->mDate = zipfileRead16(aRead);
658a07aa8d3Sdan     pLFH->crc32 = zipfileRead32(aRead);
659a07aa8d3Sdan     pLFH->szCompressed = zipfileRead32(aRead);
660a07aa8d3Sdan     pLFH->szUncompressed = zipfileRead32(aRead);
661a07aa8d3Sdan     pLFH->nFile = zipfileRead16(aRead);
662a07aa8d3Sdan     pLFH->nExtra = zipfileRead16(aRead);
663a07aa8d3Sdan   }
664a07aa8d3Sdan   return rc;
665a07aa8d3Sdan }
666a07aa8d3Sdan 
667a07aa8d3Sdan 
668a07aa8d3Sdan /*
669a07aa8d3Sdan ** Buffer aExtra (size nExtra bytes) contains zip archive "extra" fields.
670a07aa8d3Sdan ** Scan through this buffer to find an "extra-timestamp" field. If one
671a07aa8d3Sdan ** exists, extract the 32-bit modification-timestamp from it and store
672a07aa8d3Sdan ** the value in output parameter *pmTime.
673a07aa8d3Sdan **
674a07aa8d3Sdan ** Zero is returned if no extra-timestamp record could be found (and so
675a07aa8d3Sdan ** *pmTime is left unchanged), or non-zero otherwise.
676a07aa8d3Sdan **
6778558ef2eSdan ** The general format of an extra field is:
678b5090e48Sdan **
679b5090e48Sdan **   Header ID    2 bytes
680b5090e48Sdan **   Data Size    2 bytes
681b5090e48Sdan **   Data         N bytes
682b5090e48Sdan */
zipfileScanExtra(u8 * aExtra,int nExtra,u32 * pmTime)6838558ef2eSdan static int zipfileScanExtra(u8 *aExtra, int nExtra, u32 *pmTime){
6848558ef2eSdan   int ret = 0;
6858558ef2eSdan   u8 *p = aExtra;
6868558ef2eSdan   u8 *pEnd = &aExtra[nExtra];
6879ebfaad2Sdan 
6889ebfaad2Sdan   while( p<pEnd ){
6899ebfaad2Sdan     u16 id = zipfileRead16(p);
6909ebfaad2Sdan     u16 nByte = zipfileRead16(p);
6919ebfaad2Sdan 
6929ebfaad2Sdan     switch( id ){
693b5090e48Sdan       case ZIPFILE_EXTRA_TIMESTAMP: {
6949ebfaad2Sdan         u8 b = p[0];
6959ebfaad2Sdan         if( b & 0x01 ){     /* 0x01 -> modtime is present */
6968558ef2eSdan           *pmTime = zipfileGetU32(&p[1]);
6978558ef2eSdan           ret = 1;
6989ebfaad2Sdan         }
6999ebfaad2Sdan         break;
7009ebfaad2Sdan       }
7019ebfaad2Sdan     }
7029ebfaad2Sdan 
7039ebfaad2Sdan     p += nByte;
7049ebfaad2Sdan   }
7058558ef2eSdan   return ret;
7068558ef2eSdan }
7078558ef2eSdan 
7088558ef2eSdan /*
709a07aa8d3Sdan ** Convert the standard MS-DOS timestamp stored in the mTime and mDate
710a07aa8d3Sdan ** fields of the CDS structure passed as the only argument to a 32-bit
711a07aa8d3Sdan ** UNIX seconds-since-the-epoch timestamp. Return the result.
712a07aa8d3Sdan **
7138558ef2eSdan ** "Standard" MS-DOS time format:
7148558ef2eSdan **
7158558ef2eSdan **   File modification time:
7168558ef2eSdan **     Bits 00-04: seconds divided by 2
7178558ef2eSdan **     Bits 05-10: minute
7188558ef2eSdan **     Bits 11-15: hour
7198558ef2eSdan **   File modification date:
7208558ef2eSdan **     Bits 00-04: day
7218558ef2eSdan **     Bits 05-08: month (1-12)
7228558ef2eSdan **     Bits 09-15: years from 1980
723a07aa8d3Sdan **
724a07aa8d3Sdan ** https://msdn.microsoft.com/en-us/library/9kkf9tah.aspx
7258558ef2eSdan */
zipfileMtime(ZipfileCDS * pCDS)72644091ed3Sdan static u32 zipfileMtime(ZipfileCDS *pCDS){
72768b63c01Sdrh   int Y,M,D,X1,X2,A,B,sec,min,hr;
72868b63c01Sdrh   i64 JDsec;
72968b63c01Sdrh   Y = (1980 + ((pCDS->mDate >> 9) & 0x7F));
73068b63c01Sdrh   M = ((pCDS->mDate >> 5) & 0x0F);
73168b63c01Sdrh   D = (pCDS->mDate & 0x1F);
73268b63c01Sdrh   sec = (pCDS->mTime & 0x1F)*2;
73368b63c01Sdrh   min = (pCDS->mTime >> 5) & 0x3F;
73468b63c01Sdrh   hr = (pCDS->mTime >> 11) & 0x1F;
73568b63c01Sdrh   if( M<=2 ){
73668b63c01Sdrh     Y--;
73768b63c01Sdrh     M += 12;
73844091ed3Sdan   }
73968b63c01Sdrh   X1 = 36525*(Y+4716)/100;
74068b63c01Sdrh   X2 = 306001*(M+1)/10000;
74168b63c01Sdrh   A = Y/100;
74268b63c01Sdrh   B = 2 - A + (A/4);
74368b63c01Sdrh   JDsec = (i64)((X1 + X2 + D + B - 1524.5)*86400) + hr*3600 + min*60 + sec;
74468b63c01Sdrh   return (u32)(JDsec - (i64)24405875*(i64)8640);
7458558ef2eSdan }
7468558ef2eSdan 
747a07aa8d3Sdan /*
748a07aa8d3Sdan ** The opposite of zipfileMtime(). This function populates the mTime and
749a07aa8d3Sdan ** mDate fields of the CDS structure passed as the first argument according
750a07aa8d3Sdan ** to the UNIX timestamp value passed as the second.
751a07aa8d3Sdan */
zipfileMtimeToDos(ZipfileCDS * pCds,u32 mUnixTime)752a07aa8d3Sdan static void zipfileMtimeToDos(ZipfileCDS *pCds, u32 mUnixTime){
75344091ed3Sdan   /* Convert unix timestamp to JD (2440588 is noon on 1/1/1970) */
75444091ed3Sdan   i64 JD = (i64)2440588 + mUnixTime / (24*60*60);
7559ebfaad2Sdan 
75644091ed3Sdan   int A, B, C, D, E;
75744091ed3Sdan   int yr, mon, day;
75844091ed3Sdan   int hr, min, sec;
759a07aa8d3Sdan 
76044091ed3Sdan   A = (int)((JD - 1867216.25)/36524.25);
7615facffbcSmistachkin   A = (int)(JD + 1 + A - (A/4));
76244091ed3Sdan   B = A + 1524;
76344091ed3Sdan   C = (int)((B - 122.1)/365.25);
76444091ed3Sdan   D = (36525*(C&32767))/100;
76544091ed3Sdan   E = (int)((B-D)/30.6001);
766a07aa8d3Sdan 
76744091ed3Sdan   day = B - D - (int)(30.6001*E);
76844091ed3Sdan   mon = (E<14 ? E-1 : E-13);
76944091ed3Sdan   yr = mon>2 ? C-4716 : C-4715;
77044091ed3Sdan 
77144091ed3Sdan   hr = (mUnixTime % (24*60*60)) / (60*60);
77244091ed3Sdan   min = (mUnixTime % (60*60)) / 60;
77344091ed3Sdan   sec = (mUnixTime % 60);
77444091ed3Sdan 
77570acb0aaSdrh   if( yr>=1980 ){
77644091ed3Sdan     pCds->mDate = (u16)(day + (mon << 5) + ((yr-1980) << 9));
77744091ed3Sdan     pCds->mTime = (u16)(sec/2 + (min<<5) + (hr<<11));
77870acb0aaSdrh   }else{
77970acb0aaSdrh     pCds->mDate = pCds->mTime = 0;
78070acb0aaSdrh   }
78144091ed3Sdan 
78244091ed3Sdan   assert( mUnixTime<315507600
78344091ed3Sdan        || mUnixTime==zipfileMtime(pCds)
78444091ed3Sdan        || ((mUnixTime % 2) && mUnixTime-1==zipfileMtime(pCds))
78544091ed3Sdan        /* || (mUnixTime % 2) */
78644091ed3Sdan   );
787f42884c3Sdan }
7889ebfaad2Sdan 
7898005d605Sdan /*
790a07aa8d3Sdan ** If aBlob is not NULL, then it is a pointer to a buffer (nBlob bytes in
791a07aa8d3Sdan ** size) containing an entire zip archive image. Or, if aBlob is NULL,
792a07aa8d3Sdan ** then pFile is a file-handle open on a zip file. In either case, this
793a07aa8d3Sdan ** function creates a ZipfileEntry object based on the zip archive entry
794a07aa8d3Sdan ** for which the CDS record is at offset iOff.
795a07aa8d3Sdan **
796a07aa8d3Sdan ** If successful, SQLITE_OK is returned and (*ppEntry) set to point to
797a07aa8d3Sdan ** the new object. Otherwise, an SQLite error code is returned and the
798a07aa8d3Sdan ** final value of (*ppEntry) undefined.
7998005d605Sdan */
zipfileGetEntry(ZipfileTab * pTab,const u8 * aBlob,int nBlob,FILE * pFile,i64 iOff,ZipfileEntry ** ppEntry)8008005d605Sdan static int zipfileGetEntry(
8018005d605Sdan   ZipfileTab *pTab,               /* Store any error message here */
8028005d605Sdan   const u8 *aBlob,                /* Pointer to in-memory file image */
8038005d605Sdan   int nBlob,                      /* Size of aBlob[] in bytes */
8048005d605Sdan   FILE *pFile,                    /* If aBlob==0, read from this file */
805a07aa8d3Sdan   i64 iOff,                       /* Offset of CDS record */
806a07aa8d3Sdan   ZipfileEntry **ppEntry          /* OUT: Pointer to new object */
8078005d605Sdan ){
8088005d605Sdan   u8 *aRead;
8098005d605Sdan   char **pzErr = &pTab->base.zErrMsg;
8108005d605Sdan   int rc = SQLITE_OK;
8118005d605Sdan 
8128005d605Sdan   if( aBlob==0 ){
81326333ee3Sdan     aRead = pTab->aBuffer;
8148005d605Sdan     rc = zipfileReadData(pFile, aRead, ZIPFILE_CDS_FIXED_SZ, iOff, pzErr);
8158005d605Sdan   }else{
8168005d605Sdan     aRead = (u8*)&aBlob[iOff];
8178005d605Sdan   }
8188005d605Sdan 
8198005d605Sdan   if( rc==SQLITE_OK ){
8202d77d80aSdrh     sqlite3_int64 nAlloc;
8218005d605Sdan     ZipfileEntry *pNew;
8228005d605Sdan 
8238005d605Sdan     int nFile = zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF]);
8248005d605Sdan     int nExtra = zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF+2]);
8258005d605Sdan     nExtra += zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF+4]);
8268005d605Sdan 
8278005d605Sdan     nAlloc = sizeof(ZipfileEntry) + nExtra;
8288005d605Sdan     if( aBlob ){
8298005d605Sdan       nAlloc += zipfileGetU32(&aRead[ZIPFILE_CDS_SZCOMPRESSED_OFF]);
8308005d605Sdan     }
8318005d605Sdan 
8322d77d80aSdrh     pNew = (ZipfileEntry*)sqlite3_malloc64(nAlloc);
8338005d605Sdan     if( pNew==0 ){
8348005d605Sdan       rc = SQLITE_NOMEM;
8358005d605Sdan     }else{
8368005d605Sdan       memset(pNew, 0, sizeof(ZipfileEntry));
8378005d605Sdan       rc = zipfileReadCDS(aRead, &pNew->cds);
8388005d605Sdan       if( rc!=SQLITE_OK ){
8398005d605Sdan         *pzErr = sqlite3_mprintf("failed to read CDS at offset %lld", iOff);
8408005d605Sdan       }else if( aBlob==0 ){
8418005d605Sdan         rc = zipfileReadData(
8428005d605Sdan             pFile, aRead, nExtra+nFile, iOff+ZIPFILE_CDS_FIXED_SZ, pzErr
8438005d605Sdan         );
8448005d605Sdan       }else{
8458005d605Sdan         aRead = (u8*)&aBlob[iOff + ZIPFILE_CDS_FIXED_SZ];
8468005d605Sdan       }
8478005d605Sdan     }
8488005d605Sdan 
8498005d605Sdan     if( rc==SQLITE_OK ){
8508005d605Sdan       u32 *pt = &pNew->mUnixTime;
8518005d605Sdan       pNew->cds.zFile = sqlite3_mprintf("%.*s", nFile, aRead);
8528005d605Sdan       pNew->aExtra = (u8*)&pNew[1];
8538005d605Sdan       memcpy(pNew->aExtra, &aRead[nFile], nExtra);
8548005d605Sdan       if( pNew->cds.zFile==0 ){
8558005d605Sdan         rc = SQLITE_NOMEM;
8568005d605Sdan       }else if( 0==zipfileScanExtra(&aRead[nFile], pNew->cds.nExtra, pt) ){
8578005d605Sdan         pNew->mUnixTime = zipfileMtime(&pNew->cds);
8588005d605Sdan       }
8598005d605Sdan     }
8608005d605Sdan 
8618005d605Sdan     if( rc==SQLITE_OK ){
8628005d605Sdan       static const int szFix = ZIPFILE_LFH_FIXED_SZ;
8638005d605Sdan       ZipfileLFH lfh;
8648005d605Sdan       if( pFile ){
8658005d605Sdan         rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr);
8668005d605Sdan       }else{
867c86caa58Sdrh         aRead = (u8*)&aBlob[pNew->cds.iOffset];
8688005d605Sdan       }
8698005d605Sdan 
870e85e1da0Sdrh       if( rc==SQLITE_OK ) rc = zipfileReadLFH(aRead, &lfh);
8718005d605Sdan       if( rc==SQLITE_OK ){
8728005d605Sdan         pNew->iDataOff =  pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ;
8738005d605Sdan         pNew->iDataOff += lfh.nFile + lfh.nExtra;
8748005d605Sdan         if( aBlob && pNew->cds.szCompressed ){
8758005d605Sdan           pNew->aData = &pNew->aExtra[nExtra];
8768005d605Sdan           memcpy(pNew->aData, &aBlob[pNew->iDataOff], pNew->cds.szCompressed);
8778005d605Sdan         }
8788005d605Sdan       }else{
8798005d605Sdan         *pzErr = sqlite3_mprintf("failed to read LFH at offset %d",
8808005d605Sdan             (int)pNew->cds.iOffset
8818005d605Sdan         );
8828005d605Sdan       }
8838005d605Sdan     }
8848005d605Sdan 
8858005d605Sdan     if( rc!=SQLITE_OK ){
8868005d605Sdan       zipfileEntryFree(pNew);
8878005d605Sdan     }else{
8888005d605Sdan       *ppEntry = pNew;
8898005d605Sdan     }
8908005d605Sdan   }
8918005d605Sdan 
8929ebfaad2Sdan   return rc;
8939ebfaad2Sdan }
8949ebfaad2Sdan 
8959ebfaad2Sdan /*
8969ebfaad2Sdan ** Advance an ZipfileCsr to its next row of output.
8979ebfaad2Sdan */
zipfileNext(sqlite3_vtab_cursor * cur)8989ebfaad2Sdan static int zipfileNext(sqlite3_vtab_cursor *cur){
8999ebfaad2Sdan   ZipfileCsr *pCsr = (ZipfileCsr*)cur;
9009ebfaad2Sdan   int rc = SQLITE_OK;
9019ebfaad2Sdan 
9028558ef2eSdan   if( pCsr->pFile ){
9030cde0c62Sdan     i64 iEof = pCsr->eocd.iOffset + pCsr->eocd.nSize;
9048558ef2eSdan     zipfileEntryFree(pCsr->pCurrent);
9058558ef2eSdan     pCsr->pCurrent = 0;
9069ebfaad2Sdan     if( pCsr->iNextOff>=iEof ){
9079ebfaad2Sdan       pCsr->bEof = 1;
9088558ef2eSdan     }else{
9098558ef2eSdan       ZipfileEntry *p = 0;
9108558ef2eSdan       ZipfileTab *pTab = (ZipfileTab*)(cur->pVtab);
9118005d605Sdan       rc = zipfileGetEntry(pTab, 0, 0, pCsr->pFile, pCsr->iNextOff, &p);
9128558ef2eSdan       if( rc==SQLITE_OK ){
9138558ef2eSdan         pCsr->iNextOff += ZIPFILE_CDS_FIXED_SZ;
9148558ef2eSdan         pCsr->iNextOff += (int)p->cds.nExtra + p->cds.nFile + p->cds.nComment;
9158558ef2eSdan       }
9168558ef2eSdan       pCsr->pCurrent = p;
9170cde0c62Sdan     }
9189ebfaad2Sdan   }else{
9198558ef2eSdan     if( !pCsr->bNoop ){
9200cde0c62Sdan       pCsr->pCurrent = pCsr->pCurrent->pNext;
9218558ef2eSdan     }
9220cde0c62Sdan     if( pCsr->pCurrent==0 ){
9230cde0c62Sdan       pCsr->bEof = 1;
9240cde0c62Sdan     }
9250cde0c62Sdan   }
9260cde0c62Sdan 
9278558ef2eSdan   pCsr->bNoop = 0;
9289ebfaad2Sdan   return rc;
9299ebfaad2Sdan }
9309ebfaad2Sdan 
zipfileFree(void * p)931a07aa8d3Sdan static void zipfileFree(void *p) {
932a07aa8d3Sdan   sqlite3_free(p);
933373dc3bbSdan }
934373dc3bbSdan 
935a07aa8d3Sdan /*
936a07aa8d3Sdan ** Buffer aIn (size nIn bytes) contains compressed data. Uncompressed, the
937a07aa8d3Sdan ** size is nOut bytes. This function uncompresses the data and sets the
938a07aa8d3Sdan ** return value in context pCtx to the result (a blob).
939a07aa8d3Sdan **
940a07aa8d3Sdan ** If an error occurs, an error code is left in pCtx instead.
941a07aa8d3Sdan */
zipfileInflate(sqlite3_context * pCtx,const u8 * aIn,int nIn,int nOut)9427c15ac1aSdan static void zipfileInflate(
9432f7260deSdan   sqlite3_context *pCtx,          /* Store result here */
9447c15ac1aSdan   const u8 *aIn,                  /* Compressed data */
9457c15ac1aSdan   int nIn,                        /* Size of buffer aIn[] in bytes */
9467c15ac1aSdan   int nOut                        /* Expected output size */
9477c15ac1aSdan ){
9487c15ac1aSdan   u8 *aRes = sqlite3_malloc(nOut);
9497c15ac1aSdan   if( aRes==0 ){
9507c15ac1aSdan     sqlite3_result_error_nomem(pCtx);
9517c15ac1aSdan   }else{
9527c15ac1aSdan     int err;
9537c15ac1aSdan     z_stream str;
9547c15ac1aSdan     memset(&str, 0, sizeof(str));
9557c15ac1aSdan 
9567c15ac1aSdan     str.next_in = (Byte*)aIn;
9577c15ac1aSdan     str.avail_in = nIn;
9587c15ac1aSdan     str.next_out = (Byte*)aRes;
9597c15ac1aSdan     str.avail_out = nOut;
9607c15ac1aSdan 
9617c15ac1aSdan     err = inflateInit2(&str, -15);
9627c15ac1aSdan     if( err!=Z_OK ){
9637c15ac1aSdan       zipfileCtxErrorMsg(pCtx, "inflateInit2() failed (%d)", err);
9647c15ac1aSdan     }else{
9657c15ac1aSdan       err = inflate(&str, Z_NO_FLUSH);
9667c15ac1aSdan       if( err!=Z_STREAM_END ){
9677c15ac1aSdan         zipfileCtxErrorMsg(pCtx, "inflate() failed (%d)", err);
9687c15ac1aSdan       }else{
969a07aa8d3Sdan         sqlite3_result_blob(pCtx, aRes, nOut, zipfileFree);
970a07aa8d3Sdan         aRes = 0;
9717c15ac1aSdan       }
9727c15ac1aSdan     }
9737c15ac1aSdan     sqlite3_free(aRes);
9747c15ac1aSdan     inflateEnd(&str);
9757c15ac1aSdan   }
9767c15ac1aSdan }
9777c15ac1aSdan 
978a07aa8d3Sdan /*
979a07aa8d3Sdan ** Buffer aIn (size nIn bytes) contains uncompressed data. This function
980a07aa8d3Sdan ** compresses it and sets (*ppOut) to point to a buffer containing the
981a07aa8d3Sdan ** compressed data. The caller is responsible for eventually calling
982a07aa8d3Sdan ** sqlite3_free() to release buffer (*ppOut). Before returning, (*pnOut)
983a07aa8d3Sdan ** is set to the size of buffer (*ppOut) in bytes.
984a07aa8d3Sdan **
985a07aa8d3Sdan ** If no error occurs, SQLITE_OK is returned. Otherwise, an SQLite error
986a07aa8d3Sdan ** code is returned and an error message left in virtual-table handle
987a07aa8d3Sdan ** pTab. The values of (*ppOut) and (*pnOut) are left unchanged in this
988a07aa8d3Sdan ** case.
989a07aa8d3Sdan */
zipfileDeflate(const u8 * aIn,int nIn,u8 ** ppOut,int * pnOut,char ** pzErr)9907c15ac1aSdan static int zipfileDeflate(
9917c15ac1aSdan   const u8 *aIn, int nIn,         /* Input */
992f8c4b99aSdan   u8 **ppOut, int *pnOut,         /* Output */
993f8c4b99aSdan   char **pzErr                    /* OUT: Error message */
9947c15ac1aSdan ){
99588a1d6b9Smistachkin   int rc = SQLITE_OK;
996d681626aSdan   sqlite3_int64 nAlloc;
997d681626aSdan   z_stream str;
998d681626aSdan   u8 *aOut;
9997c15ac1aSdan 
1000d681626aSdan   memset(&str, 0, sizeof(str));
1001d681626aSdan   str.next_in = (Bytef*)aIn;
1002d681626aSdan   str.avail_in = nIn;
1003d681626aSdan   deflateInit2(&str, 9, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
1004d681626aSdan 
1005d681626aSdan   nAlloc = deflateBound(&str, nIn);
10062d77d80aSdrh   aOut = (u8*)sqlite3_malloc64(nAlloc);
10077c15ac1aSdan   if( aOut==0 ){
10087c15ac1aSdan     rc = SQLITE_NOMEM;
10097c15ac1aSdan   }else{
10107c15ac1aSdan     int res;
10117c15ac1aSdan     str.next_out = aOut;
10127c15ac1aSdan     str.avail_out = nAlloc;
10137c15ac1aSdan     res = deflate(&str, Z_FINISH);
10147c15ac1aSdan     if( res==Z_STREAM_END ){
10157c15ac1aSdan       *ppOut = aOut;
10167c15ac1aSdan       *pnOut = (int)str.total_out;
10177c15ac1aSdan     }else{
10187c15ac1aSdan       sqlite3_free(aOut);
1019f8c4b99aSdan       *pzErr = sqlite3_mprintf("zipfile: deflate() error");
10207c15ac1aSdan       rc = SQLITE_ERROR;
10217c15ac1aSdan     }
10227c15ac1aSdan     deflateEnd(&str);
10237c15ac1aSdan   }
10247c15ac1aSdan 
10257c15ac1aSdan   return rc;
10267c15ac1aSdan }
10277c15ac1aSdan 
10287c15ac1aSdan 
10299ebfaad2Sdan /*
10309ebfaad2Sdan ** Return values of columns for the row at which the series_cursor
10319ebfaad2Sdan ** is currently pointing.
10329ebfaad2Sdan */
zipfileColumn(sqlite3_vtab_cursor * cur,sqlite3_context * ctx,int i)10339ebfaad2Sdan static int zipfileColumn(
10349ebfaad2Sdan   sqlite3_vtab_cursor *cur,   /* The cursor */
10359ebfaad2Sdan   sqlite3_context *ctx,       /* First argument to sqlite3_result_...() */
10369ebfaad2Sdan   int i                       /* Which column to return */
10379ebfaad2Sdan ){
10389ebfaad2Sdan   ZipfileCsr *pCsr = (ZipfileCsr*)cur;
10398558ef2eSdan   ZipfileCDS *pCDS = &pCsr->pCurrent->cds;
10409ebfaad2Sdan   int rc = SQLITE_OK;
10419ebfaad2Sdan   switch( i ){
10429ebfaad2Sdan     case 0:   /* name */
10438558ef2eSdan       sqlite3_result_text(ctx, pCDS->zFile, -1, SQLITE_TRANSIENT);
10449ebfaad2Sdan       break;
10459ebfaad2Sdan     case 1:   /* mode */
10469ebfaad2Sdan       /* TODO: Whether or not the following is correct surely depends on
10479ebfaad2Sdan       ** the platform on which the archive was created.  */
10488558ef2eSdan       sqlite3_result_int(ctx, pCDS->iExternalAttr >> 16);
10499ebfaad2Sdan       break;
10509ebfaad2Sdan     case 2: { /* mtime */
10518558ef2eSdan       sqlite3_result_int64(ctx, pCsr->pCurrent->mUnixTime);
10529ebfaad2Sdan       break;
10539ebfaad2Sdan     }
10549ebfaad2Sdan     case 3: { /* sz */
10552d620070Sdan       if( sqlite3_vtab_nochange(ctx)==0 ){
10568558ef2eSdan         sqlite3_result_int64(ctx, pCDS->szUncompressed);
10572d620070Sdan       }
10589ebfaad2Sdan       break;
10599ebfaad2Sdan     }
10607c15ac1aSdan     case 4:   /* rawdata */
10612d620070Sdan       if( sqlite3_vtab_nochange(ctx) ) break;
10627c15ac1aSdan     case 5: { /* data */
10638558ef2eSdan       if( i==4 || pCDS->iCompression==0 || pCDS->iCompression==8 ){
10648558ef2eSdan         int sz = pCDS->szCompressed;
10658558ef2eSdan         int szFinal = pCDS->szUncompressed;
106689fa7469Sdan         if( szFinal>0 ){
10678005d605Sdan           u8 *aBuf;
10688005d605Sdan           u8 *aFree = 0;
10698005d605Sdan           if( pCsr->pCurrent->aData ){
10708005d605Sdan             aBuf = pCsr->pCurrent->aData;
10718005d605Sdan           }else{
10722d77d80aSdrh             aBuf = aFree = sqlite3_malloc64(sz);
10739ebfaad2Sdan             if( aBuf==0 ){
10749ebfaad2Sdan               rc = SQLITE_NOMEM;
10759ebfaad2Sdan             }else{
1076a07aa8d3Sdan               FILE *pFile = pCsr->pFile;
1077a07aa8d3Sdan               if( pFile==0 ){
1078a07aa8d3Sdan                 pFile = ((ZipfileTab*)(pCsr->base.pVtab))->pWriteFd;
1079a07aa8d3Sdan               }
10808005d605Sdan               rc = zipfileReadData(pFile, aBuf, sz, pCsr->pCurrent->iDataOff,
1081373dc3bbSdan                   &pCsr->base.pVtab->zErrMsg
1082373dc3bbSdan               );
10839ebfaad2Sdan             }
10848005d605Sdan           }
10859ebfaad2Sdan           if( rc==SQLITE_OK ){
10868558ef2eSdan             if( i==5 && pCDS->iCompression ){
108789fa7469Sdan               zipfileInflate(ctx, aBuf, sz, szFinal);
10887c15ac1aSdan             }else{
10899ebfaad2Sdan               sqlite3_result_blob(ctx, aBuf, sz, SQLITE_TRANSIENT);
10907c15ac1aSdan             }
10919ebfaad2Sdan           }
10928005d605Sdan           sqlite3_free(aFree);
109389fa7469Sdan         }else{
109489fa7469Sdan           /* Figure out if this is a directory or a zero-sized file. Consider
109589fa7469Sdan           ** it to be a directory either if the mode suggests so, or if
109689fa7469Sdan           ** the final character in the name is '/'.  */
10978558ef2eSdan           u32 mode = pCDS->iExternalAttr >> 16;
10988558ef2eSdan           if( !(mode & S_IFDIR) && pCDS->zFile[pCDS->nFile-1]!='/' ){
109989fa7469Sdan             sqlite3_result_blob(ctx, "", 0, SQLITE_STATIC);
110089fa7469Sdan           }
11019ebfaad2Sdan         }
11027c15ac1aSdan       }
11039ebfaad2Sdan       break;
11049ebfaad2Sdan     }
11057c15ac1aSdan     case 6:   /* method */
11068558ef2eSdan       sqlite3_result_int(ctx, pCDS->iCompression);
11079ebfaad2Sdan       break;
110815daa6b5Sdan     default:  /* z */
110915daa6b5Sdan       assert( i==7 );
111089fa7469Sdan       sqlite3_result_int64(ctx, pCsr->iId);
111189fa7469Sdan       break;
11129ebfaad2Sdan   }
11139ebfaad2Sdan 
11142d620070Sdan   return rc;
11159ebfaad2Sdan }
11169ebfaad2Sdan 
11179ebfaad2Sdan /*
1118a07aa8d3Sdan ** Return TRUE if the cursor is at EOF.
11199ebfaad2Sdan */
zipfileEof(sqlite3_vtab_cursor * cur)11209ebfaad2Sdan static int zipfileEof(sqlite3_vtab_cursor *cur){
11219ebfaad2Sdan   ZipfileCsr *pCsr = (ZipfileCsr*)cur;
11229ebfaad2Sdan   return pCsr->bEof;
11239ebfaad2Sdan }
11249ebfaad2Sdan 
11259ebfaad2Sdan /*
1126a07aa8d3Sdan ** If aBlob is not NULL, then it points to a buffer nBlob bytes in size
1127a07aa8d3Sdan ** containing an entire zip archive image. Or, if aBlob is NULL, then pFile
1128a07aa8d3Sdan ** is guaranteed to be a file-handle open on a zip file.
1129a07aa8d3Sdan **
1130a07aa8d3Sdan ** This function attempts to locate the EOCD record within the zip archive
1131a07aa8d3Sdan ** and populate *pEOCD with the results of decoding it. SQLITE_OK is
1132a07aa8d3Sdan ** returned if successful. Otherwise, an SQLite error code is returned and
1133a07aa8d3Sdan ** an English language error message may be left in virtual-table pTab.
11349ebfaad2Sdan */
zipfileReadEOCD(ZipfileTab * pTab,const u8 * aBlob,int nBlob,FILE * pFile,ZipfileEOCD * pEOCD)1135373dc3bbSdan static int zipfileReadEOCD(
1136373dc3bbSdan   ZipfileTab *pTab,               /* Return errors here */
11378005d605Sdan   const u8 *aBlob,                /* Pointer to in-memory file image */
11388005d605Sdan   int nBlob,                      /* Size of aBlob[] in bytes */
11398005d605Sdan   FILE *pFile,                    /* Read from this file if aBlob==0 */
1140373dc3bbSdan   ZipfileEOCD *pEOCD              /* Object to populate */
1141373dc3bbSdan ){
1142373dc3bbSdan   u8 *aRead = pTab->aBuffer;      /* Temporary buffer */
1143373dc3bbSdan   int nRead;                      /* Bytes to read from file */
11448005d605Sdan   int rc = SQLITE_OK;
11459ebfaad2Sdan 
1146e85e1da0Sdrh   memset(pEOCD, 0, sizeof(ZipfileEOCD));
11478005d605Sdan   if( aBlob==0 ){
11488005d605Sdan     i64 iOff;                     /* Offset to read from */
11498005d605Sdan     i64 szFile;                   /* Total size of file in bytes */
1150373dc3bbSdan     fseek(pFile, 0, SEEK_END);
1151373dc3bbSdan     szFile = (i64)ftell(pFile);
1152373dc3bbSdan     if( szFile==0 ){
11532d620070Sdan       return SQLITE_OK;
1154373dc3bbSdan     }
1155373dc3bbSdan     nRead = (int)(MIN(szFile, ZIPFILE_BUFFER_SIZE));
1156373dc3bbSdan     iOff = szFile - nRead;
1157dfdfd8c7Smistachkin     rc = zipfileReadData(pFile, aRead, nRead, iOff, &pTab->base.zErrMsg);
11588005d605Sdan   }else{
11598005d605Sdan     nRead = (int)(MIN(nBlob, ZIPFILE_BUFFER_SIZE));
11608005d605Sdan     aRead = (u8*)&aBlob[nBlob-nRead];
11618005d605Sdan   }
11628005d605Sdan 
11639ebfaad2Sdan   if( rc==SQLITE_OK ){
11649ebfaad2Sdan     int i;
11659ebfaad2Sdan 
11669ebfaad2Sdan     /* Scan backwards looking for the signature bytes */
11679ebfaad2Sdan     for(i=nRead-20; i>=0; i--){
11689ebfaad2Sdan       if( aRead[i]==0x50 && aRead[i+1]==0x4b
11699ebfaad2Sdan        && aRead[i+2]==0x05 && aRead[i+3]==0x06
11709ebfaad2Sdan       ){
11719ebfaad2Sdan         break;
11729ebfaad2Sdan       }
11739ebfaad2Sdan     }
11749ebfaad2Sdan     if( i<0 ){
1175373dc3bbSdan       pTab->base.zErrMsg = sqlite3_mprintf(
1176373dc3bbSdan           "cannot find end of central directory record"
1177373dc3bbSdan       );
11789ebfaad2Sdan       return SQLITE_ERROR;
11799ebfaad2Sdan     }
11809ebfaad2Sdan 
11819ebfaad2Sdan     aRead += i+4;
11829ebfaad2Sdan     pEOCD->iDisk = zipfileRead16(aRead);
11839ebfaad2Sdan     pEOCD->iFirstDisk = zipfileRead16(aRead);
11849ebfaad2Sdan     pEOCD->nEntry = zipfileRead16(aRead);
11859ebfaad2Sdan     pEOCD->nEntryTotal = zipfileRead16(aRead);
11869ebfaad2Sdan     pEOCD->nSize = zipfileRead32(aRead);
11879ebfaad2Sdan     pEOCD->iOffset = zipfileRead32(aRead);
11889ebfaad2Sdan   }
11899ebfaad2Sdan 
11902f7260deSdan   return rc;
11919ebfaad2Sdan }
11929ebfaad2Sdan 
11939ebfaad2Sdan /*
1194a07aa8d3Sdan ** Add object pNew to the linked list that begins at ZipfileTab.pFirstEntry
1195a07aa8d3Sdan ** and ends with pLastEntry. If argument pBefore is NULL, then pNew is added
1196a07aa8d3Sdan ** to the end of the list. Otherwise, it is added to the list immediately
1197a07aa8d3Sdan ** before pBefore (which is guaranteed to be a part of said list).
11988005d605Sdan */
zipfileAddEntry(ZipfileTab * pTab,ZipfileEntry * pBefore,ZipfileEntry * pNew)11998005d605Sdan static void zipfileAddEntry(
12008005d605Sdan   ZipfileTab *pTab,
12018005d605Sdan   ZipfileEntry *pBefore,
12028005d605Sdan   ZipfileEntry *pNew
12038005d605Sdan ){
12048005d605Sdan   assert( (pTab->pFirstEntry==0)==(pTab->pLastEntry==0) );
12058005d605Sdan   assert( pNew->pNext==0 );
12068005d605Sdan   if( pBefore==0 ){
12078005d605Sdan     if( pTab->pFirstEntry==0 ){
12088005d605Sdan       pTab->pFirstEntry = pTab->pLastEntry = pNew;
12098005d605Sdan     }else{
12108005d605Sdan       assert( pTab->pLastEntry->pNext==0 );
12118005d605Sdan       pTab->pLastEntry->pNext = pNew;
12128005d605Sdan       pTab->pLastEntry = pNew;
12138005d605Sdan     }
12148005d605Sdan   }else{
12158005d605Sdan     ZipfileEntry **pp;
12168005d605Sdan     for(pp=&pTab->pFirstEntry; *pp!=pBefore; pp=&((*pp)->pNext));
12178005d605Sdan     pNew->pNext = pBefore;
12188005d605Sdan     *pp = pNew;
12198005d605Sdan   }
12208005d605Sdan }
12218005d605Sdan 
zipfileLoadDirectory(ZipfileTab * pTab,const u8 * aBlob,int nBlob)12228005d605Sdan static int zipfileLoadDirectory(ZipfileTab *pTab, const u8 *aBlob, int nBlob){
12238005d605Sdan   ZipfileEOCD eocd;
12248005d605Sdan   int rc;
12258005d605Sdan   int i;
12268005d605Sdan   i64 iOff;
12278005d605Sdan 
12288005d605Sdan   rc = zipfileReadEOCD(pTab, aBlob, nBlob, pTab->pWriteFd, &eocd);
12298005d605Sdan   iOff = eocd.iOffset;
12308005d605Sdan   for(i=0; rc==SQLITE_OK && i<eocd.nEntry; i++){
12318005d605Sdan     ZipfileEntry *pNew = 0;
12328005d605Sdan     rc = zipfileGetEntry(pTab, aBlob, nBlob, pTab->pWriteFd, iOff, &pNew);
12338005d605Sdan 
12348005d605Sdan     if( rc==SQLITE_OK ){
12358005d605Sdan       zipfileAddEntry(pTab, 0, pNew);
12368005d605Sdan       iOff += ZIPFILE_CDS_FIXED_SZ;
12378005d605Sdan       iOff += (int)pNew->cds.nExtra + pNew->cds.nFile + pNew->cds.nComment;
12388005d605Sdan     }
12398005d605Sdan   }
12408005d605Sdan   return rc;
12418005d605Sdan }
12428005d605Sdan 
12438005d605Sdan /*
12449ebfaad2Sdan ** xFilter callback.
12459ebfaad2Sdan */
zipfileFilter(sqlite3_vtab_cursor * cur,int idxNum,const char * idxStr,int argc,sqlite3_value ** argv)12469ebfaad2Sdan static int zipfileFilter(
12479ebfaad2Sdan   sqlite3_vtab_cursor *cur,
12489ebfaad2Sdan   int idxNum, const char *idxStr,
12499ebfaad2Sdan   int argc, sqlite3_value **argv
12509ebfaad2Sdan ){
1251373dc3bbSdan   ZipfileTab *pTab = (ZipfileTab*)cur->pVtab;
12529ebfaad2Sdan   ZipfileCsr *pCsr = (ZipfileCsr*)cur;
125315daa6b5Sdan   const char *zFile = 0;          /* Zip file to scan */
12549ebfaad2Sdan   int rc = SQLITE_OK;             /* Return Code */
12558558ef2eSdan   int bInMemory = 0;              /* True for an in-memory zipfile */
12569ebfaad2Sdan 
12579ebfaad2Sdan   zipfileResetCursor(pCsr);
12589ebfaad2Sdan 
12590cde0c62Sdan   if( pTab->zFile ){
1260373dc3bbSdan     zFile = pTab->zFile;
12610cde0c62Sdan   }else if( idxNum==0 ){
126241a6f2cbSdrh     zipfileCursorErr(pCsr, "zipfile() function requires an argument");
126315daa6b5Sdan     return SQLITE_ERROR;
12648558ef2eSdan   }else if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){
1265*d5c35f6dSdrh     static const u8 aEmptyBlob = 0;
12668005d605Sdan     const u8 *aBlob = (const u8*)sqlite3_value_blob(argv[0]);
12678005d605Sdan     int nBlob = sqlite3_value_bytes(argv[0]);
12688005d605Sdan     assert( pTab->pFirstEntry==0 );
1269*d5c35f6dSdrh     if( aBlob==0 ){
1270*d5c35f6dSdrh       aBlob = &aEmptyBlob;
1271*d5c35f6dSdrh       nBlob = 0;
1272*d5c35f6dSdrh     }
12738005d605Sdan     rc = zipfileLoadDirectory(pTab, aBlob, nBlob);
12748005d605Sdan     pCsr->pFreeEntry = pTab->pFirstEntry;
12758005d605Sdan     pTab->pFirstEntry = pTab->pLastEntry = 0;
12768005d605Sdan     if( rc!=SQLITE_OK ) return rc;
12778005d605Sdan     bInMemory = 1;
1278373dc3bbSdan   }else{
1279373dc3bbSdan     zFile = (const char*)sqlite3_value_text(argv[0]);
1280373dc3bbSdan   }
12810cde0c62Sdan 
12828558ef2eSdan   if( 0==pTab->pWriteFd && 0==bInMemory ){
12839ebfaad2Sdan     pCsr->pFile = fopen(zFile, "rb");
12849ebfaad2Sdan     if( pCsr->pFile==0 ){
128541a6f2cbSdrh       zipfileCursorErr(pCsr, "cannot open file: %s", zFile);
12869ebfaad2Sdan       rc = SQLITE_ERROR;
12879ebfaad2Sdan     }else{
12888005d605Sdan       rc = zipfileReadEOCD(pTab, 0, 0, pCsr->pFile, &pCsr->eocd);
12899ebfaad2Sdan       if( rc==SQLITE_OK ){
12902d620070Sdan         if( pCsr->eocd.nEntry==0 ){
12912d620070Sdan           pCsr->bEof = 1;
12922d620070Sdan         }else{
12939ebfaad2Sdan           pCsr->iNextOff = pCsr->eocd.iOffset;
12949ebfaad2Sdan           rc = zipfileNext(cur);
12952d620070Sdan         }
12969ebfaad2Sdan       }
12979ebfaad2Sdan     }
12980cde0c62Sdan   }else{
12998558ef2eSdan     pCsr->bNoop = 1;
13008005d605Sdan     pCsr->pCurrent = pCsr->pFreeEntry ? pCsr->pFreeEntry : pTab->pFirstEntry;
13010cde0c62Sdan     rc = zipfileNext(cur);
13020cde0c62Sdan   }
13039ebfaad2Sdan 
13049ebfaad2Sdan   return rc;
13059ebfaad2Sdan }
13069ebfaad2Sdan 
13079ebfaad2Sdan /*
13089ebfaad2Sdan ** xBestIndex callback.
13099ebfaad2Sdan */
zipfileBestIndex(sqlite3_vtab * tab,sqlite3_index_info * pIdxInfo)13109ebfaad2Sdan static int zipfileBestIndex(
13119ebfaad2Sdan   sqlite3_vtab *tab,
13129ebfaad2Sdan   sqlite3_index_info *pIdxInfo
13139ebfaad2Sdan ){
13149ebfaad2Sdan   int i;
131520b3fc4dSdrh   int idx = -1;
131620b3fc4dSdrh   int unusable = 0;
13179ebfaad2Sdan 
13189ebfaad2Sdan   for(i=0; i<pIdxInfo->nConstraint; i++){
13199ebfaad2Sdan     const struct sqlite3_index_constraint *pCons = &pIdxInfo->aConstraint[i];
13209ebfaad2Sdan     if( pCons->iColumn!=ZIPFILE_F_COLUMN_IDX ) continue;
132120b3fc4dSdrh     if( pCons->usable==0 ){
132220b3fc4dSdrh       unusable = 1;
132320b3fc4dSdrh     }else if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){
132420b3fc4dSdrh       idx = i;
13259ebfaad2Sdan     }
132620b3fc4dSdrh   }
1327d8df36bdSdrh   pIdxInfo->estimatedCost = 1000.0;
132820b3fc4dSdrh   if( idx>=0 ){
132920b3fc4dSdrh     pIdxInfo->aConstraintUsage[idx].argvIndex = 1;
133020b3fc4dSdrh     pIdxInfo->aConstraintUsage[idx].omit = 1;
13319ebfaad2Sdan     pIdxInfo->idxNum = 1;
133220b3fc4dSdrh   }else if( unusable ){
133320b3fc4dSdrh     return SQLITE_CONSTRAINT;
13349ebfaad2Sdan   }
13359ebfaad2Sdan   return SQLITE_OK;
13369ebfaad2Sdan }
13379ebfaad2Sdan 
zipfileNewEntry(const char * zPath)133815daa6b5Sdan static ZipfileEntry *zipfileNewEntry(const char *zPath){
1339373dc3bbSdan   ZipfileEntry *pNew;
134015daa6b5Sdan   pNew = sqlite3_malloc(sizeof(ZipfileEntry));
1341373dc3bbSdan   if( pNew ){
13420cde0c62Sdan     memset(pNew, 0, sizeof(ZipfileEntry));
13438558ef2eSdan     pNew->cds.zFile = sqlite3_mprintf("%s", zPath);
13448558ef2eSdan     if( pNew->cds.zFile==0 ){
13458558ef2eSdan       sqlite3_free(pNew);
13468558ef2eSdan       pNew = 0;
1347373dc3bbSdan     }
1348373dc3bbSdan   }
1349373dc3bbSdan   return pNew;
1350373dc3bbSdan }
1351373dc3bbSdan 
zipfileSerializeLFH(ZipfileEntry * pEntry,u8 * aBuf)135226333ee3Sdan static int zipfileSerializeLFH(ZipfileEntry *pEntry, u8 *aBuf){
135326333ee3Sdan   ZipfileCDS *pCds = &pEntry->cds;
135426333ee3Sdan   u8 *a = aBuf;
1355373dc3bbSdan 
13568558ef2eSdan   pCds->nExtra = 9;
13578558ef2eSdan 
135826333ee3Sdan   /* Write the LFH itself */
135926333ee3Sdan   zipfileWrite32(a, ZIPFILE_SIGNATURE_LFH);
136026333ee3Sdan   zipfileWrite16(a, pCds->iVersionExtract);
136126333ee3Sdan   zipfileWrite16(a, pCds->flags);
136226333ee3Sdan   zipfileWrite16(a, pCds->iCompression);
136326333ee3Sdan   zipfileWrite16(a, pCds->mTime);
136426333ee3Sdan   zipfileWrite16(a, pCds->mDate);
136526333ee3Sdan   zipfileWrite32(a, pCds->crc32);
136626333ee3Sdan   zipfileWrite32(a, pCds->szCompressed);
136726333ee3Sdan   zipfileWrite32(a, pCds->szUncompressed);
136826333ee3Sdan   zipfileWrite16(a, (u16)pCds->nFile);
136926333ee3Sdan   zipfileWrite16(a, pCds->nExtra);
137026333ee3Sdan   assert( a==&aBuf[ZIPFILE_LFH_FIXED_SZ] );
137126333ee3Sdan 
137226333ee3Sdan   /* Add the file name */
137326333ee3Sdan   memcpy(a, pCds->zFile, (int)pCds->nFile);
137426333ee3Sdan   a += (int)pCds->nFile;
137526333ee3Sdan 
137626333ee3Sdan   /* The "extra" data */
137726333ee3Sdan   zipfileWrite16(a, ZIPFILE_EXTRA_TIMESTAMP);
137826333ee3Sdan   zipfileWrite16(a, 5);
137926333ee3Sdan   *a++ = 0x01;
138026333ee3Sdan   zipfileWrite32(a, pEntry->mUnixTime);
138126333ee3Sdan 
138226333ee3Sdan   return a-aBuf;
1383373dc3bbSdan }
1384373dc3bbSdan 
zipfileAppendEntry(ZipfileTab * pTab,ZipfileEntry * pEntry,const u8 * pData,int nData)138526333ee3Sdan static int zipfileAppendEntry(
138626333ee3Sdan   ZipfileTab *pTab,
138726333ee3Sdan   ZipfileEntry *pEntry,
138826333ee3Sdan   const u8 *pData,
138926333ee3Sdan   int nData
139026333ee3Sdan ){
139126333ee3Sdan   u8 *aBuf = pTab->aBuffer;
139226333ee3Sdan   int nBuf;
139326333ee3Sdan   int rc;
1394373dc3bbSdan 
139526333ee3Sdan   nBuf = zipfileSerializeLFH(pEntry, aBuf);
139626333ee3Sdan   rc = zipfileAppendData(pTab, aBuf, nBuf);
1397373dc3bbSdan   if( rc==SQLITE_OK ){
139815daa6b5Sdan     pEntry->iDataOff = pTab->szCurrent;
1399373dc3bbSdan     rc = zipfileAppendData(pTab, pData, nData);
1400373dc3bbSdan   }
1401373dc3bbSdan 
1402373dc3bbSdan   return rc;
1403373dc3bbSdan }
1404373dc3bbSdan 
zipfileGetMode(sqlite3_value * pVal,int bIsDir,u32 * pMode,char ** pzErr)1405128011a2Sdan static int zipfileGetMode(
1406128011a2Sdan   sqlite3_value *pVal,
1407f8c4b99aSdan   int bIsDir,                     /* If true, default to directory */
1408f8c4b99aSdan   u32 *pMode,                     /* OUT: Mode value */
1409f8c4b99aSdan   char **pzErr                    /* OUT: Error message */
1410128011a2Sdan ){
1411373dc3bbSdan   const char *z = (const char*)sqlite3_value_text(pVal);
1412cc234a4bSdan   u32 mode = 0;
14137c15ac1aSdan   if( z==0 ){
1414f8c4b99aSdan     mode = (bIsDir ? (S_IFDIR + 0755) : (S_IFREG + 0644));
1415f42884c3Sdan   }else if( z[0]>='0' && z[0]<='9' ){
1416cc234a4bSdan     mode = (unsigned int)sqlite3_value_int(pVal);
1417373dc3bbSdan   }else{
1418dfdfd8c7Smistachkin     const char zTemplate[11] = "-rwxrwxrwx";
1419373dc3bbSdan     int i;
1420373dc3bbSdan     if( strlen(z)!=10 ) goto parse_error;
1421373dc3bbSdan     switch( z[0] ){
1422373dc3bbSdan       case '-': mode |= S_IFREG; break;
1423373dc3bbSdan       case 'd': mode |= S_IFDIR; break;
1424373dc3bbSdan       case 'l': mode |= S_IFLNK; break;
1425373dc3bbSdan       default: goto parse_error;
1426373dc3bbSdan     }
1427373dc3bbSdan     for(i=1; i<10; i++){
1428373dc3bbSdan       if( z[i]==zTemplate[i] ) mode |= 1 << (9-i);
1429373dc3bbSdan       else if( z[i]!='-' ) goto parse_error;
1430373dc3bbSdan     }
1431373dc3bbSdan   }
1432980e2cd3Sdrh   if( ((mode & S_IFDIR)==0)==bIsDir ){
1433f8c4b99aSdan     /* The "mode" attribute is a directory, but data has been specified.
1434f8c4b99aSdan     ** Or vice-versa - no data but "mode" is a file or symlink.  */
143541a6f2cbSdrh     *pzErr = sqlite3_mprintf("zipfile: mode does not match data");
1436f8c4b99aSdan     return SQLITE_CONSTRAINT;
1437f8c4b99aSdan   }
1438373dc3bbSdan   *pMode = mode;
1439373dc3bbSdan   return SQLITE_OK;
1440373dc3bbSdan 
1441373dc3bbSdan  parse_error:
1442f8c4b99aSdan   *pzErr = sqlite3_mprintf("zipfile: parse error in mode: %s", z);
1443373dc3bbSdan   return SQLITE_ERROR;
1444373dc3bbSdan }
1445373dc3bbSdan 
1446373dc3bbSdan /*
14474bfd1829Sdan ** Both (const char*) arguments point to nul-terminated strings. Argument
14484bfd1829Sdan ** nB is the value of strlen(zB). This function returns 0 if the strings are
14494bfd1829Sdan ** identical, ignoring any trailing '/' character in either path.  */
zipfileComparePath(const char * zA,const char * zB,int nB)14504bfd1829Sdan static int zipfileComparePath(const char *zA, const char *zB, int nB){
1451cc9c26a0Sdrh   int nA = (int)strlen(zA);
14528d7f44c0Sdrh   if( nA>0 && zA[nA-1]=='/' ) nA--;
14538d7f44c0Sdrh   if( nB>0 && zB[nB-1]=='/' ) nB--;
14544bfd1829Sdan   if( nA==nB && memcmp(zA, zB, nA)==0 ) return 0;
14554bfd1829Sdan   return 1;
14564bfd1829Sdan }
14574bfd1829Sdan 
zipfileBegin(sqlite3_vtab * pVtab)145815daa6b5Sdan static int zipfileBegin(sqlite3_vtab *pVtab){
145915daa6b5Sdan   ZipfileTab *pTab = (ZipfileTab*)pVtab;
146015daa6b5Sdan   int rc = SQLITE_OK;
146115daa6b5Sdan 
146215daa6b5Sdan   assert( pTab->pWriteFd==0 );
14630d21eae0Sdrh   if( pTab->zFile==0 || pTab->zFile[0]==0 ){
14640d21eae0Sdrh     pTab->base.zErrMsg = sqlite3_mprintf("zipfile: missing filename");
14650d21eae0Sdrh     return SQLITE_ERROR;
14660d21eae0Sdrh   }
146715daa6b5Sdan 
146815daa6b5Sdan   /* Open a write fd on the file. Also load the entire central directory
146915daa6b5Sdan   ** structure into memory. During the transaction any new file data is
147015daa6b5Sdan   ** appended to the archive file, but the central directory is accumulated
147115daa6b5Sdan   ** in main-memory until the transaction is committed.  */
147215daa6b5Sdan   pTab->pWriteFd = fopen(pTab->zFile, "ab+");
147315daa6b5Sdan   if( pTab->pWriteFd==0 ){
147415daa6b5Sdan     pTab->base.zErrMsg = sqlite3_mprintf(
147515daa6b5Sdan         "zipfile: failed to open file %s for writing", pTab->zFile
147615daa6b5Sdan         );
147715daa6b5Sdan     rc = SQLITE_ERROR;
147815daa6b5Sdan   }else{
147915daa6b5Sdan     fseek(pTab->pWriteFd, 0, SEEK_END);
148015daa6b5Sdan     pTab->szCurrent = pTab->szOrig = (i64)ftell(pTab->pWriteFd);
148115daa6b5Sdan     rc = zipfileLoadDirectory(pTab, 0, 0);
148215daa6b5Sdan   }
148315daa6b5Sdan 
148415daa6b5Sdan   if( rc!=SQLITE_OK ){
148515daa6b5Sdan     zipfileCleanupTransaction(pTab);
148615daa6b5Sdan   }
148715daa6b5Sdan 
148815daa6b5Sdan   return rc;
148915daa6b5Sdan }
149015daa6b5Sdan 
14914bfd1829Sdan /*
14921dff3281Sdan ** Return the current time as a 32-bit timestamp in UNIX epoch format (like
14931dff3281Sdan ** time(2)).
14941dff3281Sdan */
zipfileTime(void)14951dff3281Sdan static u32 zipfileTime(void){
14961dff3281Sdan   sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
14971dff3281Sdan   u32 ret;
1498a959bf53Sdrh   if( pVfs==0 ) return 0;
14991dff3281Sdan   if( pVfs->iVersion>=2 && pVfs->xCurrentTimeInt64 ){
15001dff3281Sdan     i64 ms;
15011dff3281Sdan     pVfs->xCurrentTimeInt64(pVfs, &ms);
15021dff3281Sdan     ret = (u32)((ms/1000) - ((i64)24405875 * 8640));
15031dff3281Sdan   }else{
15041dff3281Sdan     double day;
15051dff3281Sdan     pVfs->xCurrentTime(pVfs, &day);
15061dff3281Sdan     ret = (u32)((day - 2440587.5) * 86400);
15071dff3281Sdan   }
15081dff3281Sdan   return ret;
15091dff3281Sdan }
15101dff3281Sdan 
15111dff3281Sdan /*
15121dff3281Sdan ** Return a 32-bit timestamp in UNIX epoch format.
15131dff3281Sdan **
15141dff3281Sdan ** If the value passed as the only argument is either NULL or an SQL NULL,
15151dff3281Sdan ** return the current time. Otherwise, return the value stored in (*pVal)
15161dff3281Sdan ** cast to a 32-bit unsigned integer.
15171dff3281Sdan */
zipfileGetTime(sqlite3_value * pVal)15181dff3281Sdan static u32 zipfileGetTime(sqlite3_value *pVal){
15191dff3281Sdan   if( pVal==0 || sqlite3_value_type(pVal)==SQLITE_NULL ){
15201dff3281Sdan     return zipfileTime();
15211dff3281Sdan   }
15221dff3281Sdan   return (u32)sqlite3_value_int64(pVal);
15231dff3281Sdan }
15241dff3281Sdan 
15251dff3281Sdan /*
152693c803e9Sdan ** Unless it is NULL, entry pOld is currently part of the pTab->pFirstEntry
152793c803e9Sdan ** linked list.  Remove it from the list and free the object.
152893c803e9Sdan */
zipfileRemoveEntryFromList(ZipfileTab * pTab,ZipfileEntry * pOld)152993c803e9Sdan static void zipfileRemoveEntryFromList(ZipfileTab *pTab, ZipfileEntry *pOld){
153093c803e9Sdan   if( pOld ){
153193c803e9Sdan     ZipfileEntry **pp;
153293c803e9Sdan     for(pp=&pTab->pFirstEntry; (*pp)!=pOld; pp=&((*pp)->pNext));
153393c803e9Sdan     *pp = (*pp)->pNext;
153493c803e9Sdan     zipfileEntryFree(pOld);
153593c803e9Sdan   }
153693c803e9Sdan }
153793c803e9Sdan 
153893c803e9Sdan /*
1539373dc3bbSdan ** xUpdate method.
1540373dc3bbSdan */
zipfileUpdate(sqlite3_vtab * pVtab,int nVal,sqlite3_value ** apVal,sqlite_int64 * pRowid)1541373dc3bbSdan static int zipfileUpdate(
1542373dc3bbSdan   sqlite3_vtab *pVtab,
1543373dc3bbSdan   int nVal,
1544373dc3bbSdan   sqlite3_value **apVal,
1545373dc3bbSdan   sqlite_int64 *pRowid
1546373dc3bbSdan ){
1547373dc3bbSdan   ZipfileTab *pTab = (ZipfileTab*)pVtab;
1548373dc3bbSdan   int rc = SQLITE_OK;             /* Return Code */
1549373dc3bbSdan   ZipfileEntry *pNew = 0;         /* New in-memory CDS entry */
1550373dc3bbSdan 
15517026bd67Smistachkin   u32 mode = 0;                   /* Mode for new entry */
15521dff3281Sdan   u32 mTime = 0;                  /* Modification time for new entry */
155388a1d6b9Smistachkin   i64 sz = 0;                     /* Uncompressed size */
15547026bd67Smistachkin   const char *zPath = 0;          /* Path for new entry */
15557026bd67Smistachkin   int nPath = 0;                  /* strlen(zPath) */
15567c15ac1aSdan   const u8 *pData = 0;            /* Pointer to buffer containing content */
15577c15ac1aSdan   int nData = 0;                  /* Size of pData buffer in bytes */
1558373dc3bbSdan   int iMethod = 0;                /* Compression method for new entry */
1559373dc3bbSdan   u8 *pFree = 0;                  /* Free this */
1560f2ed70e4Sdan   char *zFree = 0;                /* Also free this */
1561f42884c3Sdan   ZipfileEntry *pOld = 0;
156293c803e9Sdan   ZipfileEntry *pOld2 = 0;
1563d30830e4Sdan   int bUpdate = 0;                /* True for an update that modifies "name" */
1564128011a2Sdan   int bIsDir = 0;
15652d620070Sdan   u32 iCrc32 = 0;
1566128011a2Sdan 
156715daa6b5Sdan   if( pTab->pWriteFd==0 ){
156815daa6b5Sdan     rc = zipfileBegin(pVtab);
156915daa6b5Sdan     if( rc!=SQLITE_OK ) return rc;
157015daa6b5Sdan   }
15710cde0c62Sdan 
15728558ef2eSdan   /* If this is a DELETE or UPDATE, find the archive entry to delete. */
1573373dc3bbSdan   if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
157466a3a91aSdan     const char *zDelete = (const char*)sqlite3_value_text(apVal[0]);
1575eb545004Sdrh     int nDelete = (int)strlen(zDelete);
15762cc8f483Smistachkin     if( nVal>1 ){
15772cc8f483Smistachkin       const char *zUpdate = (const char*)sqlite3_value_text(apVal[1]);
15782cc8f483Smistachkin       if( zUpdate && zipfileComparePath(zUpdate, zDelete, nDelete)!=0 ){
15792cc8f483Smistachkin         bUpdate = 1;
15802cc8f483Smistachkin       }
15812cc8f483Smistachkin     }
1582f42884c3Sdan     for(pOld=pTab->pFirstEntry; 1; pOld=pOld->pNext){
15838558ef2eSdan       if( zipfileComparePath(pOld->cds.zFile, zDelete, nDelete)==0 ){
15840cde0c62Sdan         break;
1585373dc3bbSdan       }
1586f42884c3Sdan       assert( pOld->pNext );
1587373dc3bbSdan     }
1588373dc3bbSdan   }
1589373dc3bbSdan 
15908558ef2eSdan   if( nVal>1 ){
15912d620070Sdan     /* Check that "sz" and "rawdata" are both NULL: */
159241a6f2cbSdrh     if( sqlite3_value_type(apVal[5])!=SQLITE_NULL ){
159341a6f2cbSdrh       zipfileTableErr(pTab, "sz must be NULL");
159441a6f2cbSdrh       rc = SQLITE_CONSTRAINT;
159541a6f2cbSdrh     }
159641a6f2cbSdrh     if( sqlite3_value_type(apVal[6])!=SQLITE_NULL ){
159741a6f2cbSdrh       zipfileTableErr(pTab, "rawdata must be NULL");
15982d620070Sdan       rc = SQLITE_CONSTRAINT;
15992d620070Sdan     }
1600f42884c3Sdan 
1601f42884c3Sdan     if( rc==SQLITE_OK ){
16022d620070Sdan       if( sqlite3_value_type(apVal[7])==SQLITE_NULL ){
16032d620070Sdan         /* data=NULL. A directory */
1604128011a2Sdan         bIsDir = 1;
16052d620070Sdan       }else{
1606128011a2Sdan         /* Value specified for "data", and possibly "method". This must be
1607128011a2Sdan         ** a regular file or a symlink. */
16087c15ac1aSdan         const u8 *aIn = sqlite3_value_blob(apVal[7]);
16097c15ac1aSdan         int nIn = sqlite3_value_bytes(apVal[7]);
16107c15ac1aSdan         int bAuto = sqlite3_value_type(apVal[8])==SQLITE_NULL;
16117c15ac1aSdan 
16127c15ac1aSdan         iMethod = sqlite3_value_int(apVal[8]);
16137c15ac1aSdan         sz = nIn;
1614f42884c3Sdan         pData = aIn;
1615f42884c3Sdan         nData = nIn;
16167c15ac1aSdan         if( iMethod!=0 && iMethod!=8 ){
161741a6f2cbSdrh           zipfileTableErr(pTab, "unknown compression method: %d", iMethod);
16187c15ac1aSdan           rc = SQLITE_CONSTRAINT;
16192879952fSdan         }else{
16202879952fSdan           if( bAuto || iMethod ){
1621f42884c3Sdan             int nCmp;
1622f8c4b99aSdan             rc = zipfileDeflate(aIn, nIn, &pFree, &nCmp, &pTab->base.zErrMsg);
16237c15ac1aSdan             if( rc==SQLITE_OK ){
1624f42884c3Sdan               if( iMethod || nCmp<nIn ){
16257c15ac1aSdan                 iMethod = 8;
16267c15ac1aSdan                 pData = pFree;
1627f42884c3Sdan                 nData = nCmp;
16287c15ac1aSdan               }
16297c15ac1aSdan             }
16302879952fSdan           }
16312d620070Sdan           iCrc32 = crc32(0, aIn, nIn);
16327c15ac1aSdan         }
1633128011a2Sdan       }
1634f42884c3Sdan     }
1635373dc3bbSdan 
1636373dc3bbSdan     if( rc==SQLITE_OK ){
1637f8c4b99aSdan       rc = zipfileGetMode(apVal[3], bIsDir, &mode, &pTab->base.zErrMsg);
1638128011a2Sdan     }
1639128011a2Sdan 
1640128011a2Sdan     if( rc==SQLITE_OK ){
1641128011a2Sdan       zPath = (const char*)sqlite3_value_text(apVal[2]);
164254d50109Sdrh       if( zPath==0 ) zPath = "";
1643128011a2Sdan       nPath = (int)strlen(zPath);
16441dff3281Sdan       mTime = zipfileGetTime(apVal[4]);
1645128011a2Sdan     }
1646128011a2Sdan 
1647f2ed70e4Sdan     if( rc==SQLITE_OK && bIsDir ){
1648f2ed70e4Sdan       /* For a directory, check that the last character in the path is a
1649f2ed70e4Sdan       ** '/'. This appears to be required for compatibility with info-zip
1650f2ed70e4Sdan       ** (the unzip command on unix). It does not create directories
1651f2ed70e4Sdan       ** otherwise.  */
16528d7f44c0Sdrh       if( nPath<=0 || zPath[nPath-1]!='/' ){
1653f2ed70e4Sdan         zFree = sqlite3_mprintf("%s/", zPath);
1654f2ed70e4Sdan         zPath = (const char*)zFree;
16558d7f44c0Sdrh         if( zFree==0 ){
16568d7f44c0Sdrh           rc = SQLITE_NOMEM;
16578d7f44c0Sdrh           nPath = 0;
16588d7f44c0Sdrh         }else{
1659d8f2d46cSdrh           nPath = (int)strlen(zPath);
1660f2ed70e4Sdan         }
1661f2ed70e4Sdan       }
16628d7f44c0Sdrh     }
1663f2ed70e4Sdan 
16642cc8f483Smistachkin     /* Check that we're not inserting a duplicate entry -OR- updating an
16652cc8f483Smistachkin     ** entry with a path, thereby making it into a duplicate. */
16662cc8f483Smistachkin     if( (pOld==0 || bUpdate) && rc==SQLITE_OK ){
16674bfd1829Sdan       ZipfileEntry *p;
16684bfd1829Sdan       for(p=pTab->pFirstEntry; p; p=p->pNext){
16698558ef2eSdan         if( zipfileComparePath(p->cds.zFile, zPath, nPath)==0 ){
167042f3c5ffSdrh           switch( sqlite3_vtab_on_conflict(pTab->db) ){
167142f3c5ffSdrh             case SQLITE_IGNORE: {
167242f3c5ffSdrh               goto zipfile_update_done;
167342f3c5ffSdrh             }
167442f3c5ffSdrh             case SQLITE_REPLACE: {
167593c803e9Sdan               pOld2 = p;
167642f3c5ffSdrh               break;
167742f3c5ffSdrh             }
167842f3c5ffSdrh             default: {
167941a6f2cbSdrh               zipfileTableErr(pTab, "duplicate name: \"%s\"", zPath);
16804bfd1829Sdan               rc = SQLITE_CONSTRAINT;
16814bfd1829Sdan               break;
16824bfd1829Sdan             }
16834bfd1829Sdan           }
168442f3c5ffSdrh           break;
168542f3c5ffSdrh         }
168642f3c5ffSdrh       }
16874bfd1829Sdan     }
16884bfd1829Sdan 
1689128011a2Sdan     if( rc==SQLITE_OK ){
1690373dc3bbSdan       /* Create the new CDS record. */
169115daa6b5Sdan       pNew = zipfileNewEntry(zPath);
1692373dc3bbSdan       if( pNew==0 ){
1693373dc3bbSdan         rc = SQLITE_NOMEM;
1694373dc3bbSdan       }else{
16958558ef2eSdan         pNew->cds.iVersionMadeBy = ZIPFILE_NEWENTRY_MADEBY;
16968558ef2eSdan         pNew->cds.iVersionExtract = ZIPFILE_NEWENTRY_REQUIRED;
16978558ef2eSdan         pNew->cds.flags = ZIPFILE_NEWENTRY_FLAGS;
16988558ef2eSdan         pNew->cds.iCompression = (u16)iMethod;
16991dff3281Sdan         zipfileMtimeToDos(&pNew->cds, mTime);
17008558ef2eSdan         pNew->cds.crc32 = iCrc32;
17018558ef2eSdan         pNew->cds.szCompressed = nData;
17028558ef2eSdan         pNew->cds.szUncompressed = (u32)sz;
17038558ef2eSdan         pNew->cds.iExternalAttr = (mode<<16);
17048558ef2eSdan         pNew->cds.iOffset = (u32)pTab->szCurrent;
17055facffbcSmistachkin         pNew->cds.nFile = (u16)nPath;
17068558ef2eSdan         pNew->mUnixTime = (u32)mTime;
170726333ee3Sdan         rc = zipfileAppendEntry(pTab, pNew, pData, nData);
1708f42884c3Sdan         zipfileAddEntry(pTab, pOld, pNew);
1709373dc3bbSdan       }
1710373dc3bbSdan     }
1711373dc3bbSdan   }
1712373dc3bbSdan 
171393c803e9Sdan   if( rc==SQLITE_OK && (pOld || pOld2) ){
17148558ef2eSdan     ZipfileCsr *pCsr;
17158558ef2eSdan     for(pCsr=pTab->pCsrList; pCsr; pCsr=pCsr->pCsrNext){
171693c803e9Sdan       if( pCsr->pCurrent && (pCsr->pCurrent==pOld || pCsr->pCurrent==pOld2) ){
171793c803e9Sdan         pCsr->pCurrent = pCsr->pCurrent->pNext;
17188558ef2eSdan         pCsr->bNoop = 1;
1719f42884c3Sdan       }
17208558ef2eSdan     }
172193c803e9Sdan 
172293c803e9Sdan     zipfileRemoveEntryFromList(pTab, pOld);
172393c803e9Sdan     zipfileRemoveEntryFromList(pTab, pOld2);
17248558ef2eSdan   }
17258558ef2eSdan 
172642f3c5ffSdrh zipfile_update_done:
1727373dc3bbSdan   sqlite3_free(pFree);
1728f2ed70e4Sdan   sqlite3_free(zFree);
1729373dc3bbSdan   return rc;
1730373dc3bbSdan }
1731373dc3bbSdan 
zipfileSerializeEOCD(ZipfileEOCD * p,u8 * aBuf)173226333ee3Sdan static int zipfileSerializeEOCD(ZipfileEOCD *p, u8 *aBuf){
173326333ee3Sdan   u8 *a = aBuf;
173426333ee3Sdan   zipfileWrite32(a, ZIPFILE_SIGNATURE_EOCD);
173526333ee3Sdan   zipfileWrite16(a, p->iDisk);
173626333ee3Sdan   zipfileWrite16(a, p->iFirstDisk);
173726333ee3Sdan   zipfileWrite16(a, p->nEntry);
173826333ee3Sdan   zipfileWrite16(a, p->nEntryTotal);
173926333ee3Sdan   zipfileWrite32(a, p->nSize);
174026333ee3Sdan   zipfileWrite32(a, p->iOffset);
174126333ee3Sdan   zipfileWrite16(a, 0);        /* Size of trailing comment in bytes*/
174226333ee3Sdan 
174326333ee3Sdan   return a-aBuf;
174426333ee3Sdan }
174526333ee3Sdan 
zipfileAppendEOCD(ZipfileTab * pTab,ZipfileEOCD * p)1746373dc3bbSdan static int zipfileAppendEOCD(ZipfileTab *pTab, ZipfileEOCD *p){
174726333ee3Sdan   int nBuf = zipfileSerializeEOCD(p, pTab->aBuffer);
174826333ee3Sdan   assert( nBuf==ZIPFILE_EOCD_FIXED_SZ );
174926333ee3Sdan   return zipfileAppendData(pTab, pTab->aBuffer, nBuf);
1750373dc3bbSdan }
1751373dc3bbSdan 
17528558ef2eSdan /*
17538558ef2eSdan ** Serialize the CDS structure into buffer aBuf[]. Return the number
17548558ef2eSdan ** of bytes written.
17558558ef2eSdan */
zipfileSerializeCDS(ZipfileEntry * pEntry,u8 * aBuf)17568558ef2eSdan static int zipfileSerializeCDS(ZipfileEntry *pEntry, u8 *aBuf){
17578558ef2eSdan   u8 *a = aBuf;
17588558ef2eSdan   ZipfileCDS *pCDS = &pEntry->cds;
17598558ef2eSdan 
17608558ef2eSdan   if( pEntry->aExtra==0 ){
17618558ef2eSdan     pCDS->nExtra = 9;
17628558ef2eSdan   }
17638558ef2eSdan 
17648558ef2eSdan   zipfileWrite32(a, ZIPFILE_SIGNATURE_CDS);
17658558ef2eSdan   zipfileWrite16(a, pCDS->iVersionMadeBy);
17668558ef2eSdan   zipfileWrite16(a, pCDS->iVersionExtract);
17678558ef2eSdan   zipfileWrite16(a, pCDS->flags);
17688558ef2eSdan   zipfileWrite16(a, pCDS->iCompression);
17698558ef2eSdan   zipfileWrite16(a, pCDS->mTime);
17708558ef2eSdan   zipfileWrite16(a, pCDS->mDate);
17718558ef2eSdan   zipfileWrite32(a, pCDS->crc32);
17728558ef2eSdan   zipfileWrite32(a, pCDS->szCompressed);
17738558ef2eSdan   zipfileWrite32(a, pCDS->szUncompressed);
17748558ef2eSdan   assert( a==&aBuf[ZIPFILE_CDS_NFILE_OFF] );
17758558ef2eSdan   zipfileWrite16(a, pCDS->nFile);
17768558ef2eSdan   zipfileWrite16(a, pCDS->nExtra);
17778558ef2eSdan   zipfileWrite16(a, pCDS->nComment);
17788558ef2eSdan   zipfileWrite16(a, pCDS->iDiskStart);
17798558ef2eSdan   zipfileWrite16(a, pCDS->iInternalAttr);
17808558ef2eSdan   zipfileWrite32(a, pCDS->iExternalAttr);
17818558ef2eSdan   zipfileWrite32(a, pCDS->iOffset);
17828558ef2eSdan 
17838558ef2eSdan   memcpy(a, pCDS->zFile, pCDS->nFile);
17848558ef2eSdan   a += pCDS->nFile;
17858558ef2eSdan 
17868558ef2eSdan   if( pEntry->aExtra ){
17878558ef2eSdan     int n = (int)pCDS->nExtra + (int)pCDS->nComment;
17888558ef2eSdan     memcpy(a, pEntry->aExtra, n);
17898558ef2eSdan     a += n;
17908558ef2eSdan   }else{
17918558ef2eSdan     assert( pCDS->nExtra==9 );
17928558ef2eSdan     zipfileWrite16(a, ZIPFILE_EXTRA_TIMESTAMP);
17938558ef2eSdan     zipfileWrite16(a, 5);
17948558ef2eSdan     *a++ = 0x01;
17958558ef2eSdan     zipfileWrite32(a, pEntry->mUnixTime);
17968558ef2eSdan   }
17978558ef2eSdan 
17988558ef2eSdan   return a-aBuf;
17998558ef2eSdan }
18008558ef2eSdan 
zipfileCommit(sqlite3_vtab * pVtab)1801373dc3bbSdan static int zipfileCommit(sqlite3_vtab *pVtab){
1802373dc3bbSdan   ZipfileTab *pTab = (ZipfileTab*)pVtab;
1803373dc3bbSdan   int rc = SQLITE_OK;
1804373dc3bbSdan   if( pTab->pWriteFd ){
1805373dc3bbSdan     i64 iOffset = pTab->szCurrent;
1806db0cb303Sdan     ZipfileEntry *p;
1807373dc3bbSdan     ZipfileEOCD eocd;
1808373dc3bbSdan     int nEntry = 0;
1809373dc3bbSdan 
18108558ef2eSdan     /* Write out all entries */
1811db0cb303Sdan     for(p=pTab->pFirstEntry; rc==SQLITE_OK && p; p=p->pNext){
18128558ef2eSdan       int n = zipfileSerializeCDS(p, pTab->aBuffer);
18138558ef2eSdan       rc = zipfileAppendData(pTab, pTab->aBuffer, n);
1814373dc3bbSdan       nEntry++;
1815373dc3bbSdan     }
1816373dc3bbSdan 
1817373dc3bbSdan     /* Write out the EOCD record */
1818373dc3bbSdan     eocd.iDisk = 0;
1819373dc3bbSdan     eocd.iFirstDisk = 0;
1820dfdfd8c7Smistachkin     eocd.nEntry = (u16)nEntry;
1821dfdfd8c7Smistachkin     eocd.nEntryTotal = (u16)nEntry;
1822dfdfd8c7Smistachkin     eocd.nSize = (u32)(pTab->szCurrent - iOffset);
1823dfdfd8c7Smistachkin     eocd.iOffset = (u32)iOffset;
1824373dc3bbSdan     rc = zipfileAppendEOCD(pTab, &eocd);
1825373dc3bbSdan 
1826373dc3bbSdan     zipfileCleanupTransaction(pTab);
1827373dc3bbSdan   }
1828373dc3bbSdan   return rc;
1829373dc3bbSdan }
1830373dc3bbSdan 
zipfileRollback(sqlite3_vtab * pVtab)1831373dc3bbSdan static int zipfileRollback(sqlite3_vtab *pVtab){
1832373dc3bbSdan   return zipfileCommit(pVtab);
1833373dc3bbSdan }
1834373dc3bbSdan 
zipfileFindCursor(ZipfileTab * pTab,i64 iId)183589fa7469Sdan static ZipfileCsr *zipfileFindCursor(ZipfileTab *pTab, i64 iId){
183689fa7469Sdan   ZipfileCsr *pCsr;
183789fa7469Sdan   for(pCsr=pTab->pCsrList; pCsr; pCsr=pCsr->pCsrNext){
183889fa7469Sdan     if( iId==pCsr->iId ) break;
183989fa7469Sdan   }
184089fa7469Sdan   return pCsr;
184189fa7469Sdan }
184289fa7469Sdan 
zipfileFunctionCds(sqlite3_context * context,int argc,sqlite3_value ** argv)184389fa7469Sdan static void zipfileFunctionCds(
184489fa7469Sdan   sqlite3_context *context,
184589fa7469Sdan   int argc,
184689fa7469Sdan   sqlite3_value **argv
184789fa7469Sdan ){
184889fa7469Sdan   ZipfileCsr *pCsr;
184989fa7469Sdan   ZipfileTab *pTab = (ZipfileTab*)sqlite3_user_data(context);
185089fa7469Sdan   assert( argc>0 );
185189fa7469Sdan 
185289fa7469Sdan   pCsr = zipfileFindCursor(pTab, sqlite3_value_int64(argv[0]));
185389fa7469Sdan   if( pCsr ){
18548558ef2eSdan     ZipfileCDS *p = &pCsr->pCurrent->cds;
185589fa7469Sdan     char *zRes = sqlite3_mprintf("{"
185689fa7469Sdan         "\"version-made-by\" : %u, "
185789fa7469Sdan         "\"version-to-extract\" : %u, "
185889fa7469Sdan         "\"flags\" : %u, "
185989fa7469Sdan         "\"compression\" : %u, "
186089fa7469Sdan         "\"time\" : %u, "
186189fa7469Sdan         "\"date\" : %u, "
186289fa7469Sdan         "\"crc32\" : %u, "
186389fa7469Sdan         "\"compressed-size\" : %u, "
186489fa7469Sdan         "\"uncompressed-size\" : %u, "
186589fa7469Sdan         "\"file-name-length\" : %u, "
186689fa7469Sdan         "\"extra-field-length\" : %u, "
186789fa7469Sdan         "\"file-comment-length\" : %u, "
186889fa7469Sdan         "\"disk-number-start\" : %u, "
186989fa7469Sdan         "\"internal-attr\" : %u, "
187089fa7469Sdan         "\"external-attr\" : %u, "
187189fa7469Sdan         "\"offset\" : %u }",
187289fa7469Sdan         (u32)p->iVersionMadeBy, (u32)p->iVersionExtract,
187389fa7469Sdan         (u32)p->flags, (u32)p->iCompression,
187489fa7469Sdan         (u32)p->mTime, (u32)p->mDate,
187589fa7469Sdan         (u32)p->crc32, (u32)p->szCompressed,
187689fa7469Sdan         (u32)p->szUncompressed, (u32)p->nFile,
187789fa7469Sdan         (u32)p->nExtra, (u32)p->nComment,
187889fa7469Sdan         (u32)p->iDiskStart, (u32)p->iInternalAttr,
187989fa7469Sdan         (u32)p->iExternalAttr, (u32)p->iOffset
188089fa7469Sdan     );
188189fa7469Sdan 
188289fa7469Sdan     if( zRes==0 ){
188389fa7469Sdan       sqlite3_result_error_nomem(context);
188489fa7469Sdan     }else{
188589fa7469Sdan       sqlite3_result_text(context, zRes, -1, SQLITE_TRANSIENT);
188689fa7469Sdan       sqlite3_free(zRes);
188789fa7469Sdan     }
188889fa7469Sdan   }
188989fa7469Sdan }
189089fa7469Sdan 
189189fa7469Sdan /*
189289fa7469Sdan ** xFindFunction method.
189389fa7469Sdan */
zipfileFindFunction(sqlite3_vtab * pVtab,int nArg,const char * zName,void (** pxFunc)(sqlite3_context *,int,sqlite3_value **),void ** ppArg)189489fa7469Sdan static int zipfileFindFunction(
189589fa7469Sdan   sqlite3_vtab *pVtab,            /* Virtual table handle */
189689fa7469Sdan   int nArg,                       /* Number of SQL function arguments */
189789fa7469Sdan   const char *zName,              /* Name of SQL function */
189889fa7469Sdan   void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */
189989fa7469Sdan   void **ppArg                    /* OUT: User data for *pxFunc */
190089fa7469Sdan ){
190189fa7469Sdan   if( sqlite3_stricmp("zipfile_cds", zName)==0 ){
190289fa7469Sdan     *pxFunc = zipfileFunctionCds;
190389fa7469Sdan     *ppArg = (void*)pVtab;
190489fa7469Sdan     return 1;
190589fa7469Sdan   }
190689fa7469Sdan   return 0;
190789fa7469Sdan }
190889fa7469Sdan 
1909f8c4b99aSdan typedef struct ZipfileBuffer ZipfileBuffer;
1910f8c4b99aSdan struct ZipfileBuffer {
1911f8c4b99aSdan   u8 *a;                          /* Pointer to buffer */
1912f8c4b99aSdan   int n;                          /* Size of buffer in bytes */
1913f8c4b99aSdan   int nAlloc;                     /* Byte allocated at a[] */
1914f8c4b99aSdan };
1915f8c4b99aSdan 
1916f8c4b99aSdan typedef struct ZipfileCtx ZipfileCtx;
1917f8c4b99aSdan struct ZipfileCtx {
1918f8c4b99aSdan   int nEntry;
1919f8c4b99aSdan   ZipfileBuffer body;
1920f8c4b99aSdan   ZipfileBuffer cds;
1921f8c4b99aSdan };
1922f8c4b99aSdan 
zipfileBufferGrow(ZipfileBuffer * pBuf,int nByte)1923f8c4b99aSdan static int zipfileBufferGrow(ZipfileBuffer *pBuf, int nByte){
1924f8c4b99aSdan   if( pBuf->n+nByte>pBuf->nAlloc ){
1925f8c4b99aSdan     u8 *aNew;
19262d77d80aSdrh     sqlite3_int64 nNew = pBuf->n ? pBuf->n*2 : 512;
1927f8c4b99aSdan     int nReq = pBuf->n + nByte;
1928f8c4b99aSdan 
1929f8c4b99aSdan     while( nNew<nReq ) nNew = nNew*2;
19302d77d80aSdrh     aNew = sqlite3_realloc64(pBuf->a, nNew);
1931f8c4b99aSdan     if( aNew==0 ) return SQLITE_NOMEM;
1932f8c4b99aSdan     pBuf->a = aNew;
19332d77d80aSdrh     pBuf->nAlloc = (int)nNew;
1934f8c4b99aSdan   }
1935f8c4b99aSdan   return SQLITE_OK;
1936f8c4b99aSdan }
1937f8c4b99aSdan 
1938f8c4b99aSdan /*
1939f8c4b99aSdan ** xStep() callback for the zipfile() aggregate. This can be called in
1940f8c4b99aSdan ** any of the following ways:
1941f8c4b99aSdan **
1942f8c4b99aSdan **   SELECT zipfile(name,data) ...
1943f8c4b99aSdan **   SELECT zipfile(name,mode,mtime,data) ...
1944f8c4b99aSdan **   SELECT zipfile(name,mode,mtime,data,method) ...
1945f8c4b99aSdan */
zipfileStep(sqlite3_context * pCtx,int nVal,sqlite3_value ** apVal)19464baf43ffSdan static void zipfileStep(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal){
1947f8c4b99aSdan   ZipfileCtx *p;                  /* Aggregate function context */
1948f8c4b99aSdan   ZipfileEntry e;                 /* New entry to add to zip archive */
1949f8c4b99aSdan 
1950f8c4b99aSdan   sqlite3_value *pName = 0;
1951f8c4b99aSdan   sqlite3_value *pMode = 0;
1952f8c4b99aSdan   sqlite3_value *pMtime = 0;
1953f8c4b99aSdan   sqlite3_value *pData = 0;
1954f8c4b99aSdan   sqlite3_value *pMethod = 0;
1955f8c4b99aSdan 
1956f8c4b99aSdan   int bIsDir = 0;
1957f8c4b99aSdan   u32 mode;
1958f8c4b99aSdan   int rc = SQLITE_OK;
1959f8c4b99aSdan   char *zErr = 0;
1960f8c4b99aSdan 
1961f8c4b99aSdan   int iMethod = -1;               /* Compression method to use (0 or 8) */
1962f8c4b99aSdan 
1963f8c4b99aSdan   const u8 *aData = 0;            /* Possibly compressed data for new entry */
1964f8c4b99aSdan   int nData = 0;                  /* Size of aData[] in bytes */
1965f8c4b99aSdan   int szUncompressed = 0;         /* Size of data before compression */
1966f8c4b99aSdan   u8 *aFree = 0;                  /* Free this before returning */
1967f8c4b99aSdan   u32 iCrc32 = 0;                 /* crc32 of uncompressed data */
1968f8c4b99aSdan 
1969f8c4b99aSdan   char *zName = 0;                /* Path (name) of new entry */
1970f8c4b99aSdan   int nName = 0;                  /* Size of zName in bytes */
1971f8c4b99aSdan   char *zFree = 0;                /* Free this before returning */
1972f8c4b99aSdan   int nByte;
1973f8c4b99aSdan 
1974f8c4b99aSdan   memset(&e, 0, sizeof(e));
1975f8c4b99aSdan   p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx));
1976f8c4b99aSdan   if( p==0 ) return;
1977f8c4b99aSdan 
1978f8c4b99aSdan   /* Martial the arguments into stack variables */
1979f8c4b99aSdan   if( nVal!=2 && nVal!=4 && nVal!=5 ){
1980f8c4b99aSdan     zErr = sqlite3_mprintf("wrong number of arguments to function zipfile()");
1981f8c4b99aSdan     rc = SQLITE_ERROR;
1982f8c4b99aSdan     goto zipfile_step_out;
1983f8c4b99aSdan   }
1984f8c4b99aSdan   pName = apVal[0];
1985f8c4b99aSdan   if( nVal==2 ){
1986f8c4b99aSdan     pData = apVal[1];
1987f8c4b99aSdan   }else{
1988f8c4b99aSdan     pMode = apVal[1];
1989f8c4b99aSdan     pMtime = apVal[2];
1990f8c4b99aSdan     pData = apVal[3];
1991f8c4b99aSdan     if( nVal==5 ){
1992f8c4b99aSdan       pMethod = apVal[4];
1993f8c4b99aSdan     }
1994f8c4b99aSdan   }
1995f8c4b99aSdan 
1996f8c4b99aSdan   /* Check that the 'name' parameter looks ok. */
1997f8c4b99aSdan   zName = (char*)sqlite3_value_text(pName);
1998f8c4b99aSdan   nName = sqlite3_value_bytes(pName);
1999f8c4b99aSdan   if( zName==0 ){
2000f8c4b99aSdan     zErr = sqlite3_mprintf("first argument to zipfile() must be non-NULL");
2001f8c4b99aSdan     rc = SQLITE_ERROR;
2002f8c4b99aSdan     goto zipfile_step_out;
2003f8c4b99aSdan   }
2004f8c4b99aSdan 
2005f8c4b99aSdan   /* Inspect the 'method' parameter. This must be either 0 (store), 8 (use
2006f8c4b99aSdan   ** deflate compression) or NULL (choose automatically).  */
2007f8c4b99aSdan   if( pMethod && SQLITE_NULL!=sqlite3_value_type(pMethod) ){
20085facffbcSmistachkin     iMethod = (int)sqlite3_value_int64(pMethod);
2009f8c4b99aSdan     if( iMethod!=0 && iMethod!=8 ){
2010f8c4b99aSdan       zErr = sqlite3_mprintf("illegal method value: %d", iMethod);
2011f8c4b99aSdan       rc = SQLITE_ERROR;
2012f8c4b99aSdan       goto zipfile_step_out;
2013f8c4b99aSdan     }
2014f8c4b99aSdan   }
2015f8c4b99aSdan 
2016f8c4b99aSdan   /* Now inspect the data. If this is NULL, then the new entry must be a
2017f8c4b99aSdan   ** directory.  Otherwise, figure out whether or not the data should
2018f8c4b99aSdan   ** be deflated or simply stored in the zip archive. */
2019f8c4b99aSdan   if( sqlite3_value_type(pData)==SQLITE_NULL ){
2020f8c4b99aSdan     bIsDir = 1;
2021f8c4b99aSdan     iMethod = 0;
2022f8c4b99aSdan   }else{
2023f8c4b99aSdan     aData = sqlite3_value_blob(pData);
2024f8c4b99aSdan     szUncompressed = nData = sqlite3_value_bytes(pData);
2025f8c4b99aSdan     iCrc32 = crc32(0, aData, nData);
2026f8c4b99aSdan     if( iMethod<0 || iMethod==8 ){
2027f8c4b99aSdan       int nOut = 0;
2028f8c4b99aSdan       rc = zipfileDeflate(aData, nData, &aFree, &nOut, &zErr);
2029f8c4b99aSdan       if( rc!=SQLITE_OK ){
2030f8c4b99aSdan         goto zipfile_step_out;
2031f8c4b99aSdan       }
2032f8c4b99aSdan       if( iMethod==8 || nOut<nData ){
2033f8c4b99aSdan         aData = aFree;
2034f8c4b99aSdan         nData = nOut;
2035f8c4b99aSdan         iMethod = 8;
2036f8c4b99aSdan       }else{
2037f8c4b99aSdan         iMethod = 0;
2038f8c4b99aSdan       }
2039f8c4b99aSdan     }
2040f8c4b99aSdan   }
2041f8c4b99aSdan 
2042f8c4b99aSdan   /* Decode the "mode" argument. */
2043f8c4b99aSdan   rc = zipfileGetMode(pMode, bIsDir, &mode, &zErr);
2044f8c4b99aSdan   if( rc ) goto zipfile_step_out;
2045f8c4b99aSdan 
2046f8c4b99aSdan   /* Decode the "mtime" argument. */
20471dff3281Sdan   e.mUnixTime = zipfileGetTime(pMtime);
2048f8c4b99aSdan 
2049f8c4b99aSdan   /* If this is a directory entry, ensure that there is exactly one '/'
2050f8c4b99aSdan   ** at the end of the path. Or, if this is not a directory and the path
2051f8c4b99aSdan   ** ends in '/' it is an error. */
2052f8c4b99aSdan   if( bIsDir==0 ){
2053a194d315Sdrh     if( nName>0 && zName[nName-1]=='/' ){
2054f8c4b99aSdan       zErr = sqlite3_mprintf("non-directory name must not end with /");
2055f8c4b99aSdan       rc = SQLITE_ERROR;
2056f8c4b99aSdan       goto zipfile_step_out;
2057f8c4b99aSdan     }
2058f8c4b99aSdan   }else{
2059a194d315Sdrh     if( nName==0 || zName[nName-1]!='/' ){
2060f8c4b99aSdan       zName = zFree = sqlite3_mprintf("%s/", zName);
2061f8c4b99aSdan       if( zName==0 ){
2062f8c4b99aSdan         rc = SQLITE_NOMEM;
2063f8c4b99aSdan         goto zipfile_step_out;
2064f8c4b99aSdan       }
2065d8f2d46cSdrh       nName = (int)strlen(zName);
2066f8c4b99aSdan     }else{
2067f8c4b99aSdan       while( nName>1 && zName[nName-2]=='/' ) nName--;
2068f8c4b99aSdan     }
2069f8c4b99aSdan   }
2070f8c4b99aSdan 
2071f8c4b99aSdan   /* Assemble the ZipfileEntry object for the new zip archive entry */
2072f8c4b99aSdan   e.cds.iVersionMadeBy = ZIPFILE_NEWENTRY_MADEBY;
2073f8c4b99aSdan   e.cds.iVersionExtract = ZIPFILE_NEWENTRY_REQUIRED;
2074f8c4b99aSdan   e.cds.flags = ZIPFILE_NEWENTRY_FLAGS;
20755facffbcSmistachkin   e.cds.iCompression = (u16)iMethod;
2076f8c4b99aSdan   zipfileMtimeToDos(&e.cds, (u32)e.mUnixTime);
2077f8c4b99aSdan   e.cds.crc32 = iCrc32;
2078f8c4b99aSdan   e.cds.szCompressed = nData;
2079f8c4b99aSdan   e.cds.szUncompressed = szUncompressed;
2080f8c4b99aSdan   e.cds.iExternalAttr = (mode<<16);
2081f8c4b99aSdan   e.cds.iOffset = p->body.n;
20825facffbcSmistachkin   e.cds.nFile = (u16)nName;
2083f8c4b99aSdan   e.cds.zFile = zName;
2084f8c4b99aSdan 
2085f8c4b99aSdan   /* Append the LFH to the body of the new archive */
2086f8c4b99aSdan   nByte = ZIPFILE_LFH_FIXED_SZ + e.cds.nFile + 9;
2087f8c4b99aSdan   if( (rc = zipfileBufferGrow(&p->body, nByte)) ) goto zipfile_step_out;
2088f8c4b99aSdan   p->body.n += zipfileSerializeLFH(&e, &p->body.a[p->body.n]);
2089f8c4b99aSdan 
2090f8c4b99aSdan   /* Append the data to the body of the new archive */
20913f2cebb6Sdan   if( nData>0 ){
2092f8c4b99aSdan     if( (rc = zipfileBufferGrow(&p->body, nData)) ) goto zipfile_step_out;
2093f8c4b99aSdan     memcpy(&p->body.a[p->body.n], aData, nData);
2094f8c4b99aSdan     p->body.n += nData;
20953f2cebb6Sdan   }
2096f8c4b99aSdan 
2097f8c4b99aSdan   /* Append the CDS record to the directory of the new archive */
2098f8c4b99aSdan   nByte = ZIPFILE_CDS_FIXED_SZ + e.cds.nFile + 9;
2099f8c4b99aSdan   if( (rc = zipfileBufferGrow(&p->cds, nByte)) ) goto zipfile_step_out;
2100f8c4b99aSdan   p->cds.n += zipfileSerializeCDS(&e, &p->cds.a[p->cds.n]);
2101f8c4b99aSdan 
2102f8c4b99aSdan   /* Increment the count of entries in the archive */
2103f8c4b99aSdan   p->nEntry++;
2104f8c4b99aSdan 
2105f8c4b99aSdan  zipfile_step_out:
2106f8c4b99aSdan   sqlite3_free(aFree);
2107f8c4b99aSdan   sqlite3_free(zFree);
2108f8c4b99aSdan   if( rc ){
2109f8c4b99aSdan     if( zErr ){
2110f8c4b99aSdan       sqlite3_result_error(pCtx, zErr, -1);
2111f8c4b99aSdan     }else{
2112f8c4b99aSdan       sqlite3_result_error_code(pCtx, rc);
2113f8c4b99aSdan     }
2114f8c4b99aSdan   }
2115f8c4b99aSdan   sqlite3_free(zErr);
2116f8c4b99aSdan }
2117f8c4b99aSdan 
2118f8c4b99aSdan /*
2119f8c4b99aSdan ** xFinalize() callback for zipfile aggregate function.
2120f8c4b99aSdan */
zipfileFinal(sqlite3_context * pCtx)21214baf43ffSdan static void zipfileFinal(sqlite3_context *pCtx){
2122f8c4b99aSdan   ZipfileCtx *p;
2123f8c4b99aSdan   ZipfileEOCD eocd;
21242d77d80aSdrh   sqlite3_int64 nZip;
2125f8c4b99aSdan   u8 *aZip;
2126f8c4b99aSdan 
2127f8c4b99aSdan   p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx));
2128c3ef23a1Sdan   if( p==0 ) return;
2129c3ef23a1Sdan   if( p->nEntry>0 ){
2130f8c4b99aSdan     memset(&eocd, 0, sizeof(eocd));
21315facffbcSmistachkin     eocd.nEntry = (u16)p->nEntry;
21325facffbcSmistachkin     eocd.nEntryTotal = (u16)p->nEntry;
2133f8c4b99aSdan     eocd.nSize = p->cds.n;
2134f8c4b99aSdan     eocd.iOffset = p->body.n;
2135f8c4b99aSdan 
2136f8c4b99aSdan     nZip = p->body.n + p->cds.n + ZIPFILE_EOCD_FIXED_SZ;
21372d77d80aSdrh     aZip = (u8*)sqlite3_malloc64(nZip);
2138f8c4b99aSdan     if( aZip==0 ){
2139f8c4b99aSdan       sqlite3_result_error_nomem(pCtx);
2140f8c4b99aSdan     }else{
2141f8c4b99aSdan       memcpy(aZip, p->body.a, p->body.n);
2142f8c4b99aSdan       memcpy(&aZip[p->body.n], p->cds.a, p->cds.n);
2143f8c4b99aSdan       zipfileSerializeEOCD(&eocd, &aZip[p->body.n + p->cds.n]);
21442d77d80aSdrh       sqlite3_result_blob(pCtx, aZip, (int)nZip, zipfileFree);
2145f8c4b99aSdan     }
2146c3ef23a1Sdan   }
2147f8c4b99aSdan 
2148f8c4b99aSdan   sqlite3_free(p->body.a);
2149f8c4b99aSdan   sqlite3_free(p->cds.a);
2150f8c4b99aSdan }
2151f8c4b99aSdan 
2152f8c4b99aSdan 
21539ebfaad2Sdan /*
21549ebfaad2Sdan ** Register the "zipfile" virtual table.
21559ebfaad2Sdan */
zipfileRegister(sqlite3 * db)21569ebfaad2Sdan static int zipfileRegister(sqlite3 *db){
21579ebfaad2Sdan   static sqlite3_module zipfileModule = {
2158373dc3bbSdan     1,                         /* iVersion */
2159373dc3bbSdan     zipfileConnect,            /* xCreate */
21609ebfaad2Sdan     zipfileConnect,            /* xConnect */
21619ebfaad2Sdan     zipfileBestIndex,          /* xBestIndex */
21629ebfaad2Sdan     zipfileDisconnect,         /* xDisconnect */
2163373dc3bbSdan     zipfileDisconnect,         /* xDestroy */
21649ebfaad2Sdan     zipfileOpen,               /* xOpen - open a cursor */
21659ebfaad2Sdan     zipfileClose,              /* xClose - close a cursor */
21669ebfaad2Sdan     zipfileFilter,             /* xFilter - configure scan constraints */
21679ebfaad2Sdan     zipfileNext,               /* xNext - advance a cursor */
21689ebfaad2Sdan     zipfileEof,                /* xEof - check for end of scan */
21699ebfaad2Sdan     zipfileColumn,             /* xColumn - read data */
2170a07aa8d3Sdan     0,                         /* xRowid - read data */
2171373dc3bbSdan     zipfileUpdate,             /* xUpdate */
2172373dc3bbSdan     zipfileBegin,              /* xBegin */
21739ebfaad2Sdan     0,                         /* xSync */
2174373dc3bbSdan     zipfileCommit,             /* xCommit */
2175373dc3bbSdan     zipfileRollback,           /* xRollback */
217689fa7469Sdan     zipfileFindFunction,       /* xFindMethod */
21779ebfaad2Sdan     0,                         /* xRename */
2178e684ac6fSdrh     0,                         /* xSavepoint */
2179e684ac6fSdrh     0,                         /* xRelease */
2180e684ac6fSdrh     0,                         /* xRollback */
2181e684ac6fSdrh     0                          /* xShadowName */
21829ebfaad2Sdan   };
21839ebfaad2Sdan 
21849ebfaad2Sdan   int rc = sqlite3_create_module(db, "zipfile"  , &zipfileModule, 0);
218526333ee3Sdan   if( rc==SQLITE_OK ) rc = sqlite3_overload_function(db, "zipfile_cds", -1);
2186f8c4b99aSdan   if( rc==SQLITE_OK ){
2187f8c4b99aSdan     rc = sqlite3_create_function(db, "zipfile", -1, SQLITE_UTF8, 0, 0,
2188f8c4b99aSdan         zipfileStep, zipfileFinal
2189f8c4b99aSdan     );
2190f8c4b99aSdan   }
21911da9c97bSdrh   assert( sizeof(i64)==8 );
21921da9c97bSdrh   assert( sizeof(u32)==4 );
21931da9c97bSdrh   assert( sizeof(u16)==2 );
21941da9c97bSdrh   assert( sizeof(u8)==1 );
21959ebfaad2Sdan   return rc;
21969ebfaad2Sdan }
21979ebfaad2Sdan #else         /* SQLITE_OMIT_VIRTUALTABLE */
21989ebfaad2Sdan # define zipfileRegister(x) SQLITE_OK
21999ebfaad2Sdan #endif
22009ebfaad2Sdan 
22019ebfaad2Sdan #ifdef _WIN32
22029ebfaad2Sdan __declspec(dllexport)
22039ebfaad2Sdan #endif
sqlite3_zipfile_init(sqlite3 * db,char ** pzErrMsg,const sqlite3_api_routines * pApi)22049ebfaad2Sdan int sqlite3_zipfile_init(
22059ebfaad2Sdan   sqlite3 *db,
22069ebfaad2Sdan   char **pzErrMsg,
22079ebfaad2Sdan   const sqlite3_api_routines *pApi
22089ebfaad2Sdan ){
22099ebfaad2Sdan   SQLITE_EXTENSION_INIT2(pApi);
22109ebfaad2Sdan   (void)pzErrMsg;  /* Unused parameter */
22119ebfaad2Sdan   return zipfileRegister(db);
22129ebfaad2Sdan }
2213