Back to index

php5  5.3.10
zip.c
Go to the documentation of this file.
00001 /*
00002   +----------------------------------------------------------------------+
00003   | ZIP archive support for Phar                                         |
00004   +----------------------------------------------------------------------+
00005   | Copyright (c) 2007-2012 The PHP Group                                |
00006   +----------------------------------------------------------------------+
00007   | This source file is subject to version 3.01 of the PHP license,      |
00008   | that is bundled with this package in the file LICENSE, and is        |
00009   | available through the world-wide-web at the following url:           |
00010   | http://www.php.net/license/3_01.txt.                                 |
00011   | If you did not receive a copy of the PHP license and are unable to   |
00012   | obtain it through the world-wide-web, please send a note to          |
00013   | license@php.net so we can mail you a copy immediately.               |
00014   +----------------------------------------------------------------------+
00015   | Authors: Gregory Beaver <cellog@php.net>                             |
00016   +----------------------------------------------------------------------+
00017 */
00018 
00019 #include "phar_internal.h"
00020 
00021 #define PHAR_GET_16(var) ((php_uint16)((((php_uint16)var[0]) & 0xff) | \
00022        (((php_uint16)var[1]) & 0xff) << 8))
00023 #define PHAR_GET_32(var) ((php_uint32)((((php_uint32)var[0]) & 0xff) | \
00024        (((php_uint32)var[1]) & 0xff) << 8 | \
00025        (((php_uint32)var[2]) & 0xff) << 16 | \
00026        (((php_uint32)var[3]) & 0xff) << 24))
00027 static inline void phar_write_32(char buffer[4], php_uint32 value)
00028 {
00029        buffer[3] = (unsigned char) ((value & 0xff000000) >> 24);
00030        buffer[2] = (unsigned char) ((value & 0xff0000) >> 16);
00031        buffer[1] = (unsigned char) ((value & 0xff00) >> 8);
00032        buffer[0] = (unsigned char) (value & 0xff);
00033 }
00034 static inline void phar_write_16(char buffer[2], php_uint32 value)
00035 {
00036        buffer[1] = (unsigned char) ((value & 0xff00) >> 8);
00037        buffer[0] = (unsigned char) (value & 0xff);
00038 }
00039 # define PHAR_SET_32(var, value) phar_write_32(var, (php_uint32) (value));
00040 # define PHAR_SET_16(var, value) phar_write_16(var, (php_uint16) (value));
00041 
00042 static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, php_uint16 len TSRMLS_DC) /* {{{ */
00043 {
00044        union {
00045               phar_zip_extra_field_header header;
00046               phar_zip_unix3 unix3;
00047        } h;
00048        int read;
00049 
00050        do {
00051               if (sizeof(h.header) != php_stream_read(fp, (char *) &h.header, sizeof(h.header))) {
00052                      return FAILURE;
00053               }
00054 
00055               if (h.header.tag[0] != 'n' || h.header.tag[1] != 'u') {
00056                      /* skip to next header */
00057                      php_stream_seek(fp, PHAR_GET_16(h.header.size), SEEK_CUR);
00058                      len -= PHAR_GET_16(h.header.size) + 4;
00059                      continue;
00060               }
00061 
00062               /* unix3 header found */
00063               read = php_stream_read(fp, (char *) &(h.unix3.crc32), sizeof(h.unix3) - sizeof(h.header));
00064               len -= read + 4;
00065 
00066               if (sizeof(h.unix3) - sizeof(h.header) != read) {
00067                      return FAILURE;
00068               }
00069 
00070               if (PHAR_GET_16(h.unix3.size) > sizeof(h.unix3) - 4) {
00071                      /* skip symlink filename - we may add this support in later */
00072                      php_stream_seek(fp, PHAR_GET_16(h.unix3.size) - sizeof(h.unix3.size), SEEK_CUR);
00073               }
00074 
00075               /* set permissions */
00076               entry->flags &= PHAR_ENT_COMPRESSION_MASK;
00077 
00078               if (entry->is_dir) {
00079                      entry->flags |= PHAR_GET_16(h.unix3.perms) & PHAR_ENT_PERM_MASK;
00080               } else {
00081                      entry->flags |= PHAR_GET_16(h.unix3.perms) & PHAR_ENT_PERM_MASK;
00082               }
00083 
00084        } while (len);
00085 
00086        return SUCCESS;
00087 }
00088 /* }}} */
00089 
00090 /*
00091   extracted from libzip
00092   zip_dirent.c -- read directory entry (local or central), clean dirent
00093   Copyright (C) 1999, 2003, 2004, 2005 Dieter Baron and Thomas Klausner
00094 
00095   This function is part of libzip, a library to manipulate ZIP archives.
00096   The authors can be contacted at <nih@giga.or.at>
00097 
00098   Redistribution and use in source and binary forms, with or without
00099   modification, are permitted provided that the following conditions
00100   are met:
00101   1. Redistributions of source code must retain the above copyright
00102      notice, this list of conditions and the following disclaimer.
00103   2. Redistributions in binary form must reproduce the above copyright
00104      notice, this list of conditions and the following disclaimer in
00105      the documentation and/or other materials provided with the
00106      distribution.
00107   3. The names of the authors may not be used to endorse or promote
00108      products derived from this software without specific prior
00109      written permission.
00110 
00111   THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
00112   OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00113   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00114   ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
00115   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00116   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00117   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00118   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
00119   IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
00120   OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
00121   IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00122  */
00123 static time_t phar_zip_d2u_time(char *cdtime, char *cddate) /* {{{ */
00124 {
00125        int dtime = PHAR_GET_16(cdtime), ddate = PHAR_GET_16(cddate);
00126        struct tm *tm, tmbuf;
00127        time_t now;
00128 
00129        now = time(NULL);
00130        tm = php_localtime_r(&now, &tmbuf);
00131 
00132        tm->tm_year = ((ddate>>9)&127) + 1980 - 1900;
00133        tm->tm_mon = ((ddate>>5)&15) - 1;
00134        tm->tm_mday = ddate&31;
00135 
00136        tm->tm_hour = (dtime>>11)&31;
00137        tm->tm_min = (dtime>>5)&63;
00138        tm->tm_sec = (dtime<<1)&62;
00139 
00140        return mktime(tm);
00141 }
00142 /* }}} */
00143 
00144 static void phar_zip_u2d_time(time_t time, char *dtime, char *ddate) /* {{{ */
00145 {
00146        php_uint16 ctime, cdate;
00147        struct tm *tm, tmbuf;
00148 
00149        tm = php_localtime_r(&time, &tmbuf);
00150        cdate = ((tm->tm_year+1900-1980)<<9) + ((tm->tm_mon+1)<<5) + tm->tm_mday;
00151        ctime = ((tm->tm_hour)<<11) + ((tm->tm_min)<<5) + ((tm->tm_sec)>>1);
00152        PHAR_SET_16(dtime, ctime);
00153        PHAR_SET_16(ddate, cdate);
00154 }
00155 /* }}} */
00156 
00166 int phar_parse_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
00167 {
00168        phar_zip_dir_end locator;
00169        char buf[sizeof(locator) + 65536];
00170        long size;
00171        php_uint16 i;
00172        phar_archive_data *mydata = NULL;
00173        phar_entry_info entry = {0};
00174        char *p = buf, *ext, *actual_alias = NULL;
00175        char *metadata = NULL;
00176 
00177        size = php_stream_tell(fp);
00178 
00179        if (size > sizeof(locator) + 65536) {
00180               /* seek to max comment length + end of central directory record */
00181               size = sizeof(locator) + 65536;
00182               if (FAILURE == php_stream_seek(fp, -size, SEEK_END)) {
00183                      php_stream_close(fp);
00184                      if (error) {
00185                             spprintf(error, 4096, "phar error: unable to search for end of central directory in zip-based phar \"%s\"", fname);
00186                      }
00187                      return FAILURE;
00188               }
00189        } else {
00190               php_stream_seek(fp, 0, SEEK_SET);
00191        }
00192 
00193        if (!php_stream_read(fp, buf, size)) {
00194               php_stream_close(fp);
00195               if (error) {
00196                      spprintf(error, 4096, "phar error: unable to read in data to search for end of central directory in zip-based phar \"%s\"", fname);
00197               }
00198               return FAILURE;
00199        }
00200 
00201        while ((p=(char *) memchr(p + 1, 'P', (size_t) (size - (p + 1 - buf)))) != NULL) {
00202               if (!memcmp(p + 1, "K\5\6", 3)) {
00203                      memcpy((void *)&locator, (void *) p, sizeof(locator));
00204                      if (PHAR_GET_16(locator.centraldisk) != 0 || PHAR_GET_16(locator.disknumber) != 0) {
00205                             /* split archives not handled */
00206                             php_stream_close(fp);
00207                             if (error) {
00208                                    spprintf(error, 4096, "phar error: split archives spanning multiple zips cannot be processed in zip-based phar \"%s\"", fname);
00209                             }
00210                             return FAILURE;
00211                      }
00212 
00213                      if (PHAR_GET_16(locator.counthere) != PHAR_GET_16(locator.count)) {
00214                             if (error) {
00215                                    spprintf(error, 4096, "phar error: corrupt zip archive, conflicting file count in end of central directory record in zip-based phar \"%s\"", fname);
00216                             }
00217                             php_stream_close(fp);
00218                             return FAILURE;
00219                      }
00220 
00221                      mydata = pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist));
00222                      mydata->is_persistent = PHAR_G(persist);
00223 
00224                      /* read in archive comment, if any */
00225                      if (PHAR_GET_16(locator.comment_len)) {
00226 
00227                             metadata = p + sizeof(locator);
00228 
00229                             if (PHAR_GET_16(locator.comment_len) != size - (metadata - buf)) {
00230                                    if (error) {
00231                                           spprintf(error, 4096, "phar error: corrupt zip archive, zip file comment truncated in zip-based phar \"%s\"", fname);
00232                                    }
00233                                    php_stream_close(fp);
00234                                    pefree(mydata, mydata->is_persistent);
00235                                    return FAILURE;
00236                             }
00237 
00238                             mydata->metadata_len = PHAR_GET_16(locator.comment_len);
00239 
00240                             if (phar_parse_metadata(&metadata, &mydata->metadata, PHAR_GET_16(locator.comment_len) TSRMLS_CC) == FAILURE) {
00241                                    mydata->metadata_len = 0;
00242                                    /* if not valid serialized data, it is a regular string */
00243 
00244                                    if (entry.is_persistent) {
00245                                           ALLOC_PERMANENT_ZVAL(mydata->metadata);
00246                                    } else {
00247                                           ALLOC_ZVAL(mydata->metadata);
00248                                    }
00249 
00250                                    INIT_ZVAL(*mydata->metadata);
00251                                    metadata = pestrndup(metadata, PHAR_GET_16(locator.comment_len), mydata->is_persistent);
00252                                    ZVAL_STRINGL(mydata->metadata, metadata, PHAR_GET_16(locator.comment_len), 0);
00253                             }
00254                      } else {
00255                             mydata->metadata = NULL;
00256                      }
00257 
00258                      goto foundit;
00259               }
00260        }
00261 
00262        php_stream_close(fp);
00263 
00264        if (error) {
00265               spprintf(error, 4096, "phar error: end of central directory not found in zip-based phar \"%s\"", fname);
00266        }
00267 
00268        return FAILURE;
00269 foundit:
00270        mydata->fname = pestrndup(fname, fname_len, mydata->is_persistent);
00271 #ifdef PHP_WIN32
00272        phar_unixify_path_separators(mydata->fname, fname_len);
00273 #endif
00274        mydata->is_zip = 1;
00275        mydata->fname_len = fname_len;
00276        ext = strrchr(mydata->fname, '/');
00277 
00278        if (ext) {
00279               mydata->ext = memchr(ext, '.', (mydata->fname + fname_len) - ext);
00280               if (mydata->ext == ext) {
00281                      mydata->ext = memchr(ext + 1, '.', (mydata->fname + fname_len) - ext - 1);
00282               }
00283               if (mydata->ext) {
00284                      mydata->ext_len = (mydata->fname + fname_len) - mydata->ext;
00285               }
00286        }
00287 
00288        /* clean up on big-endian systems */
00289        /* seek to central directory */
00290        php_stream_seek(fp, PHAR_GET_32(locator.cdir_offset), SEEK_SET);
00291        /* read in central directory */
00292        zend_hash_init(&mydata->manifest, PHAR_GET_16(locator.count),
00293               zend_get_hash_value, destroy_phar_manifest_entry, (zend_bool)mydata->is_persistent);
00294        zend_hash_init(&mydata->mounted_dirs, 5,
00295               zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
00296        zend_hash_init(&mydata->virtual_dirs, PHAR_GET_16(locator.count) * 2,
00297               zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
00298        entry.phar = mydata;
00299        entry.is_zip = 1;
00300        entry.fp_type = PHAR_FP;
00301        entry.is_persistent = mydata->is_persistent;
00302 #define PHAR_ZIP_FAIL_FREE(errmsg, save) \
00303                      zend_hash_destroy(&mydata->manifest); \
00304                      mydata->manifest.arBuckets = 0; \
00305                      zend_hash_destroy(&mydata->mounted_dirs); \
00306                      mydata->mounted_dirs.arBuckets = 0; \
00307                      zend_hash_destroy(&mydata->virtual_dirs); \
00308                      mydata->virtual_dirs.arBuckets = 0; \
00309                      php_stream_close(fp); \
00310                      if (mydata->metadata) { \
00311                             zval_dtor(mydata->metadata); \
00312                      } \
00313                      if (mydata->signature) { \
00314                             efree(mydata->signature); \
00315                      } \
00316                      if (error) { \
00317                             spprintf(error, 4096, "phar error: %s in zip-based phar \"%s\"", errmsg, mydata->fname); \
00318                      } \
00319                      pefree(mydata->fname, mydata->is_persistent); \
00320                      if (mydata->alias) { \
00321                             pefree(mydata->alias, mydata->is_persistent); \
00322                      } \
00323                      pefree(mydata, mydata->is_persistent); \
00324                      efree(save); \
00325                      return FAILURE;
00326 #define PHAR_ZIP_FAIL(errmsg) \
00327                      zend_hash_destroy(&mydata->manifest); \
00328                      mydata->manifest.arBuckets = 0; \
00329                      zend_hash_destroy(&mydata->mounted_dirs); \
00330                      mydata->mounted_dirs.arBuckets = 0; \
00331                      zend_hash_destroy(&mydata->virtual_dirs); \
00332                      mydata->virtual_dirs.arBuckets = 0; \
00333                      php_stream_close(fp); \
00334                      if (mydata->metadata) { \
00335                             zval_dtor(mydata->metadata); \
00336                      } \
00337                      if (mydata->signature) { \
00338                             efree(mydata->signature); \
00339                      } \
00340                      if (error) { \
00341                             spprintf(error, 4096, "phar error: %s in zip-based phar \"%s\"", errmsg, mydata->fname); \
00342                      } \
00343                      pefree(mydata->fname, mydata->is_persistent); \
00344                      if (mydata->alias) { \
00345                             pefree(mydata->alias, mydata->is_persistent); \
00346                      } \
00347                      pefree(mydata, mydata->is_persistent); \
00348                      return FAILURE;
00349 
00350        /* add each central directory item to the manifest */
00351        for (i = 0; i < PHAR_GET_16(locator.count); ++i) {
00352               phar_zip_central_dir_file zipentry;
00353               off_t beforeus = php_stream_tell(fp);
00354 
00355               if (sizeof(zipentry) != php_stream_read(fp, (char *) &zipentry, sizeof(zipentry))) {
00356                      PHAR_ZIP_FAIL("unable to read central directory entry, truncated");
00357               }
00358 
00359               /* clean up for bigendian systems */
00360               if (memcmp("PK\1\2", zipentry.signature, 4)) {
00361                      /* corrupted entry */
00362                      PHAR_ZIP_FAIL("corrupted central directory entry, no magic signature");
00363               }
00364 
00365               if (entry.is_persistent) {
00366                      entry.manifest_pos = i;
00367               }
00368 
00369               entry.compressed_filesize = PHAR_GET_32(zipentry.compsize);
00370               entry.uncompressed_filesize = PHAR_GET_32(zipentry.uncompsize);
00371               entry.crc32 = PHAR_GET_32(zipentry.crc32);
00372               /* do not PHAR_GET_16 either on the next line */
00373               entry.timestamp = phar_zip_d2u_time(zipentry.timestamp, zipentry.datestamp);
00374               entry.flags = PHAR_ENT_PERM_DEF_FILE;
00375               entry.header_offset = PHAR_GET_32(zipentry.offset);
00376               entry.offset = entry.offset_abs = PHAR_GET_32(zipentry.offset) + sizeof(phar_zip_file_header) + PHAR_GET_16(zipentry.filename_len) +
00377                      PHAR_GET_16(zipentry.extra_len);
00378 
00379               if (PHAR_GET_16(zipentry.flags) & PHAR_ZIP_FLAG_ENCRYPTED) {
00380                      PHAR_ZIP_FAIL("Cannot process encrypted zip files");
00381               }
00382 
00383               if (!PHAR_GET_16(zipentry.filename_len)) {
00384                      PHAR_ZIP_FAIL("Cannot process zips created from stdin (zero-length filename)");
00385               }
00386 
00387               entry.filename_len = PHAR_GET_16(zipentry.filename_len);
00388               entry.filename = (char *) pemalloc(entry.filename_len + 1, entry.is_persistent);
00389 
00390               if (entry.filename_len != php_stream_read(fp, entry.filename, entry.filename_len)) {
00391                      pefree(entry.filename, entry.is_persistent);
00392                      PHAR_ZIP_FAIL("unable to read in filename from central directory, truncated");
00393               }
00394 
00395               entry.filename[entry.filename_len] = '\0';
00396 
00397               if (entry.filename[entry.filename_len - 1] == '/') {
00398                      entry.is_dir = 1;
00399                      entry.filename_len--;
00400                      entry.flags |= PHAR_ENT_PERM_DEF_DIR;
00401               } else {
00402                      entry.is_dir = 0;
00403               }
00404 
00405               if (entry.filename_len == sizeof(".phar/signature.bin")-1 && !strncmp(entry.filename, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) {
00406                      size_t read;
00407                      php_stream *sigfile;
00408                      off_t now;
00409                      char *sig;
00410 
00411                      now = php_stream_tell(fp);
00412                      pefree(entry.filename, entry.is_persistent);
00413                      sigfile = php_stream_fopen_tmpfile();
00414                      if (!sigfile) {
00415                             PHAR_ZIP_FAIL("couldn't open temporary file");
00416                      }
00417 
00418                      php_stream_seek(fp, 0, SEEK_SET);
00419                      /* copy file contents + local headers and zip comment, if any, to be hashed for signature */
00420                      phar_stream_copy_to_stream(fp, sigfile, entry.header_offset, NULL);
00421                      /* seek to central directory */
00422                      php_stream_seek(fp, PHAR_GET_32(locator.cdir_offset), SEEK_SET);
00423                      /* copy central directory header */
00424                      phar_stream_copy_to_stream(fp, sigfile, beforeus - PHAR_GET_32(locator.cdir_offset), NULL);
00425                      if (metadata) {
00426                             php_stream_write(sigfile, metadata, PHAR_GET_16(locator.comment_len));
00427                      }
00428                      php_stream_seek(fp, sizeof(phar_zip_file_header) + entry.header_offset + entry.filename_len + PHAR_GET_16(zipentry.extra_len), SEEK_SET);
00429                      sig = (char *) emalloc(entry.uncompressed_filesize);
00430                      read = php_stream_read(fp, sig, entry.uncompressed_filesize);
00431                      if (read != entry.uncompressed_filesize) {
00432                             php_stream_close(sigfile);
00433                             efree(sig);
00434                             PHAR_ZIP_FAIL("signature cannot be read");
00435                      }
00436                      mydata->sig_flags = PHAR_GET_32(sig);
00437                      if (FAILURE == phar_verify_signature(sigfile, php_stream_tell(sigfile), mydata->sig_flags, sig + 8, entry.uncompressed_filesize - 8, fname, &mydata->signature, &mydata->sig_len, error TSRMLS_CC)) {
00438                             efree(sig);
00439                             if (error) {
00440                                    char *save;
00441                                    php_stream_close(sigfile);
00442                                    spprintf(&save, 4096, "signature cannot be verified: %s", *error);
00443                                    efree(*error);
00444                                    PHAR_ZIP_FAIL_FREE(save, save);
00445                             } else {
00446                                    php_stream_close(sigfile);
00447                                    PHAR_ZIP_FAIL("signature cannot be verified");
00448                             }
00449                      }
00450                      php_stream_close(sigfile);
00451                      efree(sig);
00452                      /* signature checked out, let's ensure this is the last file in the phar */
00453                      if (i != PHAR_GET_16(locator.count) - 1) {
00454                             PHAR_ZIP_FAIL("entries exist after signature, invalid phar");
00455                      }
00456 
00457                      continue;
00458               }
00459 
00460               phar_add_virtual_dirs(mydata, entry.filename, entry.filename_len TSRMLS_CC);
00461 
00462               if (PHAR_GET_16(zipentry.extra_len)) {
00463                      off_t loc = php_stream_tell(fp);
00464                      if (FAILURE == phar_zip_process_extra(fp, &entry, PHAR_GET_16(zipentry.extra_len) TSRMLS_CC)) {
00465                             pefree(entry.filename, entry.is_persistent);
00466                             PHAR_ZIP_FAIL("Unable to process extra field header for file in central directory");
00467                      }
00468                      php_stream_seek(fp, loc + PHAR_GET_16(zipentry.extra_len), SEEK_SET);
00469               }
00470 
00471               switch (PHAR_GET_16(zipentry.compressed)) {
00472                      case PHAR_ZIP_COMP_NONE :
00473                             /* compression flag already set */
00474                             break;
00475                      case PHAR_ZIP_COMP_DEFLATE :
00476                             entry.flags |= PHAR_ENT_COMPRESSED_GZ;
00477                             if (!PHAR_G(has_zlib)) {
00478                                    pefree(entry.filename, entry.is_persistent);
00479                                    PHAR_ZIP_FAIL("zlib extension is required");
00480                             }
00481                             break;
00482                      case PHAR_ZIP_COMP_BZIP2 :
00483                             entry.flags |= PHAR_ENT_COMPRESSED_BZ2;
00484                             if (!PHAR_G(has_bz2)) {
00485                                    pefree(entry.filename, entry.is_persistent);
00486                                    PHAR_ZIP_FAIL("bzip2 extension is required");
00487                             }
00488                             break;
00489                      case 1 :
00490                             pefree(entry.filename, entry.is_persistent);
00491                             PHAR_ZIP_FAIL("unsupported compression method (Shrunk) used in this zip");
00492                      case 2 :
00493                      case 3 :
00494                      case 4 :
00495                      case 5 :
00496                             pefree(entry.filename, entry.is_persistent);
00497                             PHAR_ZIP_FAIL("unsupported compression method (Reduce) used in this zip");
00498                      case 6 :
00499                             pefree(entry.filename, entry.is_persistent);
00500                             PHAR_ZIP_FAIL("unsupported compression method (Implode) used in this zip");
00501                      case 7 :
00502                             pefree(entry.filename, entry.is_persistent);
00503                             PHAR_ZIP_FAIL("unsupported compression method (Tokenize) used in this zip");
00504                      case 9 :
00505                             pefree(entry.filename, entry.is_persistent);
00506                             PHAR_ZIP_FAIL("unsupported compression method (Deflate64) used in this zip");
00507                      case 10 :
00508                             pefree(entry.filename, entry.is_persistent);
00509                             PHAR_ZIP_FAIL("unsupported compression method (PKWare Implode/old IBM TERSE) used in this zip");
00510                      case 14 :
00511                             pefree(entry.filename, entry.is_persistent);
00512                             PHAR_ZIP_FAIL("unsupported compression method (LZMA) used in this zip");
00513                      case 18 :
00514                             pefree(entry.filename, entry.is_persistent);
00515                             PHAR_ZIP_FAIL("unsupported compression method (IBM TERSE) used in this zip");
00516                      case 19 :
00517                             pefree(entry.filename, entry.is_persistent);
00518                             PHAR_ZIP_FAIL("unsupported compression method (IBM LZ77) used in this zip");
00519                      case 97 :
00520                             pefree(entry.filename, entry.is_persistent);
00521                             PHAR_ZIP_FAIL("unsupported compression method (WavPack) used in this zip");
00522                      case 98 :
00523                             pefree(entry.filename, entry.is_persistent);
00524                             PHAR_ZIP_FAIL("unsupported compression method (PPMd) used in this zip");
00525                      default :
00526                             pefree(entry.filename, entry.is_persistent);
00527                             PHAR_ZIP_FAIL("unsupported compression method (unknown) used in this zip");
00528               }
00529 
00530               /* get file metadata */
00531               if (PHAR_GET_16(zipentry.comment_len)) {
00532                      if (PHAR_GET_16(zipentry.comment_len) != php_stream_read(fp, buf, PHAR_GET_16(zipentry.comment_len))) {
00533                             pefree(entry.filename, entry.is_persistent);
00534                             PHAR_ZIP_FAIL("unable to read in file comment, truncated");
00535                      }
00536 
00537                      p = buf;
00538                      entry.metadata_len = PHAR_GET_16(zipentry.comment_len);
00539 
00540                      if (phar_parse_metadata(&p, &(entry.metadata), PHAR_GET_16(zipentry.comment_len) TSRMLS_CC) == FAILURE) {
00541                             entry.metadata_len = 0;
00542                             /* if not valid serialized data, it is a regular string */
00543 
00544                             if (entry.is_persistent) {
00545                                    ALLOC_PERMANENT_ZVAL(entry.metadata);
00546                             } else {
00547                                    ALLOC_ZVAL(entry.metadata);
00548                             }
00549 
00550                             INIT_ZVAL(*entry.metadata);
00551                             ZVAL_STRINGL(entry.metadata, pestrndup(buf, PHAR_GET_16(zipentry.comment_len), entry.is_persistent), PHAR_GET_16(zipentry.comment_len), 0);
00552                      }
00553               } else {
00554                      entry.metadata = NULL;
00555               }
00556 
00557               if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
00558                      php_stream_filter *filter;
00559                      off_t saveloc;
00560                      /* verify local file header */
00561                      phar_zip_file_header local;
00562 
00563                      /* archive alias found */
00564                      saveloc = php_stream_tell(fp);
00565                      php_stream_seek(fp, PHAR_GET_32(zipentry.offset), SEEK_SET);
00566 
00567                      if (sizeof(local) != php_stream_read(fp, (char *) &local, sizeof(local))) {
00568                             pefree(entry.filename, entry.is_persistent);
00569                             PHAR_ZIP_FAIL("phar error: internal corruption of zip-based phar (cannot read local file header for alias)");
00570                      }
00571 
00572                      /* verify local header */
00573                      if (entry.filename_len != PHAR_GET_16(local.filename_len) || entry.crc32 != PHAR_GET_32(local.crc32) || entry.uncompressed_filesize != PHAR_GET_32(local.uncompsize) || entry.compressed_filesize != PHAR_GET_32(local.compsize)) {
00574                             pefree(entry.filename, entry.is_persistent);
00575                             PHAR_ZIP_FAIL("phar error: internal corruption of zip-based phar (local header of alias does not match central directory)");
00576                      }
00577 
00578                      /* construct actual offset to file start - local extra_len can be different from central extra_len */
00579                      entry.offset = entry.offset_abs =
00580                             sizeof(local) + entry.header_offset + PHAR_GET_16(local.filename_len) + PHAR_GET_16(local.extra_len);
00581 #if PHP_VERSION_ID < 50207
00582                      /* work around Bug #46147 */
00583                      fp->writepos = fp->readpos = 0;
00584 #endif
00585                      php_stream_seek(fp, entry.offset, SEEK_SET);
00586                      /* these next lines should be for php < 5.2.6 after 5.3 filters are fixed */
00587                      fp->writepos = 0;
00588                      fp->readpos = 0;
00589                      php_stream_seek(fp, entry.offset, SEEK_SET);
00590                      fp->writepos = 0;
00591                      fp->readpos = 0;
00592                      /* the above lines should be for php < 5.2.6 after 5.3 filters are fixed */
00593 
00594                      mydata->alias_len = entry.uncompressed_filesize;
00595 
00596                      if (entry.flags & PHAR_ENT_COMPRESSED_GZ) {
00597                             filter = php_stream_filter_create("zlib.inflate", NULL, php_stream_is_persistent(fp) TSRMLS_CC);
00598 
00599                             if (!filter) {
00600                                    pefree(entry.filename, entry.is_persistent);
00601                                    PHAR_ZIP_FAIL("unable to decompress alias, zlib filter creation failed");
00602                             }
00603 
00604                             php_stream_filter_append(&fp->readfilters, filter);
00605 
00606 #if PHP_MAJOR_VERSION >= 6
00607                             if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, (void **) &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) {
00608 #else
00609                             if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) {
00610 #endif
00611                                    pefree(entry.filename, entry.is_persistent);
00612 #if PHP_VERSION_ID < 50207
00613                                    PHAR_ZIP_FAIL("unable to read in alias, truncated (PHP 5.2.7 and newer has a potential fix for this problem)");
00614 #endif
00615                                    PHAR_ZIP_FAIL("unable to read in alias, truncated");
00616                             }
00617 
00618                             php_stream_filter_flush(filter, 1);
00619                             php_stream_filter_remove(filter, 1 TSRMLS_CC);
00620 
00621                      } else if (entry.flags & PHAR_ENT_COMPRESSED_BZ2) {
00622                             filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp) TSRMLS_CC);
00623 
00624                             if (!filter) {
00625                                    pefree(entry.filename, entry.is_persistent);
00626                                    PHAR_ZIP_FAIL("unable to read in alias, bzip2 filter creation failed");
00627                             }
00628 
00629                             php_stream_filter_append(&fp->readfilters, filter);
00630 
00631 #if PHP_MAJOR_VERSION >= 6
00632                             if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, (void **) &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) {
00633 #else
00634                             if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) {
00635 #endif
00636                                    pefree(entry.filename, entry.is_persistent);
00637 #if PHP_VERSION_ID < 50207
00638                                    PHAR_ZIP_FAIL("unable to read in alias, truncated (PHP 5.2.7 and newer has a potential fix for this problem)");
00639 #endif
00640                                    PHAR_ZIP_FAIL("unable to read in alias, truncated");
00641                             }
00642 
00643                             php_stream_filter_flush(filter, 1);
00644                             php_stream_filter_remove(filter, 1 TSRMLS_CC);
00645                      } else {
00646 #if PHP_MAJOR_VERSION >= 6
00647                             if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, (void **) &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) {
00648 #else
00649                             if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) {
00650 #endif
00651                                    pefree(entry.filename, entry.is_persistent);
00652                                    PHAR_ZIP_FAIL("unable to read in alias, truncated");
00653                             }
00654                      }
00655 
00656                      /* return to central directory parsing */
00657                      php_stream_seek(fp, saveloc, SEEK_SET);
00658               }
00659 
00660               phar_set_inode(&entry TSRMLS_CC);
00661               zend_hash_add(&mydata->manifest, entry.filename, entry.filename_len, (void *)&entry,sizeof(phar_entry_info), NULL);
00662        }
00663 
00664        mydata->fp = fp;
00665 
00666        if (zend_hash_exists(&(mydata->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
00667               mydata->is_data = 0;
00668        } else {
00669               mydata->is_data = 1;
00670        }
00671 
00672        zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
00673 
00674        if (actual_alias) {
00675               phar_archive_data **fd_ptr;
00676 
00677               if (!phar_validate_alias(actual_alias, mydata->alias_len)) {
00678                      if (error) {
00679                             spprintf(error, 4096, "phar error: invalid alias \"%s\" in zip-based phar \"%s\"", actual_alias, fname);
00680                      }
00681                      efree(actual_alias);
00682                      zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len);
00683                      return FAILURE;
00684               }
00685 
00686               mydata->is_temporary_alias = 0;
00687 
00688               if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), actual_alias, mydata->alias_len, (void **)&fd_ptr)) {
00689                      if (SUCCESS != phar_free_alias(*fd_ptr, actual_alias, mydata->alias_len TSRMLS_CC)) {
00690                             if (error) {
00691                                    spprintf(error, 4096, "phar error: Unable to add zip-based phar \"%s\" with implicit alias, alias is already in use", fname);
00692                             }
00693                             efree(actual_alias);
00694                             zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len);
00695                             return FAILURE;
00696                      }
00697               }
00698 
00699               mydata->alias = entry.is_persistent ? pestrndup(actual_alias, mydata->alias_len, 1) : actual_alias;
00700 
00701               if (entry.is_persistent) {
00702                      efree(actual_alias);
00703               }
00704 
00705               zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, mydata->alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
00706        } else {
00707               phar_archive_data **fd_ptr;
00708 
00709               if (alias_len) {
00710                      if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) {
00711                             if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) {
00712                                    if (error) {
00713                                           spprintf(error, 4096, "phar error: Unable to add zip-based phar \"%s\" with explicit alias, alias is already in use", fname);
00714                                    }
00715                                    zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len);
00716                                    return FAILURE;
00717                             }
00718                      }
00719 
00720                      zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, mydata->alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL);
00721                      mydata->alias = pestrndup(alias, alias_len, mydata->is_persistent);
00722                      mydata->alias_len = alias_len;
00723               } else {
00724                      mydata->alias = pestrndup(mydata->fname, fname_len, mydata->is_persistent);
00725                      mydata->alias_len = fname_len;
00726               }
00727 
00728               mydata->is_temporary_alias = 1;
00729        }
00730 
00731        if (pphar) {
00732               *pphar = mydata;
00733        }
00734 
00735        return SUCCESS;
00736 }
00737 /* }}} */
00738 
00742 int phar_open_or_create_zip(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */
00743 {
00744        phar_archive_data *phar;
00745        int ret = phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, &phar, error TSRMLS_CC);
00746 
00747        if (FAILURE == ret) {
00748               return FAILURE;
00749        }
00750 
00751        if (pphar) {
00752               *pphar = phar;
00753        }
00754 
00755        phar->is_data = is_data;
00756 
00757        if (phar->is_zip) {
00758               return ret;
00759        }
00760 
00761        if (phar->is_brandnew) {
00762               phar->internal_file_start = 0;
00763               phar->is_zip = 1;
00764               phar->is_tar = 0;
00765               return SUCCESS;
00766        }
00767 
00768        /* we've reached here - the phar exists and is a regular phar */
00769        if (error) {
00770               spprintf(error, 4096, "phar zip error: phar \"%s\" already exists as a regular phar and must be deleted from disk prior to creating as a zip-based phar", fname);
00771        }
00772 
00773        return FAILURE;
00774 }
00775 /* }}} */
00776 
00777 struct _phar_zip_pass {
00778        php_stream *filefp;
00779        php_stream *centralfp;
00780        php_stream *old;
00781        int free_fp;
00782        int free_ufp;
00783        char **error;
00784 };
00785 /* perform final modification of zip contents for each file in the manifest before saving */
00786 static int phar_zip_changed_apply(void *data, void *arg TSRMLS_DC) /* {{{ */
00787 {
00788        phar_entry_info *entry;
00789        phar_zip_file_header local;
00790        phar_zip_unix3 perms;
00791        phar_zip_central_dir_file central;
00792        struct _phar_zip_pass *p;
00793        php_uint32 newcrc32;
00794        off_t offset;
00795        int not_really_modified = 0;
00796        entry = (phar_entry_info *)data;
00797        p = (struct _phar_zip_pass*) arg;
00798 
00799        if (entry->is_mounted) {
00800               return ZEND_HASH_APPLY_KEEP;
00801        }
00802 
00803        if (entry->is_deleted) {
00804               if (entry->fp_refcount <= 0) {
00805                      return ZEND_HASH_APPLY_REMOVE;
00806               } else {
00807                      /* we can't delete this in-memory until it is closed */
00808                      return ZEND_HASH_APPLY_KEEP;
00809               }
00810        }
00811 
00812        phar_add_virtual_dirs(entry->phar, entry->filename, entry->filename_len TSRMLS_CC);
00813        memset(&local, 0, sizeof(local));
00814        memset(&central, 0, sizeof(central));
00815        memset(&perms, 0, sizeof(perms));
00816        strncpy(local.signature, "PK\3\4", 4);
00817        strncpy(central.signature, "PK\1\2", 4);
00818        PHAR_SET_16(central.extra_len, sizeof(perms));
00819        PHAR_SET_16(local.extra_len, sizeof(perms));
00820        perms.tag[0] = 'n';
00821        perms.tag[1] = 'u';
00822        PHAR_SET_16(perms.size, sizeof(perms) - 4);
00823        PHAR_SET_16(perms.perms, entry->flags & PHAR_ENT_PERM_MASK);
00824        {
00825               php_uint32 crc = (php_uint32) ~0;
00826               CRC32(crc, perms.perms[0]);
00827               CRC32(crc, perms.perms[1]);
00828               PHAR_SET_32(perms.crc32, ~crc);
00829        }
00830 
00831        if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
00832               PHAR_SET_16(central.compressed, PHAR_ZIP_COMP_DEFLATE);
00833               PHAR_SET_16(local.compressed, PHAR_ZIP_COMP_DEFLATE);
00834        }
00835 
00836        if (entry->flags & PHAR_ENT_COMPRESSED_BZ2) {
00837               PHAR_SET_16(central.compressed, PHAR_ZIP_COMP_BZIP2);
00838               PHAR_SET_16(local.compressed, PHAR_ZIP_COMP_BZIP2);
00839        }
00840 
00841        /* do not use PHAR_GET_16 on either field of the next line */
00842        phar_zip_u2d_time(entry->timestamp, local.timestamp, local.datestamp);
00843        memcpy(central.timestamp, local.timestamp, sizeof(local.timestamp));
00844        memcpy(central.datestamp, local.datestamp, sizeof(local.datestamp));
00845        PHAR_SET_16(central.filename_len, entry->filename_len + (entry->is_dir ? 1 : 0));
00846        PHAR_SET_16(local.filename_len, entry->filename_len + (entry->is_dir ? 1 : 0));
00847        PHAR_SET_32(central.offset, php_stream_tell(p->filefp));
00848 
00849        /* do extra field for perms later */
00850        if (entry->is_modified) {
00851               php_uint32 loc;
00852               php_stream_filter *filter;
00853               php_stream *efp;
00854 
00855               if (entry->is_dir) {
00856                      entry->is_modified = 0;
00857                      if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) {
00858                             php_stream_close(entry->fp);
00859                             entry->fp = NULL;
00860                             entry->fp_type = PHAR_FP;
00861                      }
00862                      goto continue_dir;
00863               }
00864 
00865               if (FAILURE == phar_open_entry_fp(entry, p->error, 0 TSRMLS_CC)) {
00866                      spprintf(p->error, 0, "unable to open file contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
00867                      return ZEND_HASH_APPLY_STOP;
00868               }
00869 
00870               /* we can be modified and already be compressed, such as when chmod() is executed */
00871               if (entry->flags & PHAR_ENT_COMPRESSION_MASK && (entry->old_flags == entry->flags || !entry->old_flags)) {
00872                      not_really_modified = 1;
00873                      goto is_compressed;
00874               }
00875 
00876               if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
00877                      spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
00878                      return ZEND_HASH_APPLY_STOP;
00879               }
00880 
00881               efp = phar_get_efp(entry, 0 TSRMLS_CC);
00882               newcrc32 = ~0;
00883 
00884               for (loc = 0;loc < entry->uncompressed_filesize; ++loc) {
00885                      CRC32(newcrc32, php_stream_getc(efp));
00886               }
00887 
00888               entry->crc32 = ~newcrc32;
00889               PHAR_SET_32(central.uncompsize, entry->uncompressed_filesize);
00890               PHAR_SET_32(local.uncompsize, entry->uncompressed_filesize);
00891 
00892               if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
00893                      /* not compressed */
00894                      entry->compressed_filesize = entry->uncompressed_filesize;
00895                      PHAR_SET_32(central.compsize, entry->uncompressed_filesize);
00896                      PHAR_SET_32(local.compsize, entry->uncompressed_filesize);
00897                      goto not_compressed;
00898               }
00899 
00900               filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0 TSRMLS_CC);
00901 
00902               if (!filter) {
00903                      if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
00904                             spprintf(p->error, 0, "unable to gzip compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
00905                      } else {
00906                             spprintf(p->error, 0, "unable to bzip2 compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
00907                      }
00908                      return ZEND_HASH_APPLY_STOP;
00909               }
00910 
00911               /* create new file that holds the compressed version */
00912               /* work around inability to specify freedom in write and strictness
00913               in read count */
00914               entry->cfp = php_stream_fopen_tmpfile();
00915 
00916               if (!entry->cfp) {
00917                      spprintf(p->error, 0, "unable to create temporary file for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
00918                      return ZEND_HASH_APPLY_STOP;
00919               }
00920 
00921               php_stream_flush(efp);
00922 
00923               if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
00924                      spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
00925                      return ZEND_HASH_APPLY_STOP;
00926               }
00927 
00928               php_stream_filter_append((&entry->cfp->writefilters), filter);
00929 
00930               if (SUCCESS != phar_stream_copy_to_stream(efp, entry->cfp, entry->uncompressed_filesize, NULL)) {
00931                      spprintf(p->error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, entry->phar->fname);
00932                      return ZEND_HASH_APPLY_STOP;
00933               }
00934 
00935               php_stream_filter_flush(filter, 1);
00936               php_stream_flush(entry->cfp);
00937               php_stream_filter_remove(filter, 1 TSRMLS_CC);
00938               php_stream_seek(entry->cfp, 0, SEEK_END);
00939               entry->compressed_filesize = (php_uint32) php_stream_tell(entry->cfp);
00940               PHAR_SET_32(central.compsize, entry->compressed_filesize);
00941               PHAR_SET_32(local.compsize, entry->compressed_filesize);
00942               /* generate crc on compressed file */
00943               php_stream_rewind(entry->cfp);
00944               entry->old_flags = entry->flags;
00945               entry->is_modified = 1;
00946        } else {
00947 is_compressed:
00948               PHAR_SET_32(central.uncompsize, entry->uncompressed_filesize);
00949               PHAR_SET_32(local.uncompsize, entry->uncompressed_filesize);
00950               PHAR_SET_32(central.compsize, entry->compressed_filesize);
00951               PHAR_SET_32(local.compsize, entry->compressed_filesize);
00952 
00953               if (-1 == php_stream_seek(p->old, entry->offset_abs, SEEK_SET)) {
00954                      spprintf(p->error, 0, "unable to seek to start of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
00955                      return ZEND_HASH_APPLY_STOP;
00956               }
00957        }
00958 not_compressed:
00959        PHAR_SET_32(central.crc32, entry->crc32);
00960        PHAR_SET_32(local.crc32, entry->crc32);
00961 continue_dir:
00962        /* set file metadata */
00963        if (entry->metadata) {
00964               php_serialize_data_t metadata_hash;
00965 
00966               if (entry->metadata_str.c) {
00967                      smart_str_free(&entry->metadata_str);
00968               }
00969               entry->metadata_str.c = 0;
00970               entry->metadata_str.len = 0;
00971               PHP_VAR_SERIALIZE_INIT(metadata_hash);
00972               php_var_serialize(&entry->metadata_str, &entry->metadata, &metadata_hash TSRMLS_CC);
00973               PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
00974               PHAR_SET_16(central.comment_len, entry->metadata_str.len);
00975        }
00976 
00977        entry->header_offset = php_stream_tell(p->filefp);
00978        offset = entry->header_offset + sizeof(local) + entry->filename_len + (entry->is_dir ? 1 : 0) + sizeof(perms);
00979 
00980        if (sizeof(local) != php_stream_write(p->filefp, (char *)&local, sizeof(local))) {
00981               spprintf(p->error, 0, "unable to write local file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
00982               return ZEND_HASH_APPLY_STOP;
00983        }
00984 
00985        if (sizeof(central) != php_stream_write(p->centralfp, (char *)&central, sizeof(central))) {
00986               spprintf(p->error, 0, "unable to write central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
00987               return ZEND_HASH_APPLY_STOP;
00988        }
00989 
00990        if (entry->is_dir) {
00991               if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) {
00992                      spprintf(p->error, 0, "unable to write filename to local directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
00993                      return ZEND_HASH_APPLY_STOP;
00994               }
00995 
00996               if (1 != php_stream_write(p->filefp, "/", 1)) {
00997                      spprintf(p->error, 0, "unable to write filename to local directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
00998                      return ZEND_HASH_APPLY_STOP;
00999               }
01000 
01001               if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) {
01002                      spprintf(p->error, 0, "unable to write filename to central directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
01003                      return ZEND_HASH_APPLY_STOP;
01004               }
01005 
01006               if (1 != php_stream_write(p->centralfp, "/", 1)) {
01007                      spprintf(p->error, 0, "unable to write filename to central directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
01008                      return ZEND_HASH_APPLY_STOP;
01009               }
01010        } else {
01011               if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) {
01012                      spprintf(p->error, 0, "unable to write filename to local directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
01013                      return ZEND_HASH_APPLY_STOP;
01014               }
01015 
01016               if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) {
01017                      spprintf(p->error, 0, "unable to write filename to central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
01018                      return ZEND_HASH_APPLY_STOP;
01019               }
01020        }
01021 
01022        if (sizeof(perms) != php_stream_write(p->filefp, (char *)&perms, sizeof(perms))) {
01023               spprintf(p->error, 0, "unable to write local extra permissions file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
01024               return ZEND_HASH_APPLY_STOP;
01025        }
01026 
01027        if (sizeof(perms) != php_stream_write(p->centralfp, (char *)&perms, sizeof(perms))) {
01028               spprintf(p->error, 0, "unable to write central extra permissions file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
01029               return ZEND_HASH_APPLY_STOP;
01030        }
01031 
01032        if (!not_really_modified && entry->is_modified) {
01033               if (entry->cfp) {
01034                      if (SUCCESS != phar_stream_copy_to_stream(entry->cfp, p->filefp, entry->compressed_filesize, NULL)) {
01035                             spprintf(p->error, 0, "unable to write compressed contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
01036                             return ZEND_HASH_APPLY_STOP;
01037                      }
01038 
01039                      php_stream_close(entry->cfp);
01040                      entry->cfp = NULL;
01041               } else {
01042                      if (FAILURE == phar_open_entry_fp(entry, p->error, 0 TSRMLS_CC)) {
01043                             return ZEND_HASH_APPLY_STOP;
01044                      }
01045 
01046                      phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC);
01047 
01048                      if (SUCCESS != phar_stream_copy_to_stream(phar_get_efp(entry, 0 TSRMLS_CC), p->filefp, entry->uncompressed_filesize, NULL)) {
01049                             spprintf(p->error, 0, "unable to write contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
01050                             return ZEND_HASH_APPLY_STOP;
01051                      }
01052               }
01053 
01054               if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp && entry->fp_refcount == 0) {
01055                      php_stream_close(entry->fp);
01056               }
01057 
01058               entry->is_modified = 0;
01059        } else {
01060               entry->is_modified = 0;
01061               if (entry->fp_refcount) {
01062                      /* open file pointers refer to this fp, do not free the stream */
01063                      switch (entry->fp_type) {
01064                             case PHAR_FP:
01065                                    p->free_fp = 0;
01066                                    break;
01067                             case PHAR_UFP:
01068                                    p->free_ufp = 0;
01069                             default:
01070                                    break;
01071                      }
01072               }
01073 
01074               if (!entry->is_dir && entry->compressed_filesize && SUCCESS != phar_stream_copy_to_stream(p->old, p->filefp, entry->compressed_filesize, NULL)) {
01075                      spprintf(p->error, 0, "unable to copy contents of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
01076                      return ZEND_HASH_APPLY_STOP;
01077               }
01078        }
01079 
01080        entry->fp = NULL;
01081        entry->offset = entry->offset_abs = offset;
01082        entry->fp_type = PHAR_FP;
01083 
01084        if (entry->metadata_str.c) {
01085               if (entry->metadata_str.len != php_stream_write(p->centralfp, entry->metadata_str.c, entry->metadata_str.len)) {
01086                      spprintf(p->error, 0, "unable to write metadata as file comment for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
01087                      smart_str_free(&entry->metadata_str);
01088                      return ZEND_HASH_APPLY_STOP;
01089               }
01090 
01091               smart_str_free(&entry->metadata_str);
01092        }
01093 
01094        return ZEND_HASH_APPLY_KEEP;
01095 }
01096 /* }}} */
01097 
01098 static int phar_zip_applysignature(phar_archive_data *phar, struct _phar_zip_pass *pass,
01099                                smart_str *metadata TSRMLS_DC) /* {{{ */
01100 {
01101        /* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */
01102        if (!phar->is_data || phar->sig_flags) {
01103               int signature_length;
01104               char *signature, sigbuf[8];
01105               phar_entry_info entry = {0};
01106               php_stream *newfile;
01107               off_t tell, st;
01108 
01109               newfile = php_stream_fopen_tmpfile();
01110               st = tell = php_stream_tell(pass->filefp);
01111               /* copy the local files, central directory, and the zip comment to generate the hash */
01112               php_stream_seek(pass->filefp, 0, SEEK_SET);
01113               phar_stream_copy_to_stream(pass->filefp, newfile, tell, NULL);
01114               tell = php_stream_tell(pass->centralfp);
01115               php_stream_seek(pass->centralfp, 0, SEEK_SET);
01116               phar_stream_copy_to_stream(pass->centralfp, newfile, tell, NULL);
01117               if (metadata->c) {
01118                      php_stream_write(newfile, metadata->c, metadata->len);
01119               }
01120 
01121               if (FAILURE == phar_create_signature(phar, newfile, &signature, &signature_length, pass->error TSRMLS_CC)) {
01122                      if (pass->error) {
01123                             char *save = *(pass->error);
01124                             spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar: %s", save);
01125                             efree(save);
01126                      }
01127 
01128                      php_stream_close(newfile);
01129                      return FAILURE;
01130               }
01131 
01132               entry.filename = ".phar/signature.bin";
01133               entry.filename_len = sizeof(".phar/signature.bin")-1;
01134               entry.fp = php_stream_fopen_tmpfile();
01135               entry.fp_type = PHAR_MOD;
01136               entry.is_modified = 1;
01137 
01138               PHAR_SET_32(sigbuf, phar->sig_flags);
01139               PHAR_SET_32(sigbuf + 4, signature_length);
01140 
01141               if (8 != (int)php_stream_write(entry.fp, sigbuf, 8) || signature_length != (int)php_stream_write(entry.fp, signature, signature_length)) {
01142                      efree(signature);
01143                      if (pass->error) {
01144                             spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar %s", phar->fname);
01145                      }
01146 
01147                      php_stream_close(newfile);
01148                      return FAILURE;
01149               }
01150 
01151               efree(signature);
01152               entry.uncompressed_filesize = entry.compressed_filesize = signature_length + 8;
01153               entry.phar = phar;
01154               /* throw out return value and write the signature */
01155               phar_zip_changed_apply((void *)&entry, (void *)pass TSRMLS_CC);
01156               php_stream_close(newfile);
01157 
01158               if (pass->error && *(pass->error)) {
01159                      /* error is set by writeheaders */
01160                      php_stream_close(newfile);
01161                      return FAILURE;
01162               }
01163        } /* signature */
01164        return SUCCESS;
01165 }
01166 /* }}} */
01167 
01168 int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defaultstub, char **error TSRMLS_DC) /* {{{ */
01169 {
01170        char *pos;
01171        smart_str main_metadata_str = {0};
01172        static const char newstub[] = "<?php // zip-based phar archive stub file\n__HALT_COMPILER();";
01173        char halt_stub[] = "__HALT_COMPILER();";
01174        char *tmp;
01175        
01176        php_stream *stubfile, *oldfile;
01177        php_serialize_data_t metadata_hash;
01178        int free_user_stub, closeoldfile = 0;
01179        phar_entry_info entry = {0};
01180        char *temperr = NULL;
01181        struct _phar_zip_pass pass;
01182        phar_zip_dir_end eocd;
01183        php_uint32 cdir_size, cdir_offset;
01184 
01185        pass.error = &temperr;
01186        entry.flags = PHAR_ENT_PERM_DEF_FILE;
01187        entry.timestamp = time(NULL);
01188        entry.is_modified = 1;
01189        entry.is_zip = 1;
01190        entry.phar = phar;
01191        entry.fp_type = PHAR_MOD;
01192 
01193        if (phar->is_persistent) {
01194               if (error) {
01195                      spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname);
01196               }
01197               return EOF;
01198        }
01199 
01200        if (phar->is_data) {
01201               goto nostub;
01202        }
01203 
01204        /* set alias */
01205        if (!phar->is_temporary_alias && phar->alias_len) {
01206               entry.fp = php_stream_fopen_tmpfile();
01207 
01208               if (phar->alias_len != (int)php_stream_write(entry.fp, phar->alias, phar->alias_len)) {
01209                      if (error) {
01210                             spprintf(error, 0, "unable to set alias in zip-based phar \"%s\"", phar->fname);
01211                      }
01212                      return EOF;
01213               }
01214 
01215               entry.uncompressed_filesize = entry.compressed_filesize = phar->alias_len;
01216               entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1);
01217               entry.filename_len = sizeof(".phar/alias.txt")-1;
01218 
01219               if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
01220                      if (error) {
01221                             spprintf(error, 0, "unable to set alias in zip-based phar \"%s\"", phar->fname);
01222                      }
01223                      return EOF;
01224               }
01225        } else {
01226               zend_hash_del(&phar->manifest, ".phar/alias.txt", sizeof(".phar/alias.txt")-1);
01227        }
01228 
01229        /* register alias */
01230        if (phar->alias_len) {
01231               if (FAILURE == phar_get_archive(&phar, phar->fname, phar->fname_len, phar->alias, phar->alias_len, error TSRMLS_CC)) {
01232                      return EOF;
01233               }
01234        }
01235 
01236        /* set stub */
01237        if (user_stub && !defaultstub) {
01238               if (len < 0) {
01239                      /* resource passed in */
01240                      if (!(php_stream_from_zval_no_verify(stubfile, (zval **)user_stub))) {
01241                             if (error) {
01242                                    spprintf(error, 0, "unable to access resource to copy stub to new zip-based phar \"%s\"", phar->fname);
01243                             }
01244                             return EOF;
01245                      }
01246 
01247                      if (len == -1) {
01248                             len = PHP_STREAM_COPY_ALL;
01249                      } else {
01250                             len = -len;
01251                      }
01252 
01253                      user_stub = 0;
01254 
01255 #if PHP_MAJOR_VERSION >= 6
01256                      if (!(len = php_stream_copy_to_mem(stubfile, (void **) &user_stub, len, 0)) || !user_stub) {
01257 #else
01258                      if (!(len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)) || !user_stub) {
01259 #endif
01260                             if (error) {
01261                                    spprintf(error, 0, "unable to read resource to copy stub to new zip-based phar \"%s\"", phar->fname);
01262                             }
01263                             return EOF;
01264                      }
01265                      free_user_stub = 1;
01266               } else {
01267                      free_user_stub = 0;
01268               }
01269 
01270               tmp = estrndup(user_stub, len);
01271               if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
01272                      efree(tmp);
01273                      if (error) {
01274                             spprintf(error, 0, "illegal stub for zip-based phar \"%s\"", phar->fname);
01275                      }
01276                      if (free_user_stub) {
01277                             efree(user_stub);
01278                      }
01279                      return EOF;
01280               }
01281               pos = user_stub + (pos - tmp);
01282               efree(tmp);
01283 
01284               len = pos - user_stub + 18;
01285               entry.fp = php_stream_fopen_tmpfile();
01286               entry.uncompressed_filesize = len + 5;
01287 
01288               if ((size_t)len != php_stream_write(entry.fp, user_stub, len)
01289               ||            5 != php_stream_write(entry.fp, " ?>\r\n", 5)) {
01290                      if (error) {
01291                             spprintf(error, 0, "unable to create stub from string in new zip-based phar \"%s\"", phar->fname);
01292                      }
01293                      if (free_user_stub) {
01294                             efree(user_stub);
01295                      }
01296                      php_stream_close(entry.fp);
01297                      return EOF;
01298               }
01299 
01300               entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
01301               entry.filename_len = sizeof(".phar/stub.php")-1;
01302 
01303               if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
01304                      if (free_user_stub) {
01305                             efree(user_stub);
01306                      }
01307                      if (error) {
01308                             spprintf(error, 0, "unable to set stub in zip-based phar \"%s\"", phar->fname);
01309                      }
01310                      return EOF;
01311               }
01312 
01313               if (free_user_stub) {
01314                      efree(user_stub);
01315               }
01316        } else {
01317               /* Either this is a brand new phar (add the stub), or the default stub is required (overwrite the stub) */
01318               entry.fp = php_stream_fopen_tmpfile();
01319 
01320               if (sizeof(newstub)-1 != php_stream_write(entry.fp, newstub, sizeof(newstub)-1)) {
01321                      php_stream_close(entry.fp);
01322                      if (error) {
01323                             spprintf(error, 0, "unable to %s stub in%szip-based phar \"%s\", failed", user_stub ? "overwrite" : "create", user_stub ? " " : " new ", phar->fname);
01324                      }
01325                      return EOF;
01326               }
01327 
01328               entry.uncompressed_filesize = entry.compressed_filesize = sizeof(newstub) - 1;
01329               entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
01330               entry.filename_len = sizeof(".phar/stub.php")-1;
01331 
01332               if (!defaultstub) {
01333                      if (!zend_hash_exists(&phar->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
01334                             if (SUCCESS != zend_hash_add(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
01335                                    php_stream_close(entry.fp);
01336                                    efree(entry.filename);
01337                                    if (error) {
01338                                           spprintf(error, 0, "unable to create stub in zip-based phar \"%s\"", phar->fname);
01339                                    }
01340                                    return EOF;
01341                             }
01342                      } else {
01343                             php_stream_close(entry.fp);
01344                             efree(entry.filename);
01345                      }
01346               } else {
01347                      if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
01348                             php_stream_close(entry.fp);
01349                             efree(entry.filename);
01350                             if (error) {
01351                                    spprintf(error, 0, "unable to overwrite stub in zip-based phar \"%s\"", phar->fname);
01352                             }
01353                             return EOF;
01354                      }
01355               }
01356        }
01357 nostub:
01358        if (phar->fp && !phar->is_brandnew) {
01359               oldfile = phar->fp;
01360               closeoldfile = 0;
01361               php_stream_rewind(oldfile);
01362        } else {
01363               oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
01364               closeoldfile = oldfile != NULL;
01365        }
01366 
01367        /* save modified files to the zip */
01368        pass.old = oldfile;
01369        pass.filefp = php_stream_fopen_tmpfile();
01370 
01371        if (!pass.filefp) {
01372 fperror:
01373               if (closeoldfile) {
01374                      php_stream_close(oldfile);
01375               }
01376               if (error) {
01377                      spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to open temporary file", phar->fname);
01378               }
01379               return EOF;
01380        }
01381 
01382        pass.centralfp = php_stream_fopen_tmpfile();
01383 
01384        if (!pass.centralfp) {
01385               goto fperror;
01386        }
01387 
01388        pass.free_fp = pass.free_ufp = 1;
01389        memset(&eocd, 0, sizeof(eocd));
01390 
01391        strncpy(eocd.signature, "PK\5\6", 4);
01392        if (!phar->is_data && !phar->sig_flags) {
01393               phar->sig_flags = PHAR_SIG_SHA1;
01394        }
01395        if (phar->sig_flags) {
01396               PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest) + 1);
01397               PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest) + 1);
01398        } else {
01399               PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest));
01400               PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest));
01401        }
01402        zend_hash_apply_with_argument(&phar->manifest, phar_zip_changed_apply, (void *) &pass TSRMLS_CC);
01403 
01404        if (phar->metadata) {
01405               /* set phar metadata */
01406               PHP_VAR_SERIALIZE_INIT(metadata_hash);
01407               php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash TSRMLS_CC);
01408               PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
01409        }
01410        if (temperr) {
01411               if (error) {
01412                      spprintf(error, 4096, "phar zip flush of \"%s\" failed: %s", phar->fname, temperr);
01413               }
01414               efree(temperr);
01415 temperror:
01416               php_stream_close(pass.centralfp);
01417 nocentralerror:
01418               if (phar->metadata) {
01419                      smart_str_free(&main_metadata_str);
01420               }
01421               php_stream_close(pass.filefp);
01422               if (closeoldfile) {
01423                      php_stream_close(oldfile);
01424               }
01425               return EOF;
01426        }
01427 
01428        if (FAILURE == phar_zip_applysignature(phar, &pass, &main_metadata_str TSRMLS_CC)) {
01429               goto temperror;
01430        }
01431 
01432        /* save zip */
01433        cdir_size = php_stream_tell(pass.centralfp);
01434        cdir_offset = php_stream_tell(pass.filefp);
01435        PHAR_SET_32(eocd.cdir_size, cdir_size);
01436        PHAR_SET_32(eocd.cdir_offset, cdir_offset);
01437        php_stream_seek(pass.centralfp, 0, SEEK_SET);
01438 
01439        {
01440               size_t clen;
01441               int ret = phar_stream_copy_to_stream(pass.centralfp, pass.filefp, PHP_STREAM_COPY_ALL, &clen);
01442               if (SUCCESS != ret || clen != cdir_size) {
01443                      if (error) {
01444                             spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write central-directory", phar->fname);
01445                      }
01446                      goto temperror;
01447               }
01448        }
01449 
01450        php_stream_close(pass.centralfp);
01451 
01452        if (phar->metadata) {
01453               /* set phar metadata */
01454               PHAR_SET_16(eocd.comment_len, main_metadata_str.len);
01455 
01456               if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
01457                      if (error) {
01458                             spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
01459                      }
01460                      goto nocentralerror;
01461               }
01462 
01463               if (main_metadata_str.len != php_stream_write(pass.filefp, main_metadata_str.c, main_metadata_str.len)) {
01464                      if (error) {
01465                             spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write metadata to zip comment", phar->fname);
01466                      }
01467                      goto nocentralerror;
01468               }
01469 
01470               smart_str_free(&main_metadata_str);
01471 
01472        } else {
01473               if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
01474                      if (error) {
01475                             spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
01476                      }
01477                      goto nocentralerror;
01478               }
01479        }
01480 
01481        if (phar->fp && pass.free_fp) {
01482               php_stream_close(phar->fp);
01483        }
01484 
01485        if (phar->ufp) {
01486               if (pass.free_ufp) {
01487                      php_stream_close(phar->ufp);
01488               }
01489               phar->ufp = NULL;
01490        }
01491 
01492        /* re-open */
01493        phar->is_brandnew = 0;
01494 
01495        if (phar->donotflush) {
01496               /* deferred flush */
01497               phar->fp = pass.filefp;
01498        } else {
01499               phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
01500               if (!phar->fp) {
01501                      if (closeoldfile) {
01502                             php_stream_close(oldfile);
01503                      }
01504                      phar->fp = pass.filefp;
01505                      if (error) {
01506                             spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
01507                      }
01508                      return EOF;
01509               }
01510               php_stream_rewind(pass.filefp);
01511               phar_stream_copy_to_stream(pass.filefp, phar->fp, PHP_STREAM_COPY_ALL, NULL);
01512               /* we could also reopen the file in "rb" mode but there is no need for that */
01513               php_stream_close(pass.filefp);
01514        }
01515 
01516        if (closeoldfile) {
01517               php_stream_close(oldfile);
01518        }
01519        return EOF;
01520 }
01521 /* }}} */
01522 
01523 /*
01524  * Local variables:
01525  * tab-width: 4
01526  * c-basic-offset: 4
01527  * End:
01528  * vim600: noet sw=4 ts=4 fdm=marker
01529  * vim<600: noet sw=4 ts=4
01530  */