|
9b780d2…
|
drh
|
1 |
/* |
|
c19f34c…
|
drh
|
2 |
** Copyright (c) 2007 D. Richard Hipp |
|
9b780d2…
|
drh
|
3 |
** |
|
9b780d2…
|
drh
|
4 |
** This program is free software; you can redistribute it and/or |
|
c06edd2…
|
drh
|
5 |
** modify it under the terms of the Simplified BSD License (also |
|
c06edd2…
|
drh
|
6 |
** known as the "2-Clause License" or "FreeBSD License".) |
|
c06edd2…
|
drh
|
7 |
|
|
9b780d2…
|
drh
|
8 |
** This program is distributed in the hope that it will be useful, |
|
c06edd2…
|
drh
|
9 |
** but without any warranty; without even the implied warranty of |
|
c06edd2…
|
drh
|
10 |
** merchantability or fitness for a particular purpose. |
|
9b780d2…
|
drh
|
11 |
** |
|
9b780d2…
|
drh
|
12 |
** Author contact information: |
|
9b780d2…
|
drh
|
13 |
** [email protected] |
|
9b780d2…
|
drh
|
14 |
** http://www.hwaci.com/drh/ |
|
9b780d2…
|
drh
|
15 |
** |
|
9b780d2…
|
drh
|
16 |
******************************************************************************* |
|
9b780d2…
|
drh
|
17 |
** |
|
7eb5b0a…
|
dan
|
18 |
** This file contains code used to generate ZIP and SQLAR archives. |
|
9b780d2…
|
drh
|
19 |
*/ |
|
c30cd93…
|
jan.nijtmans
|
20 |
#include "config.h" |
|
9b780d2…
|
drh
|
21 |
#include <assert.h> |
|
f9c2d23…
|
stephan
|
22 |
#include <zlib.h> |
|
9b780d2…
|
drh
|
23 |
#include "zip.h" |
|
7eb5b0a…
|
dan
|
24 |
|
|
7eb5b0a…
|
dan
|
25 |
/* |
|
7eb5b0a…
|
dan
|
26 |
** Type of archive to build. |
|
7eb5b0a…
|
dan
|
27 |
*/ |
|
7eb5b0a…
|
dan
|
28 |
#define ARCHIVE_ZIP 0 |
|
7eb5b0a…
|
dan
|
29 |
#define ARCHIVE_SQLAR 1 |
|
9b780d2…
|
drh
|
30 |
|
|
9b780d2…
|
drh
|
31 |
/* |
|
9b780d2…
|
drh
|
32 |
** Write a 16- or 32-bit integer as little-endian into the given buffer. |
|
9b780d2…
|
drh
|
33 |
*/ |
|
9b780d2…
|
drh
|
34 |
static void put16(char *z, int v){ |
|
9b780d2…
|
drh
|
35 |
z[0] = v & 0xff; |
|
9b780d2…
|
drh
|
36 |
z[1] = (v>>8) & 0xff; |
|
9b780d2…
|
drh
|
37 |
} |
|
9b780d2…
|
drh
|
38 |
static void put32(char *z, int v){ |
|
9b780d2…
|
drh
|
39 |
z[0] = v & 0xff; |
|
9b780d2…
|
drh
|
40 |
z[1] = (v>>8) & 0xff; |
|
9b780d2…
|
drh
|
41 |
z[2] = (v>>16) & 0xff; |
|
9b780d2…
|
drh
|
42 |
z[3] = (v>>24) & 0xff; |
|
9b780d2…
|
drh
|
43 |
} |
|
9b780d2…
|
drh
|
44 |
|
|
9b780d2…
|
drh
|
45 |
/* |
|
9b780d2…
|
drh
|
46 |
** Variables in which to accumulate a growing ZIP archive. |
|
9b780d2…
|
drh
|
47 |
*/ |
|
9b780d2…
|
drh
|
48 |
static Blob body; /* The body of the ZIP archive */ |
|
9b780d2…
|
drh
|
49 |
static Blob toc; /* The table of contents */ |
|
9b780d2…
|
drh
|
50 |
static int nEntry; /* Number of files */ |
|
dead090…
|
drh
|
51 |
static int dosTime; /* DOS-format time */ |
|
dead090…
|
drh
|
52 |
static int dosDate; /* DOS-format date */ |
|
90048e0…
|
drh
|
53 |
static int unixTime; /* Seconds since 1970 */ |
|
90048e0…
|
drh
|
54 |
static int nDir; /* Number of entries in azDir[] */ |
|
90048e0…
|
drh
|
55 |
static char **azDir; /* Directory names already added to the archive */ |
|
7eb5b0a…
|
dan
|
56 |
|
|
7eb5b0a…
|
dan
|
57 |
typedef struct Archive Archive; |
|
7eb5b0a…
|
dan
|
58 |
struct Archive { |
|
7eb5b0a…
|
dan
|
59 |
int eType; /* Type of archive (SQLAR or ZIP) */ |
|
7eb5b0a…
|
dan
|
60 |
Blob *pBlob; /* Output blob */ |
|
7eb5b0a…
|
dan
|
61 |
Blob tmp; /* Blob used as temp space for compression */ |
|
7eb5b0a…
|
dan
|
62 |
sqlite3 *db; /* Db used to assemble sqlar archive */ |
|
7eb5b0a…
|
dan
|
63 |
sqlite3_stmt *pInsert; /* INSERT statement for SQLAR */ |
|
7eb5b0a…
|
dan
|
64 |
sqlite3_vfs vfs; /* VFS object */ |
|
7eb5b0a…
|
dan
|
65 |
}; |
|
7eb5b0a…
|
dan
|
66 |
|
|
7eb5b0a…
|
dan
|
67 |
/* |
|
7eb5b0a…
|
dan
|
68 |
** Ensure that blob pBlob is at least nMin bytes in size. |
|
7eb5b0a…
|
dan
|
69 |
*/ |
|
7eb5b0a…
|
dan
|
70 |
static void zip_blob_minsize(Blob *pBlob, int nMin){ |
|
53db40e…
|
drh
|
71 |
if( (int)blob_size(pBlob)<nMin ){ |
|
7eb5b0a…
|
dan
|
72 |
blob_resize(pBlob, nMin); |
|
7eb5b0a…
|
dan
|
73 |
} |
|
7eb5b0a…
|
dan
|
74 |
} |
|
7eb5b0a…
|
dan
|
75 |
|
|
7eb5b0a…
|
dan
|
76 |
/************************************************************************* |
|
7eb5b0a…
|
dan
|
77 |
** Implementation of "archive" VFS. A VFS designed to store the contents |
|
7eb5b0a…
|
dan
|
78 |
** of a new database in a Blob. Used to construct sqlar archives in |
|
7eb5b0a…
|
dan
|
79 |
** memory. |
|
7eb5b0a…
|
dan
|
80 |
*/ |
|
7eb5b0a…
|
dan
|
81 |
typedef struct ArchiveFile ArchiveFile; |
|
7eb5b0a…
|
dan
|
82 |
struct ArchiveFile { |
|
7eb5b0a…
|
dan
|
83 |
sqlite3_file base; /* Base class */ |
|
7eb5b0a…
|
dan
|
84 |
Blob *pBlob; |
|
7eb5b0a…
|
dan
|
85 |
}; |
|
7eb5b0a…
|
dan
|
86 |
|
|
7eb5b0a…
|
dan
|
87 |
static int archiveClose(sqlite3_file *pFile){ |
|
7eb5b0a…
|
dan
|
88 |
return SQLITE_OK; |
|
7eb5b0a…
|
dan
|
89 |
} |
|
7eb5b0a…
|
dan
|
90 |
static int archiveRead( |
|
7eb5b0a…
|
dan
|
91 |
sqlite3_file *pFile, void *pBuf, int iAmt, sqlite3_int64 iOfst |
|
7eb5b0a…
|
dan
|
92 |
){ |
|
7eb5b0a…
|
dan
|
93 |
assert( iOfst==0 || iOfst==24 ); |
|
7eb5b0a…
|
dan
|
94 |
return SQLITE_IOERR_SHORT_READ; |
|
7eb5b0a…
|
dan
|
95 |
} |
|
7eb5b0a…
|
dan
|
96 |
static int archiveWrite( |
|
7eb5b0a…
|
dan
|
97 |
sqlite3_file *pFile, const void *pBuf, int iAmt, sqlite3_int64 iOfst |
|
7eb5b0a…
|
dan
|
98 |
){ |
|
7eb5b0a…
|
dan
|
99 |
ArchiveFile *pAF = (ArchiveFile*)pFile; |
|
7eb5b0a…
|
dan
|
100 |
int nMin = (int)iOfst + iAmt; |
|
7eb5b0a…
|
dan
|
101 |
char *aBlob; /* Output buffer */ |
|
7eb5b0a…
|
dan
|
102 |
|
|
7eb5b0a…
|
dan
|
103 |
zip_blob_minsize(pAF->pBlob, nMin); |
|
7eb5b0a…
|
dan
|
104 |
aBlob = blob_buffer(pAF->pBlob); |
|
7eb5b0a…
|
dan
|
105 |
memcpy(&aBlob[iOfst], pBuf, iAmt); |
|
7eb5b0a…
|
dan
|
106 |
return SQLITE_OK; |
|
7eb5b0a…
|
dan
|
107 |
} |
|
7eb5b0a…
|
dan
|
108 |
static int archiveTruncate(sqlite3_file *pFile, sqlite3_int64 size){ |
|
7eb5b0a…
|
dan
|
109 |
return SQLITE_OK; |
|
7eb5b0a…
|
dan
|
110 |
} |
|
7eb5b0a…
|
dan
|
111 |
static int archiveSync(sqlite3_file *pFile, int flags){ |
|
7eb5b0a…
|
dan
|
112 |
return SQLITE_OK; |
|
7eb5b0a…
|
dan
|
113 |
} |
|
7eb5b0a…
|
dan
|
114 |
static int archiveFileSize(sqlite3_file *pFile, sqlite3_int64 *pSize){ |
|
7eb5b0a…
|
dan
|
115 |
*pSize = 0; |
|
7eb5b0a…
|
dan
|
116 |
return SQLITE_OK; |
|
7eb5b0a…
|
dan
|
117 |
} |
|
7eb5b0a…
|
dan
|
118 |
static int archiveLock(sqlite3_file *pFile, int eLock){ |
|
7eb5b0a…
|
dan
|
119 |
return SQLITE_OK; |
|
7eb5b0a…
|
dan
|
120 |
} |
|
7eb5b0a…
|
dan
|
121 |
static int archiveUnlock(sqlite3_file *pFile, int eLock){ |
|
7eb5b0a…
|
dan
|
122 |
return SQLITE_OK; |
|
7eb5b0a…
|
dan
|
123 |
} |
|
7eb5b0a…
|
dan
|
124 |
static int archiveCheckReservedLock(sqlite3_file *pFile, int *pResOut){ |
|
7eb5b0a…
|
dan
|
125 |
*pResOut = 0; |
|
7eb5b0a…
|
dan
|
126 |
return SQLITE_OK; |
|
7eb5b0a…
|
dan
|
127 |
} |
|
7eb5b0a…
|
dan
|
128 |
static int archiveFileControl(sqlite3_file *pFile, int op, void *pArg){ |
|
7eb5b0a…
|
dan
|
129 |
if( op==SQLITE_FCNTL_SIZE_HINT ){ |
|
7eb5b0a…
|
dan
|
130 |
ArchiveFile *pAF = (ArchiveFile*)pFile; |
|
7eb5b0a…
|
dan
|
131 |
zip_blob_minsize(pAF->pBlob, (int)(*(sqlite3_int64*)pArg)); |
|
7eb5b0a…
|
dan
|
132 |
} |
|
7eb5b0a…
|
dan
|
133 |
return SQLITE_NOTFOUND; |
|
7eb5b0a…
|
dan
|
134 |
} |
|
7eb5b0a…
|
dan
|
135 |
static int archiveSectorSize(sqlite3_file *pFile){ |
|
7eb5b0a…
|
dan
|
136 |
return 512; |
|
7eb5b0a…
|
dan
|
137 |
} |
|
7eb5b0a…
|
dan
|
138 |
static int archiveDeviceCharacteristics(sqlite3_file *pFile){ |
|
7eb5b0a…
|
dan
|
139 |
return 0; |
|
7eb5b0a…
|
dan
|
140 |
} |
|
7eb5b0a…
|
dan
|
141 |
|
|
7eb5b0a…
|
dan
|
142 |
static int archiveOpen( |
|
275da70…
|
danield
|
143 |
sqlite3_vfs *pVfs, const char *zName, |
|
7eb5b0a…
|
dan
|
144 |
sqlite3_file *pFile, int flags, int *pOutFlags |
|
7eb5b0a…
|
dan
|
145 |
){ |
|
7eb5b0a…
|
dan
|
146 |
static struct sqlite3_io_methods methods = { |
|
7eb5b0a…
|
dan
|
147 |
1, /* iVersion */ |
|
7eb5b0a…
|
dan
|
148 |
archiveClose, |
|
7eb5b0a…
|
dan
|
149 |
archiveRead, |
|
7eb5b0a…
|
dan
|
150 |
archiveWrite, |
|
7eb5b0a…
|
dan
|
151 |
archiveTruncate, |
|
7eb5b0a…
|
dan
|
152 |
archiveSync, |
|
7eb5b0a…
|
dan
|
153 |
archiveFileSize, |
|
7eb5b0a…
|
dan
|
154 |
archiveLock, |
|
7eb5b0a…
|
dan
|
155 |
archiveUnlock, |
|
7eb5b0a…
|
dan
|
156 |
archiveCheckReservedLock, |
|
7eb5b0a…
|
dan
|
157 |
archiveFileControl, |
|
7eb5b0a…
|
dan
|
158 |
archiveSectorSize, |
|
7eb5b0a…
|
dan
|
159 |
archiveDeviceCharacteristics, |
|
7eb5b0a…
|
dan
|
160 |
0, 0, 0, 0, |
|
7eb5b0a…
|
dan
|
161 |
0, 0 |
|
7eb5b0a…
|
dan
|
162 |
}; |
|
7eb5b0a…
|
dan
|
163 |
|
|
7eb5b0a…
|
dan
|
164 |
ArchiveFile *pAF = (ArchiveFile*)pFile; |
|
7eb5b0a…
|
dan
|
165 |
assert( flags & SQLITE_OPEN_MAIN_DB ); |
|
7eb5b0a…
|
dan
|
166 |
|
|
7eb5b0a…
|
dan
|
167 |
pAF->base.pMethods = &methods; |
|
7eb5b0a…
|
dan
|
168 |
pAF->pBlob = (Blob*)pVfs->pAppData; |
|
7eb5b0a…
|
dan
|
169 |
|
|
7eb5b0a…
|
dan
|
170 |
return SQLITE_OK; |
|
7eb5b0a…
|
dan
|
171 |
} |
|
7eb5b0a…
|
dan
|
172 |
static int archiveDelete(sqlite3_vfs *pVfs, const char *zName, int syncDir){ |
|
7eb5b0a…
|
dan
|
173 |
return SQLITE_OK; |
|
7eb5b0a…
|
dan
|
174 |
} |
|
7eb5b0a…
|
dan
|
175 |
static int archiveAccess( |
|
7eb5b0a…
|
dan
|
176 |
sqlite3_vfs *pVfs, const char *zName, int flags, int *pResOut |
|
7eb5b0a…
|
dan
|
177 |
){ |
|
7eb5b0a…
|
dan
|
178 |
*pResOut = 0; |
|
7eb5b0a…
|
dan
|
179 |
return SQLITE_OK; |
|
7eb5b0a…
|
dan
|
180 |
} |
|
7eb5b0a…
|
dan
|
181 |
static int archiveFullPathname( |
|
7eb5b0a…
|
dan
|
182 |
sqlite3_vfs *pVfs, const char *zIn, int nOut, char *zOut |
|
7eb5b0a…
|
dan
|
183 |
){ |
|
7eb5b0a…
|
dan
|
184 |
int n = strlen(zIn); |
|
7eb5b0a…
|
dan
|
185 |
memcpy(zOut, zIn, n+1); |
|
7eb5b0a…
|
dan
|
186 |
return SQLITE_OK; |
|
7eb5b0a…
|
dan
|
187 |
} |
|
7eb5b0a…
|
dan
|
188 |
static int archiveRandomness(sqlite3_vfs *pVfs, int nByte, char *zOut){ |
|
7eb5b0a…
|
dan
|
189 |
memset(zOut, 0, nByte); |
|
7eb5b0a…
|
dan
|
190 |
return SQLITE_OK; |
|
7eb5b0a…
|
dan
|
191 |
} |
|
7eb5b0a…
|
dan
|
192 |
static int archiveSleep(sqlite3_vfs *pVfs, int microseconds){ |
|
7eb5b0a…
|
dan
|
193 |
return SQLITE_OK; |
|
7eb5b0a…
|
dan
|
194 |
} |
|
7eb5b0a…
|
dan
|
195 |
static int archiveCurrentTime(sqlite3_vfs *pVfs, double *prOut){ |
|
7eb5b0a…
|
dan
|
196 |
return SQLITE_OK; |
|
7eb5b0a…
|
dan
|
197 |
} |
|
7eb5b0a…
|
dan
|
198 |
static int archiveGetLastError(sqlite3_vfs *pVfs, int nBuf, char *aBuf){ |
|
7eb5b0a…
|
dan
|
199 |
return SQLITE_OK; |
|
7eb5b0a…
|
dan
|
200 |
} |
|
7eb5b0a…
|
dan
|
201 |
/* |
|
7eb5b0a…
|
dan
|
202 |
** End of "archive" VFS. |
|
7eb5b0a…
|
dan
|
203 |
*************************************************************************/ |
|
9b780d2…
|
drh
|
204 |
|
|
9b780d2…
|
drh
|
205 |
/* |
|
9b780d2…
|
drh
|
206 |
** Initialize a new ZIP archive. |
|
9b780d2…
|
drh
|
207 |
*/ |
|
9b780d2…
|
drh
|
208 |
void zip_open(void){ |
|
9b780d2…
|
drh
|
209 |
blob_zero(&body); |
|
9b780d2…
|
drh
|
210 |
blob_zero(&toc); |
|
9b780d2…
|
drh
|
211 |
nEntry = 0; |
|
dead090…
|
drh
|
212 |
dosTime = 0; |
|
dead090…
|
drh
|
213 |
dosDate = 0; |
|
90048e0…
|
drh
|
214 |
unixTime = 0; |
|
dead090…
|
drh
|
215 |
} |
|
dead090…
|
drh
|
216 |
|
|
dead090…
|
drh
|
217 |
/* |
|
dead090…
|
drh
|
218 |
** Set the date and time values from an ISO8601 date string. |
|
dead090…
|
drh
|
219 |
*/ |
|
dead090…
|
drh
|
220 |
void zip_set_timedate_from_str(const char *zDate){ |
|
dead090…
|
drh
|
221 |
int y, m, d; |
|
dead090…
|
drh
|
222 |
int H, M, S; |
|
dead090…
|
drh
|
223 |
|
|
dead090…
|
drh
|
224 |
y = atoi(zDate); |
|
dead090…
|
drh
|
225 |
m = atoi(&zDate[5]); |
|
dead090…
|
drh
|
226 |
d = atoi(&zDate[8]); |
|
dead090…
|
drh
|
227 |
H = atoi(&zDate[11]); |
|
dead090…
|
drh
|
228 |
M = atoi(&zDate[14]); |
|
dead090…
|
drh
|
229 |
S = atoi(&zDate[17]); |
|
39bbc60…
|
drh
|
230 |
dosTime = (H<<11) + (M<<5) + (S>>1); |
|
dead090…
|
drh
|
231 |
dosDate = ((y-1980)<<9) + (m<<5) + d; |
|
dead090…
|
drh
|
232 |
} |
|
dead090…
|
drh
|
233 |
|
|
dead090…
|
drh
|
234 |
/* |
|
e2bdc10…
|
danield
|
235 |
** Set the date and time from a Julian day number. |
|
dead090…
|
drh
|
236 |
*/ |
|
dead090…
|
drh
|
237 |
void zip_set_timedate(double rDate){ |
|
dead090…
|
drh
|
238 |
char *zDate = db_text(0, "SELECT datetime(%.17g)", rDate); |
|
dead090…
|
drh
|
239 |
zip_set_timedate_from_str(zDate); |
|
8206ac9…
|
stephan
|
240 |
fossil_free(zDate); |
|
57f16ce…
|
drh
|
241 |
unixTime = (int)((rDate - 2440587.5)*86400.0); |
|
9b780d2…
|
drh
|
242 |
} |
|
9b780d2…
|
drh
|
243 |
|
|
9b780d2…
|
drh
|
244 |
/* |
|
9b780d2…
|
drh
|
245 |
** Append a single file to a growing ZIP archive. |
|
9b780d2…
|
drh
|
246 |
** |
|
9b780d2…
|
drh
|
247 |
** pFile is the file to be appended. zName is the name |
|
9b780d2…
|
drh
|
248 |
** that the file should be saved as. |
|
9b780d2…
|
drh
|
249 |
*/ |
|
7eb5b0a…
|
dan
|
250 |
static void zip_add_file_to_zip( |
|
7eb5b0a…
|
dan
|
251 |
Archive *p, |
|
275da70…
|
danield
|
252 |
const char *zName, |
|
275da70…
|
danield
|
253 |
const Blob *pFile, |
|
7eb5b0a…
|
dan
|
254 |
int mPerm |
|
7eb5b0a…
|
dan
|
255 |
){ |
|
9b780d2…
|
drh
|
256 |
z_stream stream; |
|
9b780d2…
|
drh
|
257 |
int nameLen; |
|
d78835d…
|
drh
|
258 |
int toOut = 0; |
|
9b780d2…
|
drh
|
259 |
int iStart; |
|
1bf6686…
|
stephan
|
260 |
unsigned long iCRC = 0; |
|
90048e0…
|
drh
|
261 |
int nByte = 0; |
|
90048e0…
|
drh
|
262 |
int nByteCompr = 0; |
|
90048e0…
|
drh
|
263 |
int nBlob; /* Size of the blob */ |
|
90048e0…
|
drh
|
264 |
int iMethod; /* Compression method. */ |
|
90048e0…
|
drh
|
265 |
int iMode = 0644; /* Access permissions */ |
|
9b780d2…
|
drh
|
266 |
char *z; |
|
9b780d2…
|
drh
|
267 |
char zHdr[30]; |
|
90048e0…
|
drh
|
268 |
char zExTime[13]; |
|
9b780d2…
|
drh
|
269 |
char zBuf[100]; |
|
9b780d2…
|
drh
|
270 |
char zOutBuf[100000]; |
|
9b780d2…
|
drh
|
271 |
|
|
e2bdc10…
|
danield
|
272 |
/* Fill inasmuch of the header as we know. |
|
9b780d2…
|
drh
|
273 |
*/ |
|
31b9822…
|
drh
|
274 |
nameLen = (int)strlen(zName); |
|
31b9822…
|
drh
|
275 |
if( nameLen==0 ) return; |
|
90048e0…
|
drh
|
276 |
nBlob = pFile ? blob_size(pFile) : 0; |
|
3d5cf48…
|
mistachkin
|
277 |
if( pFile ){ /* This is a file, possibly empty... */ |
|
ecedaf9…
|
mistachkin
|
278 |
iMethod = (nBlob>0) ? 8 : 0; /* Cannot compress zero bytes. */ |
|
e4f1c1f…
|
drh
|
279 |
switch( mPerm ){ |
|
e4f1c1f…
|
drh
|
280 |
case PERM_LNK: iMode = 0120755; break; |
|
e4f1c1f…
|
drh
|
281 |
case PERM_EXE: iMode = 0100755; break; |
|
e4f1c1f…
|
drh
|
282 |
default: iMode = 0100644; break; |
|
e4f1c1f…
|
drh
|
283 |
} |
|
3d5cf48…
|
mistachkin
|
284 |
}else{ /* This is a directory, no blob... */ |
|
90048e0…
|
drh
|
285 |
iMethod = 0; |
|
90048e0…
|
drh
|
286 |
iMode = 040755; |
|
90048e0…
|
drh
|
287 |
} |
|
90048e0…
|
drh
|
288 |
memset(zHdr, 0, sizeof(zHdr)); |
|
9b780d2…
|
drh
|
289 |
put32(&zHdr[0], 0x04034b50); |
|
90048e0…
|
drh
|
290 |
put16(&zHdr[4], 0x000a); |
|
3ff5ca0…
|
drh
|
291 |
put16(&zHdr[6], 0x0800); |
|
90048e0…
|
drh
|
292 |
put16(&zHdr[8], iMethod); |
|
dead090…
|
drh
|
293 |
put16(&zHdr[10], dosTime); |
|
dead090…
|
drh
|
294 |
put16(&zHdr[12], dosDate); |
|
9b780d2…
|
drh
|
295 |
put16(&zHdr[26], nameLen); |
|
90048e0…
|
drh
|
296 |
put16(&zHdr[28], 13); |
|
52b35c8…
|
jan.nijtmans
|
297 |
|
|
90048e0…
|
drh
|
298 |
put16(&zExTime[0], 0x5455); |
|
90048e0…
|
drh
|
299 |
put16(&zExTime[2], 9); |
|
90048e0…
|
drh
|
300 |
zExTime[4] = 3; |
|
90048e0…
|
drh
|
301 |
put32(&zExTime[5], unixTime); |
|
90048e0…
|
drh
|
302 |
put32(&zExTime[9], unixTime); |
|
52b35c8…
|
jan.nijtmans
|
303 |
|
|
9b780d2…
|
drh
|
304 |
|
|
9b780d2…
|
drh
|
305 |
/* Write the header and filename. |
|
9b780d2…
|
drh
|
306 |
*/ |
|
9b780d2…
|
drh
|
307 |
iStart = blob_size(&body); |
|
9b780d2…
|
drh
|
308 |
blob_append(&body, zHdr, 30); |
|
9b780d2…
|
drh
|
309 |
blob_append(&body, zName, nameLen); |
|
90048e0…
|
drh
|
310 |
blob_append(&body, zExTime, 13); |
|
90048e0…
|
drh
|
311 |
|
|
90048e0…
|
drh
|
312 |
if( nBlob>0 ){ |
|
90048e0…
|
drh
|
313 |
/* Write the compressed file. Compute the CRC as we progress. |
|
90048e0…
|
drh
|
314 |
*/ |
|
90048e0…
|
drh
|
315 |
stream.zalloc = (alloc_func)0; |
|
90048e0…
|
drh
|
316 |
stream.zfree = (free_func)0; |
|
90048e0…
|
drh
|
317 |
stream.opaque = 0; |
|
90048e0…
|
drh
|
318 |
stream.avail_in = blob_size(pFile); |
|
90048e0…
|
drh
|
319 |
stream.next_in = (unsigned char*)blob_buffer(pFile); |
|
90048e0…
|
drh
|
320 |
stream.avail_out = sizeof(zOutBuf); |
|
90048e0…
|
drh
|
321 |
stream.next_out = (unsigned char*)zOutBuf; |
|
d78835d…
|
drh
|
322 |
deflateInit2(&stream, 9, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); |
|
90048e0…
|
drh
|
323 |
iCRC = crc32(0, stream.next_in, stream.avail_in); |
|
90048e0…
|
drh
|
324 |
while( stream.avail_in>0 ){ |
|
90048e0…
|
drh
|
325 |
deflate(&stream, 0); |
|
90048e0…
|
drh
|
326 |
toOut = sizeof(zOutBuf) - stream.avail_out; |
|
d78835d…
|
drh
|
327 |
blob_append(&body, zOutBuf, toOut); |
|
90048e0…
|
drh
|
328 |
stream.avail_out = sizeof(zOutBuf); |
|
90048e0…
|
drh
|
329 |
stream.next_out = (unsigned char*)zOutBuf; |
|
90048e0…
|
drh
|
330 |
} |
|
90048e0…
|
drh
|
331 |
do{ |
|
90048e0…
|
drh
|
332 |
stream.avail_out = sizeof(zOutBuf); |
|
90048e0…
|
drh
|
333 |
stream.next_out = (unsigned char*)zOutBuf; |
|
90048e0…
|
drh
|
334 |
deflate(&stream, Z_FINISH); |
|
90048e0…
|
drh
|
335 |
toOut = sizeof(zOutBuf) - stream.avail_out; |
|
d78835d…
|
drh
|
336 |
blob_append(&body, zOutBuf, toOut); |
|
90048e0…
|
drh
|
337 |
}while( stream.avail_out==0 ); |
|
90048e0…
|
drh
|
338 |
nByte = stream.total_in; |
|
d78835d…
|
drh
|
339 |
nByteCompr = stream.total_out; |
|
90048e0…
|
drh
|
340 |
deflateEnd(&stream); |
|
52b35c8…
|
jan.nijtmans
|
341 |
|
|
90048e0…
|
drh
|
342 |
/* Go back and write the header, now that we know the compressed file size. |
|
90048e0…
|
drh
|
343 |
*/ |
|
90048e0…
|
drh
|
344 |
z = &blob_buffer(&body)[iStart]; |
|
90048e0…
|
drh
|
345 |
put32(&z[14], iCRC); |
|
90048e0…
|
drh
|
346 |
put32(&z[18], nByteCompr); |
|
90048e0…
|
drh
|
347 |
put32(&z[22], nByte); |
|
90048e0…
|
drh
|
348 |
} |
|
52b35c8…
|
jan.nijtmans
|
349 |
|
|
9b780d2…
|
drh
|
350 |
/* Make an entry in the tables of contents |
|
9b780d2…
|
drh
|
351 |
*/ |
|
9b780d2…
|
drh
|
352 |
memset(zBuf, 0, sizeof(zBuf)); |
|
9b780d2…
|
drh
|
353 |
put32(&zBuf[0], 0x02014b50); |
|
9b780d2…
|
drh
|
354 |
put16(&zBuf[4], 0x0317); |
|
90048e0…
|
drh
|
355 |
put16(&zBuf[6], 0x000a); |
|
3ff5ca0…
|
drh
|
356 |
put16(&zBuf[8], 0x0800); |
|
90048e0…
|
drh
|
357 |
put16(&zBuf[10], iMethod); |
|
dead090…
|
drh
|
358 |
put16(&zBuf[12], dosTime); |
|
dead090…
|
drh
|
359 |
put16(&zBuf[14], dosDate); |
|
9b780d2…
|
drh
|
360 |
put32(&zBuf[16], iCRC); |
|
9b780d2…
|
drh
|
361 |
put32(&zBuf[20], nByteCompr); |
|
9b780d2…
|
drh
|
362 |
put32(&zBuf[24], nByte); |
|
9b780d2…
|
drh
|
363 |
put16(&zBuf[28], nameLen); |
|
90048e0…
|
drh
|
364 |
put16(&zBuf[30], 9); |
|
9b780d2…
|
drh
|
365 |
put16(&zBuf[32], 0); |
|
deb6f2a…
|
drh
|
366 |
put16(&zBuf[34], 0); |
|
9b780d2…
|
drh
|
367 |
put16(&zBuf[36], 0); |
|
90048e0…
|
drh
|
368 |
put32(&zBuf[38], ((unsigned)iMode)<<16); |
|
9b780d2…
|
drh
|
369 |
put32(&zBuf[42], iStart); |
|
9b780d2…
|
drh
|
370 |
blob_append(&toc, zBuf, 46); |
|
9b780d2…
|
drh
|
371 |
blob_append(&toc, zName, nameLen); |
|
90048e0…
|
drh
|
372 |
put16(&zExTime[2], 5); |
|
90048e0…
|
drh
|
373 |
blob_append(&toc, zExTime, 9); |
|
9b780d2…
|
drh
|
374 |
nEntry++; |
|
9b780d2…
|
drh
|
375 |
} |
|
9b780d2…
|
drh
|
376 |
|
|
7eb5b0a…
|
dan
|
377 |
static void zip_add_file_to_sqlar( |
|
7eb5b0a…
|
dan
|
378 |
Archive *p, |
|
275da70…
|
danield
|
379 |
const char *zName, |
|
275da70…
|
danield
|
380 |
const Blob *pFile, |
|
7eb5b0a…
|
dan
|
381 |
int mPerm |
|
7eb5b0a…
|
dan
|
382 |
){ |
|
31b9822…
|
drh
|
383 |
int nName = (int)strlen(zName); |
|
7eb5b0a…
|
dan
|
384 |
|
|
7eb5b0a…
|
dan
|
385 |
if( p->db==0 ){ |
|
7eb5b0a…
|
dan
|
386 |
assert( p->vfs.zName==0 ); |
|
7eb5b0a…
|
dan
|
387 |
p->vfs.zName = (const char*)mprintf("archivevfs%p", (void*)p); |
|
7eb5b0a…
|
dan
|
388 |
p->vfs.iVersion = 1; |
|
7eb5b0a…
|
dan
|
389 |
p->vfs.szOsFile = sizeof(ArchiveFile); |
|
7eb5b0a…
|
dan
|
390 |
p->vfs.mxPathname = 512; |
|
7eb5b0a…
|
dan
|
391 |
p->vfs.pAppData = (void*)p->pBlob; |
|
7eb5b0a…
|
dan
|
392 |
p->vfs.xOpen = archiveOpen; |
|
7eb5b0a…
|
dan
|
393 |
p->vfs.xDelete = archiveDelete; |
|
7eb5b0a…
|
dan
|
394 |
p->vfs.xAccess = archiveAccess; |
|
7eb5b0a…
|
dan
|
395 |
p->vfs.xFullPathname = archiveFullPathname; |
|
7eb5b0a…
|
dan
|
396 |
p->vfs.xRandomness = archiveRandomness; |
|
7eb5b0a…
|
dan
|
397 |
p->vfs.xSleep = archiveSleep; |
|
7eb5b0a…
|
dan
|
398 |
p->vfs.xCurrentTime = archiveCurrentTime; |
|
7eb5b0a…
|
dan
|
399 |
p->vfs.xGetLastError = archiveGetLastError; |
|
7eb5b0a…
|
dan
|
400 |
sqlite3_vfs_register(&p->vfs, 0); |
|
275da70…
|
danield
|
401 |
sqlite3_open_v2("file:xyz.db", &p->db, |
|
7eb5b0a…
|
dan
|
402 |
SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE, p->vfs.zName |
|
7eb5b0a…
|
dan
|
403 |
); |
|
7eb5b0a…
|
dan
|
404 |
assert( p->db ); |
|
7eb5b0a…
|
dan
|
405 |
blob_zero(&p->tmp); |
|
275da70…
|
danield
|
406 |
sqlite3_exec(p->db, |
|
f5c81a6…
|
drh
|
407 |
"PRAGMA page_size=512;" |
|
7eb5b0a…
|
dan
|
408 |
"PRAGMA journal_mode = off;" |
|
7eb5b0a…
|
dan
|
409 |
"PRAGMA cache_spill = off;" |
|
7eb5b0a…
|
dan
|
410 |
"BEGIN;" |
|
2f9920a…
|
drh
|
411 |
"CREATE TABLE sqlar(\n" |
|
2f9920a…
|
drh
|
412 |
" name TEXT PRIMARY KEY, -- name of the file\n" |
|
2f9920a…
|
drh
|
413 |
" mode INT, -- access permissions\n" |
|
2f9920a…
|
drh
|
414 |
" mtime INT, -- last modification time\n" |
|
2f9920a…
|
drh
|
415 |
" sz INT, -- original file size\n" |
|
2f9920a…
|
drh
|
416 |
" data BLOB -- compressed content\n" |
|
7eb5b0a…
|
dan
|
417 |
");", 0, 0, 0 |
|
7eb5b0a…
|
dan
|
418 |
); |
|
275da70…
|
danield
|
419 |
sqlite3_prepare(p->db, |
|
275da70…
|
danield
|
420 |
"INSERT INTO sqlar VALUES(?, ?, ?, ?, ?)", -1, |
|
7eb5b0a…
|
dan
|
421 |
&p->pInsert, 0 |
|
7eb5b0a…
|
dan
|
422 |
); |
|
7eb5b0a…
|
dan
|
423 |
assert( p->pInsert ); |
|
7eb5b0a…
|
dan
|
424 |
|
|
7eb5b0a…
|
dan
|
425 |
sqlite3_bind_int64(p->pInsert, 3, unixTime); |
|
7eb5b0a…
|
dan
|
426 |
blob_zero(p->pBlob); |
|
7eb5b0a…
|
dan
|
427 |
} |
|
7eb5b0a…
|
dan
|
428 |
|
|
31b9822…
|
drh
|
429 |
if( nName==0 ) return; |
|
7eb5b0a…
|
dan
|
430 |
if( pFile==0 ){ |
|
7eb5b0a…
|
dan
|
431 |
/* Directory. */ |
|
7eb5b0a…
|
dan
|
432 |
if( zName[nName-1]=='/' ) nName--; |
|
7eb5b0a…
|
dan
|
433 |
sqlite3_bind_text(p->pInsert, 1, zName, nName, SQLITE_STATIC); |
|
7eb5b0a…
|
dan
|
434 |
sqlite3_bind_int(p->pInsert, 2, 040755); |
|
7eb5b0a…
|
dan
|
435 |
sqlite3_bind_int(p->pInsert, 4, 0); |
|
7eb5b0a…
|
dan
|
436 |
sqlite3_bind_null(p->pInsert, 5); |
|
7eb5b0a…
|
dan
|
437 |
}else{ |
|
7eb5b0a…
|
dan
|
438 |
sqlite3_bind_text(p->pInsert, 1, zName, nName, SQLITE_STATIC); |
|
7eb5b0a…
|
dan
|
439 |
if( mPerm==PERM_LNK ){ |
|
7eb5b0a…
|
dan
|
440 |
sqlite3_bind_int(p->pInsert, 2, 0120755); |
|
7eb5b0a…
|
dan
|
441 |
sqlite3_bind_int(p->pInsert, 4, -1); |
|
275da70…
|
danield
|
442 |
sqlite3_bind_text(p->pInsert, 5, |
|
7eb5b0a…
|
dan
|
443 |
blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC |
|
7eb5b0a…
|
dan
|
444 |
); |
|
7eb5b0a…
|
dan
|
445 |
}else{ |
|
53db40e…
|
drh
|
446 |
unsigned int nIn = blob_size(pFile); |
|
7eb5b0a…
|
dan
|
447 |
unsigned long int nOut = nIn; |
|
7eb5b0a…
|
dan
|
448 |
sqlite3_bind_int(p->pInsert, 2, mPerm==PERM_EXE ? 0100755 : 0100644); |
|
7eb5b0a…
|
dan
|
449 |
sqlite3_bind_int(p->pInsert, 4, nIn); |
|
7eb5b0a…
|
dan
|
450 |
zip_blob_minsize(&p->tmp, nIn); |
|
3f2cde2…
|
drh
|
451 |
compress( (unsigned char*) |
|
7eb5b0a…
|
dan
|
452 |
blob_buffer(&p->tmp), &nOut, (unsigned char*)blob_buffer(pFile), nIn |
|
7eb5b0a…
|
dan
|
453 |
); |
|
53db40e…
|
drh
|
454 |
if( nOut>=(unsigned long)nIn ){ |
|
275da70…
|
danield
|
455 |
sqlite3_bind_blob(p->pInsert, 5, |
|
7eb5b0a…
|
dan
|
456 |
blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC |
|
7eb5b0a…
|
dan
|
457 |
); |
|
7eb5b0a…
|
dan
|
458 |
}else{ |
|
275da70…
|
danield
|
459 |
sqlite3_bind_blob(p->pInsert, 5, |
|
7eb5b0a…
|
dan
|
460 |
blob_buffer(&p->tmp), nOut, SQLITE_STATIC |
|
7eb5b0a…
|
dan
|
461 |
); |
|
7eb5b0a…
|
dan
|
462 |
} |
|
7eb5b0a…
|
dan
|
463 |
} |
|
7eb5b0a…
|
dan
|
464 |
} |
|
7eb5b0a…
|
dan
|
465 |
|
|
7eb5b0a…
|
dan
|
466 |
sqlite3_step(p->pInsert); |
|
7eb5b0a…
|
dan
|
467 |
sqlite3_reset(p->pInsert); |
|
7eb5b0a…
|
dan
|
468 |
} |
|
7eb5b0a…
|
dan
|
469 |
|
|
7eb5b0a…
|
dan
|
470 |
static void zip_add_file( |
|
7eb5b0a…
|
dan
|
471 |
Archive *p, |
|
275da70…
|
danield
|
472 |
const char *zName, |
|
275da70…
|
danield
|
473 |
const Blob *pFile, |
|
7eb5b0a…
|
dan
|
474 |
int mPerm |
|
7eb5b0a…
|
dan
|
475 |
){ |
|
7eb5b0a…
|
dan
|
476 |
if( p->eType==ARCHIVE_ZIP ){ |
|
7eb5b0a…
|
dan
|
477 |
zip_add_file_to_zip(p, zName, pFile, mPerm); |
|
7eb5b0a…
|
dan
|
478 |
}else{ |
|
7eb5b0a…
|
dan
|
479 |
zip_add_file_to_sqlar(p, zName, pFile, mPerm); |
|
7eb5b0a…
|
dan
|
480 |
} |
|
7eb5b0a…
|
dan
|
481 |
} |
|
7eb5b0a…
|
dan
|
482 |
|
|
7eb5b0a…
|
dan
|
483 |
/* |
|
7eb5b0a…
|
dan
|
484 |
** If the given filename includes one or more directory entries, make |
|
7eb5b0a…
|
dan
|
485 |
** sure the directories are already in the archive. If they are not |
|
7eb5b0a…
|
dan
|
486 |
** in the archive, add them. |
|
7eb5b0a…
|
dan
|
487 |
*/ |
|
7eb5b0a…
|
dan
|
488 |
static void zip_add_folders(Archive *p, char *zName){ |
|
7eb5b0a…
|
dan
|
489 |
int i, c; |
|
7eb5b0a…
|
dan
|
490 |
int j; |
|
7eb5b0a…
|
dan
|
491 |
for(i=0; zName[i]; i++){ |
|
7eb5b0a…
|
dan
|
492 |
if( zName[i]=='/' ){ |
|
7eb5b0a…
|
dan
|
493 |
c = zName[i+1]; |
|
7eb5b0a…
|
dan
|
494 |
zName[i+1] = 0; |
|
7eb5b0a…
|
dan
|
495 |
for(j=0; j<nDir; j++){ |
|
7eb5b0a…
|
dan
|
496 |
if( fossil_strcmp(zName, azDir[j])==0 ) break; |
|
7eb5b0a…
|
dan
|
497 |
} |
|
7eb5b0a…
|
dan
|
498 |
if( j>=nDir ){ |
|
7eb5b0a…
|
dan
|
499 |
nDir++; |
|
7eb5b0a…
|
dan
|
500 |
azDir = fossil_realloc(azDir, sizeof(azDir[0])*nDir); |
|
4c3e172…
|
danield
|
501 |
azDir[j] = fossil_strdup(zName); |
|
7eb5b0a…
|
dan
|
502 |
zip_add_file(p, zName, 0, 0); |
|
7eb5b0a…
|
dan
|
503 |
} |
|
7eb5b0a…
|
dan
|
504 |
zName[i+1] = c; |
|
7eb5b0a…
|
dan
|
505 |
} |
|
7eb5b0a…
|
dan
|
506 |
} |
|
7eb5b0a…
|
dan
|
507 |
} |
|
7eb5b0a…
|
dan
|
508 |
|
|
7eb5b0a…
|
dan
|
509 |
/* |
|
7eb5b0a…
|
dan
|
510 |
** Free all the members of structure Archive allocated while processing |
|
7eb5b0a…
|
dan
|
511 |
** an SQLAR request. |
|
7eb5b0a…
|
dan
|
512 |
*/ |
|
7eb5b0a…
|
dan
|
513 |
static void free_archive(Archive *p){ |
|
7eb5b0a…
|
dan
|
514 |
if( p->vfs.zName ){ |
|
7eb5b0a…
|
dan
|
515 |
sqlite3_vfs_unregister(&p->vfs); |
|
7eb5b0a…
|
dan
|
516 |
fossil_free((char*)p->vfs.zName); |
|
7eb5b0a…
|
dan
|
517 |
p->vfs.zName = 0; |
|
7eb5b0a…
|
dan
|
518 |
} |
|
7eb5b0a…
|
dan
|
519 |
sqlite3_finalize(p->pInsert); |
|
7eb5b0a…
|
dan
|
520 |
p->pInsert = 0; |
|
7eb5b0a…
|
dan
|
521 |
sqlite3_close(p->db); |
|
7eb5b0a…
|
dan
|
522 |
p->db = 0; |
|
7eb5b0a…
|
dan
|
523 |
} |
|
9b780d2…
|
drh
|
524 |
|
|
9b780d2…
|
drh
|
525 |
/* |
|
9b780d2…
|
drh
|
526 |
** Write the ZIP archive into the given BLOB. |
|
9b780d2…
|
drh
|
527 |
*/ |
|
7eb5b0a…
|
dan
|
528 |
static void zip_close(Archive *p){ |
|
90048e0…
|
drh
|
529 |
int i; |
|
7eb5b0a…
|
dan
|
530 |
if( p->eType==ARCHIVE_ZIP ){ |
|
7eb5b0a…
|
dan
|
531 |
int iTocStart; |
|
7eb5b0a…
|
dan
|
532 |
int iTocEnd; |
|
7eb5b0a…
|
dan
|
533 |
char zBuf[30]; |
|
7eb5b0a…
|
dan
|
534 |
|
|
7eb5b0a…
|
dan
|
535 |
iTocStart = blob_size(&body); |
|
7eb5b0a…
|
dan
|
536 |
blob_append(&body, blob_buffer(&toc), blob_size(&toc)); |
|
7eb5b0a…
|
dan
|
537 |
iTocEnd = blob_size(&body); |
|
7eb5b0a…
|
dan
|
538 |
|
|
7eb5b0a…
|
dan
|
539 |
memset(zBuf, 0, sizeof(zBuf)); |
|
7eb5b0a…
|
dan
|
540 |
put32(&zBuf[0], 0x06054b50); |
|
7eb5b0a…
|
dan
|
541 |
put16(&zBuf[4], 0); |
|
7eb5b0a…
|
dan
|
542 |
put16(&zBuf[6], 0); |
|
7eb5b0a…
|
dan
|
543 |
put16(&zBuf[8], nEntry); |
|
7eb5b0a…
|
dan
|
544 |
put16(&zBuf[10], nEntry); |
|
7eb5b0a…
|
dan
|
545 |
put32(&zBuf[12], iTocEnd - iTocStart); |
|
7eb5b0a…
|
dan
|
546 |
put32(&zBuf[16], iTocStart); |
|
7eb5b0a…
|
dan
|
547 |
put16(&zBuf[20], 0); |
|
7eb5b0a…
|
dan
|
548 |
blob_append(&body, zBuf, 22); |
|
7eb5b0a…
|
dan
|
549 |
blob_reset(&toc); |
|
7eb5b0a…
|
dan
|
550 |
*(p->pBlob) = body; |
|
7eb5b0a…
|
dan
|
551 |
blob_zero(&body); |
|
7eb5b0a…
|
dan
|
552 |
}else{ |
|
7eb5b0a…
|
dan
|
553 |
if( p->db ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); |
|
7eb5b0a…
|
dan
|
554 |
free_archive(p); |
|
7eb5b0a…
|
dan
|
555 |
blob_reset(&p->tmp); |
|
7eb5b0a…
|
dan
|
556 |
} |
|
7eb5b0a…
|
dan
|
557 |
|
|
9b780d2…
|
drh
|
558 |
nEntry = 0; |
|
90048e0…
|
drh
|
559 |
for(i=0; i<nDir; i++){ |
|
8206ac9…
|
stephan
|
560 |
fossil_free(azDir[i]); |
|
90048e0…
|
drh
|
561 |
} |
|
8206ac9…
|
stephan
|
562 |
fossil_free(azDir); |
|
90048e0…
|
drh
|
563 |
nDir = 0; |
|
90048e0…
|
drh
|
564 |
azDir = 0; |
|
9b780d2…
|
drh
|
565 |
} |
|
90048e0…
|
drh
|
566 |
|
|
932d351…
|
drh
|
567 |
/* Functions found in shell.c */ |
|
932d351…
|
drh
|
568 |
extern int sqlite3_fileio_init(sqlite3*,char**,const sqlite3_api_routines*); |
|
932d351…
|
drh
|
569 |
extern int sqlite3_zipfile_init(sqlite3*,char**,const sqlite3_api_routines*); |
|
932d351…
|
drh
|
570 |
|
|
9b780d2…
|
drh
|
571 |
/* |
|
9b780d2…
|
drh
|
572 |
** COMMAND: test-filezip |
|
9b780d2…
|
drh
|
573 |
** |
|
932d351…
|
drh
|
574 |
** Usage: %fossil test-filezip [OPTIONS] ZIPFILE [FILENAME...] |
|
932d351…
|
drh
|
575 |
** |
|
932d351…
|
drh
|
576 |
** This command uses Fossil infrastructure or read or create a ZIP |
|
932d351…
|
drh
|
577 |
** archive named by the ZIPFILE argument. With no options, a new |
|
932d351…
|
drh
|
578 |
** ZIP archive is created and there must be at least one FILENAME |
|
932d351…
|
drh
|
579 |
** argument. If the -l option is used, the contents of the named ZIP |
|
932d351…
|
drh
|
580 |
** archive are listed on standard output. With the -x argument, the |
|
932d351…
|
drh
|
581 |
** contents of the ZIP archive are extracted. |
|
932d351…
|
drh
|
582 |
** |
|
932d351…
|
drh
|
583 |
** There are two purposes for this command: (1) To server as a test |
|
932d351…
|
drh
|
584 |
** platform for the Fossil ZIP archive generator, and (2) to provide |
|
932d351…
|
drh
|
585 |
** rudimentary ZIP archive creation capabilities on platforms that do |
|
932d351…
|
drh
|
586 |
** not have the "zip" command installed. |
|
932d351…
|
drh
|
587 |
** |
|
932d351…
|
drh
|
588 |
** Options: |
|
932d351…
|
drh
|
589 |
** |
|
932d351…
|
drh
|
590 |
** -h|--dereference Follow symlinks |
|
932d351…
|
drh
|
591 |
** -l|--list List the contents of the ZIP archive |
|
932d351…
|
drh
|
592 |
** -x|--extract Extract files from a ZIP archive |
|
9b780d2…
|
drh
|
593 |
*/ |
|
9b780d2…
|
drh
|
594 |
void filezip_cmd(void){ |
|
1772357…
|
drh
|
595 |
int eFType = SymFILE; |
|
932d351…
|
drh
|
596 |
int doList = 0; |
|
932d351…
|
drh
|
597 |
int doExtract = 0; |
|
932d351…
|
drh
|
598 |
char *zArchiveName; |
|
1772357…
|
drh
|
599 |
if( find_option("dereference","h",0)!=0 ){ |
|
1772357…
|
drh
|
600 |
eFType = ExtFILE; |
|
1772357…
|
drh
|
601 |
} |
|
932d351…
|
drh
|
602 |
if( find_option("list","l",0)!=0 ){ |
|
932d351…
|
drh
|
603 |
doList = 1; |
|
932d351…
|
drh
|
604 |
} |
|
932d351…
|
drh
|
605 |
if( find_option("extract","x",0)!=0 ){ |
|
932d351…
|
drh
|
606 |
if( doList ){ |
|
932d351…
|
drh
|
607 |
fossil_fatal("incompatible options: -l and -x"); |
|
932d351…
|
drh
|
608 |
} |
|
932d351…
|
drh
|
609 |
doExtract = 1; |
|
932d351…
|
drh
|
610 |
} |
|
6368fce…
|
drh
|
611 |
if( g.argc<3 ){ |
|
932d351…
|
drh
|
612 |
usage("ARCHIVE FILES..."); |
|
6368fce…
|
drh
|
613 |
} |
|
932d351…
|
drh
|
614 |
zArchiveName = g.argv[2]; |
|
6368fce…
|
drh
|
615 |
sqlite3_open(":memory:", &g.db); |
|
932d351…
|
drh
|
616 |
if( doList ){ |
|
932d351…
|
drh
|
617 |
/* Do a content listing of a ZIP archive */ |
|
932d351…
|
drh
|
618 |
Stmt q; |
|
932d351…
|
drh
|
619 |
int nRow = 0; |
|
932d351…
|
drh
|
620 |
i64 szTotal = 0; |
|
932d351…
|
drh
|
621 |
if( file_size(zArchiveName, eFType)<0 ){ |
|
932d351…
|
drh
|
622 |
fossil_fatal("No such ZIP archive: %s", zArchiveName); |
|
932d351…
|
drh
|
623 |
} |
|
932d351…
|
drh
|
624 |
if( g.argc>3 ){ |
|
932d351…
|
drh
|
625 |
fossil_fatal("extra arguments after \"fossil test-filezip -l ARCHIVE\""); |
|
932d351…
|
drh
|
626 |
} |
|
932d351…
|
drh
|
627 |
sqlite3_zipfile_init(g.db, 0, 0); |
|
932d351…
|
drh
|
628 |
db_multi_exec("CREATE VIRTUAL TABLE z1 USING zipfile(%Q)", zArchiveName); |
|
31ce0d3…
|
drh
|
629 |
db_prepare(&q, |
|
31ce0d3…
|
drh
|
630 |
"SELECT sz, datetime(mtime,'unixepoch')," |
|
31ce0d3…
|
drh
|
631 |
" if(((mode>>12)&15)==10,name||' -> '||data,name) FROM z1" |
|
31ce0d3…
|
drh
|
632 |
); |
|
932d351…
|
drh
|
633 |
while( db_step(&q)==SQLITE_ROW ){ |
|
932d351…
|
drh
|
634 |
int sz = db_column_int(&q, 0); |
|
932d351…
|
drh
|
635 |
szTotal += sz; |
|
932d351…
|
drh
|
636 |
if( nRow==0 ){ |
|
932d351…
|
drh
|
637 |
fossil_print(" Length Date Time Name\n"); |
|
932d351…
|
drh
|
638 |
fossil_print("--------- ---------- ----- ----\n"); |
|
932d351…
|
drh
|
639 |
} |
|
932d351…
|
drh
|
640 |
nRow++; |
|
932d351…
|
drh
|
641 |
fossil_print("%9d %.16s %s\n", sz, db_column_text(&q,1), |
|
932d351…
|
drh
|
642 |
db_column_text(&q,2)); |
|
932d351…
|
drh
|
643 |
} |
|
932d351…
|
drh
|
644 |
if( nRow ){ |
|
932d351…
|
drh
|
645 |
fossil_print("--------- --------\n"); |
|
932d351…
|
drh
|
646 |
fossil_print("%9lld %16s %d files\n", szTotal, "", nRow); |
|
932d351…
|
drh
|
647 |
} |
|
932d351…
|
drh
|
648 |
db_finalize(&q); |
|
932d351…
|
drh
|
649 |
}else if( doExtract ){ |
|
932d351…
|
drh
|
650 |
/* Extract files from an existing ZIP archive */ |
|
932d351…
|
drh
|
651 |
if( file_size(zArchiveName, eFType)<0 ){ |
|
932d351…
|
drh
|
652 |
fossil_fatal("No such ZIP archive: %s", zArchiveName); |
|
932d351…
|
drh
|
653 |
} |
|
932d351…
|
drh
|
654 |
if( g.argc>3 ){ |
|
932d351…
|
drh
|
655 |
fossil_fatal("extra arguments after \"fossil test-filezip -x ARCHIVE\""); |
|
932d351…
|
drh
|
656 |
} |
|
932d351…
|
drh
|
657 |
sqlite3_zipfile_init(g.db, 0, 0); |
|
932d351…
|
drh
|
658 |
sqlite3_fileio_init(g.db, 0, 0); |
|
932d351…
|
drh
|
659 |
db_multi_exec("CREATE VIRTUAL TABLE z1 USING zipfile(%Q)", zArchiveName); |
|
31ce0d3…
|
drh
|
660 |
db_multi_exec( |
|
31ce0d3…
|
drh
|
661 |
"SELECT writefile(name,data) FROM z1" |
|
31ce0d3…
|
drh
|
662 |
" WHERE ((mode>>12)&15)!=10" |
|
31ce0d3…
|
drh
|
663 |
); |
|
932d351…
|
drh
|
664 |
}else{ |
|
932d351…
|
drh
|
665 |
/* Without the -x or -l options, construct a new ZIP archive */ |
|
932d351…
|
drh
|
666 |
int i; |
|
932d351…
|
drh
|
667 |
Blob zip; |
|
932d351…
|
drh
|
668 |
Blob file; |
|
932d351…
|
drh
|
669 |
Archive sArchive; |
|
932d351…
|
drh
|
670 |
memset(&sArchive, 0, sizeof(Archive)); |
|
932d351…
|
drh
|
671 |
sArchive.eType = ARCHIVE_ZIP; |
|
932d351…
|
drh
|
672 |
sArchive.pBlob = &zip; |
|
932d351…
|
drh
|
673 |
if( file_size(zArchiveName, eFType)>0 ){ |
|
932d351…
|
drh
|
674 |
fossil_fatal("ZIP archive %s already exists", zArchiveName); |
|
932d351…
|
drh
|
675 |
} |
|
932d351…
|
drh
|
676 |
zip_open(); |
|
932d351…
|
drh
|
677 |
for(i=3; i<g.argc; i++){ |
|
932d351…
|
drh
|
678 |
double rDate; |
|
932d351…
|
drh
|
679 |
i64 iDate; |
|
932d351…
|
drh
|
680 |
blob_zero(&file); |
|
932d351…
|
drh
|
681 |
blob_read_from_file(&file, g.argv[i], eFType); |
|
932d351…
|
drh
|
682 |
iDate = file_mtime(g.argv[i], eFType); |
|
932d351…
|
drh
|
683 |
rDate = ((double)iDate)/86400.0 + 2440587.5; |
|
932d351…
|
drh
|
684 |
zip_set_timedate(rDate); |
|
932d351…
|
drh
|
685 |
zip_add_file(&sArchive, g.argv[i], &file, file_perm(0,eFType)); |
|
932d351…
|
drh
|
686 |
blob_reset(&file); |
|
932d351…
|
drh
|
687 |
} |
|
932d351…
|
drh
|
688 |
zip_close(&sArchive); |
|
932d351…
|
drh
|
689 |
blob_write_to_file(&zip, g.argv[2]); |
|
932d351…
|
drh
|
690 |
} |
|
dead090…
|
drh
|
691 |
} |
|
dead090…
|
drh
|
692 |
|
|
dead090…
|
drh
|
693 |
/* |
|
dead090…
|
drh
|
694 |
** Given the RID for a manifest, construct a ZIP archive containing |
|
dead090…
|
drh
|
695 |
** all files in the corresponding baseline. |
|
dead090…
|
drh
|
696 |
** |
|
dead090…
|
drh
|
697 |
** If RID is for an object that is not a real manifest, then the |
|
dead090…
|
drh
|
698 |
** resulting ZIP archive contains a single file which is the RID |
|
ef449a1…
|
drh
|
699 |
** object. The pInclude and pExclude parameters are ignored in this case. |
|
dead090…
|
drh
|
700 |
** |
|
dead090…
|
drh
|
701 |
** If the RID object does not exist in the repository, then |
|
dead090…
|
drh
|
702 |
** pZip is zeroed. |
|
8672e24…
|
drh
|
703 |
** |
|
4bbb00a…
|
drh
|
704 |
** zDir is a "synthetic" subdirectory which all zipped files get |
|
8672e24…
|
drh
|
705 |
** added to as part of the zip file. It may be 0 or an empty string, |
|
8672e24…
|
drh
|
706 |
** in which case it is ignored. The intention is to create a zip which |
|
8672e24…
|
drh
|
707 |
** politely expands into a subdir instead of filling your current dir |
|
8ad5e46…
|
wyoung
|
708 |
** with source files. For example, pass a commit hash or "ProjectName". |
|
4bbb00a…
|
drh
|
709 |
** |
|
4bbb00a…
|
drh
|
710 |
*/ |
|
7eb5b0a…
|
dan
|
711 |
static void zip_of_checkin( |
|
7eb5b0a…
|
dan
|
712 |
int eType, /* Type of archive (ZIP or SQLAR) */ |
|
bc36fdc…
|
danield
|
713 |
int rid, /* The RID of the check-in to build the archive from */ |
|
7eb5b0a…
|
dan
|
714 |
Blob *pZip, /* Write the archive content into this blob */ |
|
7eb5b0a…
|
dan
|
715 |
const char *zDir, /* Top-level directory of the archive */ |
|
ef449a1…
|
drh
|
716 |
Glob *pInclude, /* Only include files that match this pattern */ |
|
c88880f…
|
drh
|
717 |
Glob *pExclude, /* Exclude files that match this pattern */ |
|
c88880f…
|
drh
|
718 |
int listFlag /* Print each file on stdout */ |
|
ef449a1…
|
drh
|
719 |
){ |
|
d13054c…
|
drh
|
720 |
Blob mfile, hash, file; |
|
d13054c…
|
drh
|
721 |
Manifest *pManifest; |
|
d13054c…
|
drh
|
722 |
ManifestFile *pFile; |
|
4bbb00a…
|
drh
|
723 |
Blob filename; |
|
4bbb00a…
|
drh
|
724 |
int nPrefix; |
|
52b35c8…
|
jan.nijtmans
|
725 |
|
|
7eb5b0a…
|
dan
|
726 |
Archive sArchive; |
|
7eb5b0a…
|
dan
|
727 |
memset(&sArchive, 0, sizeof(Archive)); |
|
7eb5b0a…
|
dan
|
728 |
sArchive.eType = eType; |
|
7eb5b0a…
|
dan
|
729 |
sArchive.pBlob = pZip; |
|
3e2b5c4…
|
drh
|
730 |
blob_zero(&sArchive.tmp); |
|
c88880f…
|
drh
|
731 |
if( pZip ) blob_zero(pZip); |
|
7eb5b0a…
|
dan
|
732 |
|
|
4bbb00a…
|
drh
|
733 |
content_get(rid, &mfile); |
|
4bbb00a…
|
drh
|
734 |
if( blob_size(&mfile)==0 ){ |
|
4bbb00a…
|
drh
|
735 |
return; |
|
4bbb00a…
|
drh
|
736 |
} |
|
fd9b7bd…
|
drh
|
737 |
blob_set_dynamic(&hash, rid_to_uuid(rid)); |
|
4bbb00a…
|
drh
|
738 |
blob_zero(&filename); |
|
c88880f…
|
drh
|
739 |
if( pZip ) zip_open(); |
|
4bbb00a…
|
drh
|
740 |
|
|
4bbb00a…
|
drh
|
741 |
if( zDir && zDir[0] ){ |
|
4bbb00a…
|
drh
|
742 |
blob_appendf(&filename, "%s/", zDir); |
|
4bbb00a…
|
drh
|
743 |
} |
|
4bbb00a…
|
drh
|
744 |
nPrefix = blob_size(&filename); |
|
4bbb00a…
|
drh
|
745 |
|
|
ec81aee…
|
jan.nijtmans
|
746 |
pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0); |
|
d13054c…
|
drh
|
747 |
if( pManifest ){ |
|
189bfc2…
|
jan
|
748 |
int flg, eflg = 0; |
|
3f06618…
|
mistachkin
|
749 |
char *zName = 0; |
|
d13054c…
|
drh
|
750 |
zip_set_timedate(pManifest->rDate); |
|
dfc0f1b…
|
drh
|
751 |
flg = db_get_manifest_setting(blob_str(&hash)); |
|
189bfc2…
|
jan
|
752 |
if( flg ){ |
|
189bfc2…
|
jan
|
753 |
/* eflg is the effective flags, taking include/exclude into account */ |
|
189bfc2…
|
jan
|
754 |
if( (pInclude==0 || glob_match(pInclude, "manifest")) |
|
189bfc2…
|
jan
|
755 |
&& !glob_match(pExclude, "manifest") |
|
189bfc2…
|
jan
|
756 |
&& (flg & MFESTFLG_RAW) ){ |
|
189bfc2…
|
jan
|
757 |
eflg |= MFESTFLG_RAW; |
|
189bfc2…
|
jan
|
758 |
} |
|
189bfc2…
|
jan
|
759 |
if( (pInclude==0 || glob_match(pInclude, "manifest.uuid")) |
|
189bfc2…
|
jan
|
760 |
&& !glob_match(pExclude, "manifest.uuid") |
|
189bfc2…
|
jan
|
761 |
&& (flg & MFESTFLG_UUID) ){ |
|
189bfc2…
|
jan
|
762 |
eflg |= MFESTFLG_UUID; |
|
189bfc2…
|
jan
|
763 |
} |
|
189bfc2…
|
jan
|
764 |
if( (pInclude==0 || glob_match(pInclude, "manifest.tags")) |
|
189bfc2…
|
jan
|
765 |
&& !glob_match(pExclude, "manifest.tags") |
|
189bfc2…
|
jan
|
766 |
&& (flg & MFESTFLG_TAGS) ){ |
|
189bfc2…
|
jan
|
767 |
eflg |= MFESTFLG_TAGS; |
|
189bfc2…
|
jan
|
768 |
} |
|
189bfc2…
|
jan
|
769 |
|
|
b9de604…
|
andygoth
|
770 |
if( eflg & MFESTFLG_RAW ){ |
|
b9de604…
|
andygoth
|
771 |
blob_append(&filename, "manifest", -1); |
|
b9de604…
|
andygoth
|
772 |
zName = blob_str(&filename); |
|
c88880f…
|
drh
|
773 |
if( listFlag ) fossil_print("%s\n", zName); |
|
c88880f…
|
drh
|
774 |
if( pZip ){ |
|
c88880f…
|
drh
|
775 |
zip_add_folders(&sArchive, zName); |
|
c88880f…
|
drh
|
776 |
zip_add_file(&sArchive, zName, &mfile, 0); |
|
c88880f…
|
drh
|
777 |
} |
|
fd9b7bd…
|
drh
|
778 |
} |
|
189bfc2…
|
jan
|
779 |
if( eflg & MFESTFLG_UUID ){ |
|
189bfc2…
|
jan
|
780 |
blob_append(&hash, "\n", 1); |
|
189bfc2…
|
jan
|
781 |
blob_resize(&filename, nPrefix); |
|
189bfc2…
|
jan
|
782 |
blob_append(&filename, "manifest.uuid", -1); |
|
189bfc2…
|
jan
|
783 |
zName = blob_str(&filename); |
|
c88880f…
|
drh
|
784 |
if( listFlag ) fossil_print("%s\n", zName); |
|
c88880f…
|
drh
|
785 |
if( pZip ){ |
|
c88880f…
|
drh
|
786 |
zip_add_folders(&sArchive, zName); |
|
c88880f…
|
drh
|
787 |
zip_add_file(&sArchive, zName, &hash, 0); |
|
c88880f…
|
drh
|
788 |
} |
|
189bfc2…
|
jan
|
789 |
} |
|
189bfc2…
|
jan
|
790 |
if( eflg & MFESTFLG_TAGS ){ |
|
189bfc2…
|
jan
|
791 |
blob_resize(&filename, nPrefix); |
|
189bfc2…
|
jan
|
792 |
blob_append(&filename, "manifest.tags", -1); |
|
189bfc2…
|
jan
|
793 |
zName = blob_str(&filename); |
|
c88880f…
|
drh
|
794 |
if( listFlag ) fossil_print("%s\n", zName); |
|
c88880f…
|
drh
|
795 |
if( pZip ){ |
|
c88880f…
|
drh
|
796 |
Blob tagslist; |
|
c88880f…
|
drh
|
797 |
blob_zero(&tagslist); |
|
c88880f…
|
drh
|
798 |
get_checkin_taglist(rid, &tagslist); |
|
c88880f…
|
drh
|
799 |
zip_add_folders(&sArchive, zName); |
|
c88880f…
|
drh
|
800 |
zip_add_file(&sArchive, zName, &tagslist, 0); |
|
c88880f…
|
drh
|
801 |
blob_reset(&tagslist); |
|
c88880f…
|
drh
|
802 |
} |
|
189bfc2…
|
jan
|
803 |
} |
|
d13054c…
|
drh
|
804 |
} |
|
d13054c…
|
drh
|
805 |
manifest_file_rewind(pManifest); |
|
c88880f…
|
drh
|
806 |
if( pZip ) zip_add_file(&sArchive, "", 0, 0); |
|
d13054c…
|
drh
|
807 |
while( (pFile = manifest_file_next(pManifest,0))!=0 ){ |
|
ef449a1…
|
drh
|
808 |
int fid; |
|
ef449a1…
|
drh
|
809 |
if( pInclude!=0 && !glob_match(pInclude, pFile->zName) ) continue; |
|
ef449a1…
|
drh
|
810 |
if( glob_match(pExclude, pFile->zName) ) continue; |
|
ef449a1…
|
drh
|
811 |
fid = uuid_to_rid(pFile->zUuid, 0); |
|
d13054c…
|
drh
|
812 |
if( fid ){ |
|
d13054c…
|
drh
|
813 |
blob_resize(&filename, nPrefix); |
|
d13054c…
|
drh
|
814 |
blob_append(&filename, pFile->zName, -1); |
|
d13054c…
|
drh
|
815 |
zName = blob_str(&filename); |
|
c88880f…
|
drh
|
816 |
if( listFlag ) fossil_print("%s\n", zName); |
|
c88880f…
|
drh
|
817 |
if( pZip ){ |
|
c88880f…
|
drh
|
818 |
content_get(fid, &file); |
|
c88880f…
|
drh
|
819 |
zip_add_folders(&sArchive, zName); |
|
c88880f…
|
drh
|
820 |
zip_add_file(&sArchive, zName, &file, manifest_file_mperm(pFile)); |
|
c88880f…
|
drh
|
821 |
blob_reset(&file); |
|
c88880f…
|
drh
|
822 |
} |
|
d13054c…
|
drh
|
823 |
} |
|
d13054c…
|
drh
|
824 |
} |
|
d13054c…
|
drh
|
825 |
} |
|
b9de604…
|
andygoth
|
826 |
blob_reset(&mfile); |
|
d13054c…
|
drh
|
827 |
manifest_destroy(pManifest); |
|
d13054c…
|
drh
|
828 |
blob_reset(&filename); |
|
fd9b7bd…
|
drh
|
829 |
blob_reset(&hash); |
|
c88880f…
|
drh
|
830 |
if( pZip ){ |
|
c88880f…
|
drh
|
831 |
zip_close(&sArchive); |
|
c88880f…
|
drh
|
832 |
} |
|
7eb5b0a…
|
dan
|
833 |
} |
|
7eb5b0a…
|
dan
|
834 |
|
|
7eb5b0a…
|
dan
|
835 |
/* |
|
7eb5b0a…
|
dan
|
836 |
** Implementation of zip_cmd and sqlar_cmd. |
|
7eb5b0a…
|
dan
|
837 |
*/ |
|
7eb5b0a…
|
dan
|
838 |
static void archive_cmd(int eType){ |
|
7eb5b0a…
|
dan
|
839 |
int rid; |
|
7eb5b0a…
|
dan
|
840 |
Blob zip; |
|
7eb5b0a…
|
dan
|
841 |
const char *zName; |
|
7eb5b0a…
|
dan
|
842 |
Glob *pInclude = 0; |
|
7eb5b0a…
|
dan
|
843 |
Glob *pExclude = 0; |
|
7eb5b0a…
|
dan
|
844 |
const char *zInclude; |
|
7eb5b0a…
|
dan
|
845 |
const char *zExclude; |
|
c88880f…
|
drh
|
846 |
int listFlag = 0; |
|
c88880f…
|
drh
|
847 |
const char *zOut; |
|
7eb5b0a…
|
dan
|
848 |
|
|
7eb5b0a…
|
dan
|
849 |
zName = find_option("name", 0, 1); |
|
7eb5b0a…
|
dan
|
850 |
zExclude = find_option("exclude", "X", 1); |
|
7eb5b0a…
|
dan
|
851 |
if( zExclude ) pExclude = glob_create(zExclude); |
|
7eb5b0a…
|
dan
|
852 |
zInclude = find_option("include", 0, 1); |
|
7eb5b0a…
|
dan
|
853 |
if( zInclude ) pInclude = glob_create(zInclude); |
|
c88880f…
|
drh
|
854 |
listFlag = find_option("list","l",0)!=0; |
|
7eb5b0a…
|
dan
|
855 |
db_find_and_open_repository(0, 0); |
|
7eb5b0a…
|
dan
|
856 |
|
|
7eb5b0a…
|
dan
|
857 |
/* We should be done with options.. */ |
|
7eb5b0a…
|
dan
|
858 |
verify_all_options(); |
|
7eb5b0a…
|
dan
|
859 |
|
|
7eb5b0a…
|
dan
|
860 |
if( g.argc!=4 ){ |
|
7eb5b0a…
|
dan
|
861 |
usage("VERSION OUTPUTFILE"); |
|
7eb5b0a…
|
dan
|
862 |
} |
|
7eb5b0a…
|
dan
|
863 |
g.zOpenRevision = g.argv[2]; |
|
7eb5b0a…
|
dan
|
864 |
rid = name_to_typed_rid(g.argv[2], "ci"); |
|
7eb5b0a…
|
dan
|
865 |
if( rid==0 ){ |
|
7eb5b0a…
|
dan
|
866 |
fossil_fatal("Check-in not found: %s", g.argv[2]); |
|
7eb5b0a…
|
dan
|
867 |
return; |
|
7eb5b0a…
|
dan
|
868 |
} |
|
c88880f…
|
drh
|
869 |
zOut = g.argv[3]; |
|
c88880f…
|
drh
|
870 |
if( fossil_strcmp(zOut,"")==0 || fossil_strcmp(zOut,"/dev/null")==0 ){ |
|
c88880f…
|
drh
|
871 |
zOut = 0; |
|
c88880f…
|
drh
|
872 |
} |
|
7eb5b0a…
|
dan
|
873 |
|
|
7eb5b0a…
|
dan
|
874 |
if( zName==0 ){ |
|
6ce705b…
|
drh
|
875 |
zName = archive_base_name(rid); |
|
275da70…
|
danield
|
876 |
} |
|
275da70…
|
danield
|
877 |
zip_of_checkin(eType, rid, zOut ? &zip : 0, |
|
c88880f…
|
drh
|
878 |
zName, pInclude, pExclude, listFlag); |
|
7eb5b0a…
|
dan
|
879 |
glob_free(pInclude); |
|
7eb5b0a…
|
dan
|
880 |
glob_free(pExclude); |
|
c88880f…
|
drh
|
881 |
if( zOut ){ |
|
c88880f…
|
drh
|
882 |
blob_write_to_file(&zip, zOut); |
|
c88880f…
|
drh
|
883 |
blob_reset(&zip); |
|
c88880f…
|
drh
|
884 |
} |
|
ef449a1…
|
drh
|
885 |
} |
|
ef449a1…
|
drh
|
886 |
|
|
ef449a1…
|
drh
|
887 |
/* |
|
ef449a1…
|
drh
|
888 |
** COMMAND: zip* |
|
ef449a1…
|
drh
|
889 |
** |
|
ef449a1…
|
drh
|
890 |
** Usage: %fossil zip VERSION OUTPUTFILE [OPTIONS] |
|
ef449a1…
|
drh
|
891 |
** |
|
ef449a1…
|
drh
|
892 |
** Generate a ZIP archive for a check-in. If the --name option is |
|
ef449a1…
|
drh
|
893 |
** used, its argument becomes the name of the top-level directory in the |
|
ef449a1…
|
drh
|
894 |
** resulting ZIP archive. If --name is omitted, the top-level directory |
|
953cfbd…
|
mistachkin
|
895 |
** name is derived from the project name, the check-in date and time, and |
|
953cfbd…
|
mistachkin
|
896 |
** the artifact ID of the check-in. |
|
953cfbd…
|
mistachkin
|
897 |
** |
|
953cfbd…
|
mistachkin
|
898 |
** The GLOBLIST argument to --exclude and --include can be a comma-separated |
|
953cfbd…
|
mistachkin
|
899 |
** list of glob patterns, where each glob pattern may optionally be enclosed |
|
953cfbd…
|
mistachkin
|
900 |
** in "..." or '...' so that it may contain commas. If a file matches both |
|
953cfbd…
|
mistachkin
|
901 |
** --include and --exclude then it is excluded. |
|
953cfbd…
|
mistachkin
|
902 |
** |
|
c88880f…
|
drh
|
903 |
** If OUTPUTFILE is an empty string or "/dev/null" then no ZIP archive is |
|
c88880f…
|
drh
|
904 |
** actually generated. This feature can be used in combination with |
|
d708847…
|
andybradford
|
905 |
** the --list option to get a list of the filenames that would be in the |
|
c88880f…
|
drh
|
906 |
** ZIP archive had it actually been generated. |
|
c88880f…
|
drh
|
907 |
** |
|
953cfbd…
|
mistachkin
|
908 |
** Options: |
|
953cfbd…
|
mistachkin
|
909 |
** -X|--exclude GLOBLIST Comma-separated list of GLOBs of files to exclude |
|
953cfbd…
|
mistachkin
|
910 |
** --include GLOBLIST Comma-separated list of GLOBs of files to include |
|
c88880f…
|
drh
|
911 |
** -l|--list Show archive content on stdout |
|
953cfbd…
|
mistachkin
|
912 |
** --name DIRECTORYNAME The name of the top-level directory in the archive |
|
953cfbd…
|
mistachkin
|
913 |
** -R REPOSITORY Specify a Fossil repository |
|
953cfbd…
|
mistachkin
|
914 |
*/ |
|
953cfbd…
|
mistachkin
|
915 |
void zip_cmd(void){ |
|
7eb5b0a…
|
dan
|
916 |
archive_cmd(ARCHIVE_ZIP); |
|
7eb5b0a…
|
dan
|
917 |
} |
|
7eb5b0a…
|
dan
|
918 |
|
|
7eb5b0a…
|
dan
|
919 |
/* |
|
7eb5b0a…
|
dan
|
920 |
** COMMAND: sqlar* |
|
7eb5b0a…
|
dan
|
921 |
** |
|
7eb5b0a…
|
dan
|
922 |
** Usage: %fossil sqlar VERSION OUTPUTFILE [OPTIONS] |
|
7eb5b0a…
|
dan
|
923 |
** |
|
7eb5b0a…
|
dan
|
924 |
** Generate an SQLAR archive for a check-in. If the --name option is |
|
7eb5b0a…
|
dan
|
925 |
** used, its argument becomes the name of the top-level directory in the |
|
7eb5b0a…
|
dan
|
926 |
** resulting SQLAR archive. If --name is omitted, the top-level directory |
|
7eb5b0a…
|
dan
|
927 |
** name is derived from the project name, the check-in date and time, and |
|
7eb5b0a…
|
dan
|
928 |
** the artifact ID of the check-in. |
|
7eb5b0a…
|
dan
|
929 |
** |
|
7eb5b0a…
|
dan
|
930 |
** The GLOBLIST argument to --exclude and --include can be a comma-separated |
|
7eb5b0a…
|
dan
|
931 |
** list of glob patterns, where each glob pattern may optionally be enclosed |
|
7eb5b0a…
|
dan
|
932 |
** in "..." or '...' so that it may contain commas. If a file matches both |
|
7eb5b0a…
|
dan
|
933 |
** --include and --exclude then it is excluded. |
|
7eb5b0a…
|
dan
|
934 |
** |
|
c88880f…
|
drh
|
935 |
** If OUTPUTFILE is an empty string or "/dev/null" then no SQLAR archive is |
|
c88880f…
|
drh
|
936 |
** actually generated. This feature can be used in combination with |
|
d708847…
|
andybradford
|
937 |
** the --list option to get a list of the filenames that would be in the |
|
c88880f…
|
drh
|
938 |
** SQLAR archive had it actually been generated. |
|
c88880f…
|
drh
|
939 |
** |
|
7eb5b0a…
|
dan
|
940 |
** Options: |
|
7eb5b0a…
|
dan
|
941 |
** -X|--exclude GLOBLIST Comma-separated list of GLOBs of files to exclude |
|
7eb5b0a…
|
dan
|
942 |
** --include GLOBLIST Comma-separated list of GLOBs of files to include |
|
c88880f…
|
drh
|
943 |
** -l|--list Show archive content on stdout |
|
7eb5b0a…
|
dan
|
944 |
** --name DIRECTORYNAME The name of the top-level directory in the archive |
|
7eb5b0a…
|
dan
|
945 |
** -R REPOSITORY Specify a Fossil repository |
|
7eb5b0a…
|
dan
|
946 |
*/ |
|
7eb5b0a…
|
dan
|
947 |
void sqlar_cmd(void){ |
|
7eb5b0a…
|
dan
|
948 |
archive_cmd(ARCHIVE_SQLAR); |
|
7c2577b…
|
drh
|
949 |
} |
|
7c2577b…
|
drh
|
950 |
|
|
7c2577b…
|
drh
|
951 |
/* |
|
768e192…
|
drh
|
952 |
** WEBPAGE: sqlar |
|
7c2577b…
|
drh
|
953 |
** WEBPAGE: zip |
|
768e192…
|
drh
|
954 |
** |
|
9eadac2…
|
drh
|
955 |
** URLs: |
|
9eadac2…
|
drh
|
956 |
** |
|
4717db3…
|
wyoung
|
957 |
** /zip/[VERSION/]NAME.zip |
|
4717db3…
|
wyoung
|
958 |
** /sqlar/[VERSION/]NAME.sqlar |
|
9eadac2…
|
drh
|
959 |
** |
|
9eadac2…
|
drh
|
960 |
** Generate a ZIP Archive or an SQL Archive for the check-in specified by |
|
9eadac2…
|
drh
|
961 |
** VERSION. The archive is called NAME.zip or NAME.sqlar and has a top-level |
|
9eadac2…
|
drh
|
962 |
** directory called NAME. |
|
9eadac2…
|
drh
|
963 |
** |
|
3a6dd83…
|
drh
|
964 |
** The optional VERSION element defaults to the name of the main branch |
|
3a6dd83…
|
drh
|
965 |
** (usually "trunk") per the r= rules below. |
|
9eadac2…
|
drh
|
966 |
** All of the following URLs are equivalent: |
|
9eadac2…
|
drh
|
967 |
** |
|
9eadac2…
|
drh
|
968 |
** /zip/release/xyz.zip |
|
9eadac2…
|
drh
|
969 |
** /zip?r=release&name=xyz.zip |
|
9eadac2…
|
drh
|
970 |
** /zip/xyz.zip?r=release |
|
9eadac2…
|
drh
|
971 |
** /zip?name=release/xyz.zip |
|
ef449a1…
|
drh
|
972 |
** |
|
ef449a1…
|
drh
|
973 |
** Query parameters: |
|
ef449a1…
|
drh
|
974 |
** |
|
9eadac2…
|
drh
|
975 |
** name=[CKIN/]NAME The optional CKIN component of the name= parameter |
|
9eadac2…
|
drh
|
976 |
** identifies the check-in from which the archive is |
|
9eadac2…
|
drh
|
977 |
** constructed. If CKIN is omitted and there is no |
|
3a6dd83…
|
drh
|
978 |
** r= query parameter, then use the name of the main |
|
3a6dd83…
|
drh
|
979 |
** branch (usually "trunk"). NAME is the |
|
9eadac2…
|
drh
|
980 |
** name of the download file. The top-level directory |
|
9eadac2…
|
drh
|
981 |
** in the generated archive is called by NAME with the |
|
9eadac2…
|
drh
|
982 |
** file extension removed. |
|
9eadac2…
|
drh
|
983 |
** |
|
9eadac2…
|
drh
|
984 |
** r=TAG TAG identifies the check-in that is turned into an |
|
3a6dd83…
|
drh
|
985 |
** SQL or ZIP archive. The default value is the name |
|
3a6dd83…
|
drh
|
986 |
** of the main branch (usually "trunk"). |
|
9eadac2…
|
drh
|
987 |
** If r= is omitted and if the name= query parameter |
|
9eadac2…
|
drh
|
988 |
** contains one "/" character then the of part the |
|
9eadac2…
|
drh
|
989 |
** name= value before the / becomes the TAG and the |
|
9eadac2…
|
drh
|
990 |
** part of the name= value after the / is the download |
|
9eadac2…
|
drh
|
991 |
** filename. If no check-in is specified by either |
|
3a6dd83…
|
drh
|
992 |
** name= or r=, then the name of the main branch |
|
3a6dd83…
|
drh
|
993 |
** (usually "trunk") is used. |
|
9eadac2…
|
drh
|
994 |
** |
|
33d3bf3…
|
km
|
995 |
** in=PATTERN Only include files that match the comma-separated |
|
ef449a1…
|
drh
|
996 |
** list of GLOB patterns in PATTERN, as with ex= |
|
ef449a1…
|
drh
|
997 |
** |
|
ef449a1…
|
drh
|
998 |
** ex=PATTERN Omit any file that match PATTERN. PATTERN is a |
|
ef449a1…
|
drh
|
999 |
** comma-separated list of GLOB patterns, where each |
|
ef449a1…
|
drh
|
1000 |
** pattern can optionally be quoted using ".." or '..'. |
|
ef449a1…
|
drh
|
1001 |
** Any file matching both ex= and in= is excluded. |
|
4d198d0…
|
drh
|
1002 |
** |
|
4d198d0…
|
drh
|
1003 |
** Robot Defenses: |
|
4d198d0…
|
drh
|
1004 |
** |
|
4d198d0…
|
drh
|
1005 |
** * If "zip" appears in the robot-restrict setting, then robots are |
|
4d198d0…
|
drh
|
1006 |
** not allowed to access this page. Suspected robots will be |
|
4d198d0…
|
drh
|
1007 |
** presented with a captcha. |
|
4d198d0…
|
drh
|
1008 |
** |
|
4d198d0…
|
drh
|
1009 |
** * If "zipX" appears in the robot-restrict setting, then robots are |
|
4d198d0…
|
drh
|
1010 |
** restricted in the same way as with "zip", but with exceptions. |
|
4d198d0…
|
drh
|
1011 |
** If the check-in for which an archive is requested is a leaf check-in |
|
4d198d0…
|
drh
|
1012 |
** and if the robot-zip-leaf setting is true, then the request is |
|
4d198d0…
|
drh
|
1013 |
** allowed. Or if the check-in has a tag that matches any of the |
|
4d198d0…
|
drh
|
1014 |
** GLOB patterns on the list in the robot-zip-tag setting, then the |
|
4d198d0…
|
drh
|
1015 |
** request is allowed. Otherwise, the usual robot defenses are |
|
4d198d0…
|
drh
|
1016 |
** activated. |
|
7c2577b…
|
drh
|
1017 |
*/ |
|
7c2577b…
|
drh
|
1018 |
void baseline_zip_page(void){ |
|
7c2577b…
|
drh
|
1019 |
int rid; |
|
ece33ee…
|
drh
|
1020 |
const char *z; |
|
953cfbd…
|
mistachkin
|
1021 |
char *zName, *zRid, *zKey; |
|
7c2577b…
|
drh
|
1022 |
int nName, nRid; |
|
ef449a1…
|
drh
|
1023 |
const char *zInclude; /* The in= query parameter */ |
|
ef449a1…
|
drh
|
1024 |
const char *zExclude; /* The ex= query parameter */ |
|
ef449a1…
|
drh
|
1025 |
Blob cacheKey; /* The key to cache */ |
|
ef449a1…
|
drh
|
1026 |
Glob *pInclude = 0; /* The compiled in= glob pattern */ |
|
ef449a1…
|
drh
|
1027 |
Glob *pExclude = 0; /* The compiled ex= glob pattern */ |
|
953cfbd…
|
mistachkin
|
1028 |
Blob zip; /* ZIP archive accumulated here */ |
|
768e192…
|
drh
|
1029 |
int eType = ARCHIVE_ZIP; /* Type of archive to generate */ |
|
768e192…
|
drh
|
1030 |
char *zType; /* Human-readable archive type */ |
|
7c2577b…
|
drh
|
1031 |
|
|
7c2577b…
|
drh
|
1032 |
login_check_credentials(); |
|
653dd40…
|
drh
|
1033 |
if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; } |
|
16b3309…
|
drh
|
1034 |
if( robot_restrict("zip") ) return; |
|
768e192…
|
drh
|
1035 |
if( fossil_strcmp(g.zPath, "sqlar")==0 ){ |
|
768e192…
|
drh
|
1036 |
eType = ARCHIVE_SQLAR; |
|
768e192…
|
drh
|
1037 |
zType = "SQL"; |
|
768e192…
|
drh
|
1038 |
}else{ |
|
768e192…
|
drh
|
1039 |
eType = ARCHIVE_ZIP; |
|
768e192…
|
drh
|
1040 |
zType = "ZIP"; |
|
768e192…
|
drh
|
1041 |
} |
|
9413395…
|
drh
|
1042 |
fossil_nice_default(); |
|
bb9233a…
|
drh
|
1043 |
zName = fossil_strdup(PD("name","")); |
|
ece33ee…
|
drh
|
1044 |
z = P("r"); |
|
ece33ee…
|
drh
|
1045 |
if( z==0 ) z = P("uuid"); |
|
3e94c7e…
|
drh
|
1046 |
if( z==0 ) z = tar_uuid_from_name(&zName); |
|
ca0d66b…
|
danield
|
1047 |
if( z==0 ) z = fossil_strdup(db_main_branch()); |
|
3e94c7e…
|
drh
|
1048 |
nName = strlen(zName); |
|
da23bec…
|
andygoth
|
1049 |
g.zOpenRevision = zRid = fossil_strdup(z); |
|
7c2577b…
|
drh
|
1050 |
nRid = strlen(zRid); |
|
ef449a1…
|
drh
|
1051 |
zInclude = P("in"); |
|
ef449a1…
|
drh
|
1052 |
if( zInclude ) pInclude = glob_create(zInclude); |
|
ef449a1…
|
drh
|
1053 |
zExclude = P("ex"); |
|
ef449a1…
|
drh
|
1054 |
if( zExclude ) pExclude = glob_create(zExclude); |
|
44339d5…
|
drh
|
1055 |
if( zInclude==0 && zExclude==0 ){ |
|
44339d5…
|
drh
|
1056 |
etag_check_for_invariant_name(z); |
|
44339d5…
|
drh
|
1057 |
} |
|
275da70…
|
danield
|
1058 |
if( eType==ARCHIVE_ZIP |
|
768e192…
|
drh
|
1059 |
&& nName>4 |
|
768e192…
|
drh
|
1060 |
&& fossil_strcmp(&zName[nName-4], ".zip")==0 |
|
768e192…
|
drh
|
1061 |
){ |
|
e0199bf…
|
stephan
|
1062 |
/* Special case: Remove the ".zip" suffix. */ |
|
e0199bf…
|
stephan
|
1063 |
nName -= 4; |
|
e0199bf…
|
stephan
|
1064 |
zName[nName] = 0; |
|
275da70…
|
danield
|
1065 |
}else if( eType==ARCHIVE_SQLAR |
|
768e192…
|
drh
|
1066 |
&& nName>6 |
|
768e192…
|
drh
|
1067 |
&& fossil_strcmp(&zName[nName-6], ".sqlar")==0 |
|
768e192…
|
drh
|
1068 |
){ |
|
768e192…
|
drh
|
1069 |
/* Special case: Remove the ".sqlar" suffix. */ |
|
768e192…
|
drh
|
1070 |
nName -= 6; |
|
768e192…
|
drh
|
1071 |
zName[nName] = 0; |
|
e0199bf…
|
stephan
|
1072 |
}else{ |
|
768e192…
|
drh
|
1073 |
/* If the file suffix is not ".zip" or ".sqlar" then just remove the |
|
e0199bf…
|
stephan
|
1074 |
** suffix up to and including the last "." */ |
|
e0199bf…
|
stephan
|
1075 |
for(nName=strlen(zName)-1; nName>5; nName--){ |
|
e0199bf…
|
stephan
|
1076 |
if( zName[nName]=='.' ){ |
|
e0199bf…
|
stephan
|
1077 |
zName[nName] = 0; |
|
e0199bf…
|
stephan
|
1078 |
break; |
|
e0199bf…
|
stephan
|
1079 |
} |
|
7c2577b…
|
drh
|
1080 |
} |
|
7c2577b…
|
drh
|
1081 |
} |
|
a10fc44…
|
drh
|
1082 |
rid = symbolic_name_to_rid(nRid?zRid:zName, "ci"); |
|
a10fc44…
|
drh
|
1083 |
if( rid<=0 ){ |
|
a10fc44…
|
drh
|
1084 |
cgi_set_status(404, "Not Found"); |
|
7c2577b…
|
drh
|
1085 |
@ Not found |
|
7c2577b…
|
drh
|
1086 |
return; |
|
7c2577b…
|
drh
|
1087 |
} |
|
4d198d0…
|
drh
|
1088 |
if( robot_restrict_zip(rid) ) return; |
|
ef449a1…
|
drh
|
1089 |
if( nRid==0 && nName>10 ) zName[10] = 0; |
|
ef449a1…
|
drh
|
1090 |
|
|
ef449a1…
|
drh
|
1091 |
/* Compute a unique key for the cache entry based on query parameters */ |
|
ef449a1…
|
drh
|
1092 |
blob_init(&cacheKey, 0, 0); |
|
768e192…
|
drh
|
1093 |
blob_appendf(&cacheKey, "/%s/%z", g.zPath, rid_to_uuid(rid)); |
|
953cfbd…
|
mistachkin
|
1094 |
blob_appendf(&cacheKey, "/%q", zName); |
|
ef449a1…
|
drh
|
1095 |
if( zInclude ) blob_appendf(&cacheKey, ",in=%Q", zInclude); |
|
ef449a1…
|
drh
|
1096 |
if( zExclude ) blob_appendf(&cacheKey, ",ex=%Q", zExclude); |
|
ef449a1…
|
drh
|
1097 |
zKey = blob_str(&cacheKey); |
|
7383450…
|
drh
|
1098 |
etag_check(ETAG_HASH, zKey); |
|
ef449a1…
|
drh
|
1099 |
|
|
112c713…
|
drh
|
1100 |
style_set_current_feature("zip"); |
|
953cfbd…
|
mistachkin
|
1101 |
if( P("debug")!=0 ){ |
|
768e192…
|
drh
|
1102 |
style_header("%s Archive Generator Debug Screen", zType); |
|
f5482a0…
|
wyoung
|
1103 |
@ zName = "%h(zName)"<br> |
|
f5482a0…
|
wyoung
|
1104 |
@ rid = %d(rid)<br> |
|
953cfbd…
|
mistachkin
|
1105 |
if( zInclude ){ |
|
f5482a0…
|
wyoung
|
1106 |
@ zInclude = "%h(zInclude)"<br> |
|
953cfbd…
|
mistachkin
|
1107 |
} |
|
953cfbd…
|
mistachkin
|
1108 |
if( zExclude ){ |
|
f5482a0…
|
wyoung
|
1109 |
@ zExclude = "%h(zExclude)"<br> |
|
953cfbd…
|
mistachkin
|
1110 |
} |
|
953cfbd…
|
mistachkin
|
1111 |
@ zKey = "%h(zKey)" |
|
112c713…
|
drh
|
1112 |
style_finish_page(); |
|
953cfbd…
|
mistachkin
|
1113 |
return; |
|
953cfbd…
|
mistachkin
|
1114 |
} |
|
953cfbd…
|
mistachkin
|
1115 |
if( referred_from_login() ){ |
|
768e192…
|
drh
|
1116 |
style_header("%s Archive Download", zType); |
|
768e192…
|
drh
|
1117 |
@ <form action='%R/%s(g.zPath)/%h(zName).%s(g.zPath)'> |
|
953cfbd…
|
mistachkin
|
1118 |
cgi_query_parameters_to_hidden(); |
|
768e192…
|
drh
|
1119 |
@ <p>%s(zType) Archive named <b>%h(zName).%s(g.zPath)</b> |
|
768e192…
|
drh
|
1120 |
@ holding the content of check-in <b>%h(zRid)</b>: |
|
f5482a0…
|
wyoung
|
1121 |
@ <input type="submit" value="Download"> |
|
953cfbd…
|
mistachkin
|
1122 |
@ </form> |
|
112c713…
|
drh
|
1123 |
style_finish_page(); |
|
953cfbd…
|
mistachkin
|
1124 |
return; |
|
953cfbd…
|
mistachkin
|
1125 |
} |
|
57f1e87…
|
drh
|
1126 |
cgi_check_for_malice(); |
|
450b62f…
|
drh
|
1127 |
blob_zero(&zip); |
|
450b62f…
|
drh
|
1128 |
if( cache_read(&zip, zKey)==0 ){ |
|
c88880f…
|
drh
|
1129 |
zip_of_checkin(eType, rid, &zip, zName, pInclude, pExclude, 0); |
|
450b62f…
|
drh
|
1130 |
cache_write(&zip, zKey); |
|
450b62f…
|
drh
|
1131 |
} |
|
ef449a1…
|
drh
|
1132 |
glob_free(pInclude); |
|
ef449a1…
|
drh
|
1133 |
glob_free(pExclude); |
|
953cfbd…
|
mistachkin
|
1134 |
fossil_free(zName); |
|
953cfbd…
|
mistachkin
|
1135 |
fossil_free(zRid); |
|
dbe16d7…
|
mistachkin
|
1136 |
g.zOpenRevision = 0; |
|
953cfbd…
|
mistachkin
|
1137 |
blob_reset(&cacheKey); |
|
7c2577b…
|
drh
|
1138 |
cgi_set_content(&zip); |
|
768e192…
|
drh
|
1139 |
if( eType==ARCHIVE_ZIP ){ |
|
768e192…
|
drh
|
1140 |
cgi_set_content_type("application/zip"); |
|
768e192…
|
drh
|
1141 |
}else{ |
|
768e192…
|
drh
|
1142 |
cgi_set_content_type("application/sqlar"); |
|
768e192…
|
drh
|
1143 |
} |
|
9b780d2…
|
drh
|
1144 |
} |