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