Diff
Not logged in

Differences From:

File src/zip.c part of check-in [deb6f2af4f] - Change the disk number start from 1 to 0 in generated ZIP archives. Ticket 04cba6a00145ab597dabc48ad5ee688496a57650 by drh on 2009-06-20 23:05:55. [view]

To:

File src/zip.c part of check-in [90048e0b30] - Add an "extended timestamp" field to generated ZIP archives. The extended timestamp is in UTC so it does not mangle the time when the server and client are in different timezones. Ticket 28044ab5a42b75 by drh on 2009-08-15 02:17:27. [view]

@@ -49,8 +49,11 @@
 static Blob toc;     /* The table of contents */
 static int nEntry;   /* Number of files */
 static int dosTime;  /* DOS-format time */
 static int dosDate;  /* DOS-format date */
+static int unixTime; /* Seconds since 1970 */
+static int nDir;     /* Number of entries in azDir[] */
+static char **azDir; /* Directory names already added to the archive */
 
 /*
 ** Initialize a new ZIP archive.
 */
@@ -59,8 +62,9 @@
   blob_zero(&toc);
   nEntry = 0;
   dosTime = 0;
   dosDate = 0;
+  unixTime = 0;
 }
 
 /*
 ** Set the date and time values from an ISO8601 date string.
@@ -85,8 +89,35 @@
 void zip_set_timedate(double rDate){
   char *zDate = db_text(0, "SELECT datetime(%.17g)", rDate);
   zip_set_timedate_from_str(zDate);
   free(zDate);
+  unixTime = (rDate - 2440587.5)*86400.0;
+}
+
+/*
+** If the given filename includes one or more directory entries, make
+** sure the directories are already in the archive.  If they are not
+** in the archive, add them.
+*/
+void zip_add_folders(char *zName){
+  int i, c;
+  int j;
+  for(i=0; zName[i]; i++){
+    if( zName[i]=='/' ){
+      c = zName[i+1];
+      zName[i+1] = 0;
+      for(j=0; j<nDir; j++){
+        if( strcmp(zName, azDir[j])==0 ) break;
+      }
+      if( j>=nDir ){
+        nDir++;
+        azDir = realloc(azDir, nDir);
+        azDir[j] = sqlite3_mprintf("%s", zName);
+        zip_add_file(zName, 0);
+      }
+      zName[i+1] = c;
+    }
+  }
 }
 
 /*
 ** Append a single file to a growing ZIP archive.
@@ -99,108 +130,133 @@
   int nameLen;
   int skip;
   int toOut;
   int iStart;
-  int iCRC;
-  int nByte;
-  int nByteCompr;
+  int iCRC = 0;
+  int nByte = 0;
+  int nByteCompr = 0;
+  int nBlob;                 /* Size of the blob */
+  int iMethod;               /* Compression method. */
+  int iMode = 0644;          /* Access permissions */
   char *z;
   char zHdr[30];
+  char zExTime[13];
   char zBuf[100];
   char zOutBuf[100000];
 
   /* Fill in as much of the header as we know.
   */
+  nBlob = pFile ? blob_size(pFile) : 0;
+  if( nBlob>0 ){
+    iMethod = 8;
+    iMode = 0100644;
+  }else{
+    iMethod = 0;
+    iMode = 040755;
+  }
   nameLen = strlen(zName);
+  memset(zHdr, 0, sizeof(zHdr));
   put32(&zHdr[0], 0x04034b50);
-  put16(&zHdr[4], 0x0014);
+  put16(&zHdr[4], 0x000a);
   put16(&zHdr[6], 0);
-  put16(&zHdr[8], 8);
+  put16(&zHdr[8], iMethod);
   put16(&zHdr[10], dosTime);
   put16(&zHdr[12], dosDate);
   put16(&zHdr[26], nameLen);
-  put16(&zHdr[28], 0);
+  put16(&zHdr[28], 13);
+
+  put16(&zExTime[0], 0x5455);
+  put16(&zExTime[2], 9);
+  zExTime[4] = 3;
+  put32(&zExTime[5], unixTime);
+  put32(&zExTime[9], unixTime);
+
 
   /* Write the header and filename.
   */
   iStart = blob_size(&body);
   blob_append(&body, zHdr, 30);
   blob_append(&body, zName, nameLen);
-
-  /* The first two bytes that come out of the deflate compressor are
-  ** some kind of header that ZIP does not use.  So skip the first two
-  ** output bytes.
-  */
-  skip = 2;
-
-  /* Write the compressed file.  Compute the CRC as we progress.
-  */
-  stream.zalloc = (alloc_func)0;
-  stream.zfree = (free_func)0;
-  stream.opaque = 0;
-  stream.avail_in = blob_size(pFile);
-  stream.next_in = (unsigned char*)blob_buffer(pFile);
-  stream.avail_out = sizeof(zOutBuf);
-  stream.next_out = (unsigned char*)zOutBuf;
-  deflateInit(&stream, 9);
-  iCRC = crc32(0, stream.next_in, stream.avail_in);
-  while( stream.avail_in>0 ){
-    deflate(&stream, 0);
-    toOut = sizeof(zOutBuf) - stream.avail_out;
-    if( toOut>skip ){
-      blob_append(&body, &zOutBuf[skip], toOut - skip);
-      skip = 0;
-    }else{
-      skip -= toOut;
-    }
+  blob_append(&body, zExTime, 13);
+
+  if( nBlob>0 ){
+    /* The first two bytes that come out of the deflate compressor are
+    ** some kind of header that ZIP does not use.  So skip the first two
+    ** output bytes.
+    */
+    skip = 2;
+
+    /* Write the compressed file.  Compute the CRC as we progress.
+    */
+    stream.zalloc = (alloc_func)0;
+    stream.zfree = (free_func)0;
+    stream.opaque = 0;
+    stream.avail_in = blob_size(pFile);
+    stream.next_in = (unsigned char*)blob_buffer(pFile);
     stream.avail_out = sizeof(zOutBuf);
     stream.next_out = (unsigned char*)zOutBuf;
-  }
-  do{
-    stream.avail_out = sizeof(zOutBuf);
-    stream.next_out = (unsigned char*)zOutBuf;
-    deflate(&stream, Z_FINISH);
-    toOut = sizeof(zOutBuf) - stream.avail_out;
-    if( toOut>skip ){
-      blob_append(&body, &zOutBuf[skip], toOut - skip);
-      skip = 0;
-    }else{
-      skip -= toOut;
+    deflateInit(&stream, 9);
+    iCRC = crc32(0, stream.next_in, stream.avail_in);
+    while( stream.avail_in>0 ){
+      deflate(&stream, 0);
+      toOut = sizeof(zOutBuf) - stream.avail_out;
+      if( toOut>skip ){
+        blob_append(&body, &zOutBuf[skip], toOut - skip);
+        skip = 0;
+      }else{
+        skip -= toOut;
+      }
+      stream.avail_out = sizeof(zOutBuf);
+      stream.next_out = (unsigned char*)zOutBuf;
     }
-  }while( stream.avail_out==0 );
-  nByte = stream.total_in;
-  nByteCompr = stream.total_out - 2;
-  deflateEnd(&stream);
-
-  /* Go back and write the header, now that we know the compressed file size.
-  */
-  z = &blob_buffer(&body)[iStart];
-  put32(&z[14], iCRC);
-  put32(&z[18], nByteCompr);
-  put32(&z[22], nByte);
+    do{
+      stream.avail_out = sizeof(zOutBuf);
+      stream.next_out = (unsigned char*)zOutBuf;
+      deflate(&stream, Z_FINISH);
+      toOut = sizeof(zOutBuf) - stream.avail_out;
+      if( toOut>skip ){
+        blob_append(&body, &zOutBuf[skip], toOut - skip);
+        skip = 0;
+      }else{
+        skip -= toOut;
+      }
+    }while( stream.avail_out==0 );
+    nByte = stream.total_in;
+    nByteCompr = stream.total_out - 2;
+    deflateEnd(&stream);
+
+    /* Go back and write the header, now that we know the compressed file size.
+    */
+    z = &blob_buffer(&body)[iStart];
+    put32(&z[14], iCRC);
+    put32(&z[18], nByteCompr);
+    put32(&z[22], nByte);
+  }
 
   /* Make an entry in the tables of contents
   */
   memset(zBuf, 0, sizeof(zBuf));
   put32(&zBuf[0], 0x02014b50);
   put16(&zBuf[4], 0x0317);
-  put16(&zBuf[6], 0x0014);
+  put16(&zBuf[6], 0x000a);
   put16(&zBuf[8], 0);
-  put16(&zBuf[10], 0x0008);
+  put16(&zBuf[10], iMethod);
   put16(&zBuf[12], dosTime);
   put16(&zBuf[14], dosDate);
   put32(&zBuf[16], iCRC);
   put32(&zBuf[20], nByteCompr);
   put32(&zBuf[24], nByte);
   put16(&zBuf[28], nameLen);
-  put16(&zBuf[30], 0);
+  put16(&zBuf[30], 9);
   put16(&zBuf[32], 0);
   put16(&zBuf[34], 0);
   put16(&zBuf[36], 0);
-  put32(&zBuf[38], ((unsigned)(0100000 | 0644))<<16);
+  put32(&zBuf[38], ((unsigned)iMode)<<16);
   put32(&zBuf[42], iStart);
   blob_append(&toc, zBuf, 46);
   blob_append(&toc, zName, nameLen);
+  put16(&zExTime[2], 5);
+  blob_append(&toc, zExTime, 9);
   nEntry++;
 }
 
 
@@ -209,8 +265,9 @@
 */
 void zip_close(Blob *pZip){
   int iTocStart;
   int iTocEnd;
+  int i;
   char zBuf[30];
 
   iTocStart = blob_size(&body);
   blob_append(&body, blob_buffer(&toc), blob_size(&toc));
@@ -229,8 +286,14 @@
   blob_reset(&toc);
   *pZip = body;
   blob_zero(&body);
   nEntry = 0;
+  for(i=0; i<nDir; i++){
+    sqlite3_free(azDir[i]);
+  }
+  free(azDir);
+  nDir = 0;
+  azDir = 0;
 }
 
 /*
 ** COMMAND: test-filezip
@@ -297,25 +360,31 @@
   }
   nPrefix = blob_size(&filename);
 
   if( manifest_parse(&m, &mfile) ){
+    char *zName;
     zip_set_timedate(m.rDate);
     blob_append(&filename, "manifest", -1);
-    zip_add_file(blob_str(&filename), &file);
+    zName = blob_str(&filename);
+    zip_add_folders(zName);
+    zip_add_file(zName, &file);
     sha1sum_blob(&file, &hash);
     blob_reset(&file);
     blob_append(&hash, "\n", 1);
     blob_resize(&filename, nPrefix);
     blob_append(&filename, "manifest.uuid", -1);
-    zip_add_file(blob_str(&filename), &hash);
+    zName = blob_str(&filename);
+    zip_add_file(zName, &hash);
     blob_reset(&hash);
     for(i=0; i<m.nFile; i++){
       int fid = uuid_to_rid(m.aFile[i].zUuid, 0);
       if( fid ){
         content_get(fid, &file);
         blob_resize(&filename, nPrefix);
         blob_append(&filename, m.aFile[i].zName, -1);
-        zip_add_file(blob_str(&filename), &file);
+        zName = blob_str(&filename);
+        zip_add_folders(zName);
+        zip_add_file(zName, &file);
         blob_reset(&file);
       }
     }
     manifest_clear(&m);