/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is the Netscape security libraries. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1994-2000 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "signtool.h" #include "zip.h" #include "zlib.h" #include "prmem.h" static void inttox (int in, char *out); static void longtox (long in, char *out); /**************************************************************** * * J z i p O p e n * * Opens a new ZIP file and creates a new ZIPfile structure to * control the process of installing files into a zip. */ ZIPfile* JzipOpen(char *filename, char *comment) { ZIPfile * zipfile; PRExplodedTime prtime; zipfile = PORT_ZAlloc(sizeof(ZIPfile)); if (!zipfile) out_of_memory(); /* Construct time and date */ PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &prtime); zipfile->date = ((prtime.tm_year - 1980) << 9) | ((prtime.tm_month + 1) << 5) | prtime.tm_mday; zipfile->time = (prtime.tm_hour << 11) | (prtime.tm_min << 5) | (prtime.tm_sec & 0x3f); zipfile->fp = NULL; if (filename && (zipfile->fp = PR_Open(filename, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0777)) == NULL) { char *nsprErr; if (PR_GetErrorTextLength()) { nsprErr = PR_Malloc(PR_GetErrorTextLength()); PR_GetErrorText(nsprErr); } else { nsprErr = NULL; } PR_fprintf(errorFD, "%s: can't open output jar, %s.%s\n", PROGRAM_NAME, filename, nsprErr ? nsprErr : ""); if (nsprErr) PR_Free(nsprErr); errorCount++; exit (ERRX); } zipfile->list = NULL; if (filename) { zipfile->filename = PORT_ZAlloc(strlen(filename) + 1); if (!zipfile->filename) out_of_memory(); PORT_Strcpy(zipfile->filename, filename); } if (comment) { zipfile->comment = PORT_ZAlloc(strlen(comment) + 1); if (!zipfile->comment) out_of_memory(); PORT_Strcpy(zipfile->comment, comment); } return zipfile; } static void* my_alloc_func(void*opaque, uInt items, uInt size) { return PORT_Alloc(items * size); } static void my_free_func(void*opaque, void*address) { PORT_Free(address); } static void handle_zerror(int err, char *msg) { if (!msg) { msg = ""; } errorCount++; /* unless Z_OK...see below */ switch (err) { case Z_OK: PR_fprintf(errorFD, "No error: %s\n", msg); errorCount--; /* this was incremented above */ break; case Z_MEM_ERROR: PR_fprintf(errorFD, "Deflation ran out of memory: %s\n", msg); break; case Z_STREAM_ERROR: PR_fprintf(errorFD, "Invalid compression level: %s\n", msg); break; case Z_VERSION_ERROR: PR_fprintf(errorFD, "Incompatible compression library version: %s\n", msg); break; case Z_DATA_ERROR: PR_fprintf(errorFD, "Compression data error: %s\n", msg); break; default: PR_fprintf(errorFD, "Unknown error in compression library: %s\n", msg); break; } } /**************************************************************** * * J z i p A d d * * Adds a new file into a ZIP file. The ZIP file must have already * been opened with JzipOpen. */ int JzipAdd(char *fullname, char *filename, ZIPfile *zipfile, int compression_level) { ZIPentry * entry; PRFileDesc * readfp; PRFileDesc * zipfp; unsigned long crc; unsigned long local_size_pos; int num; int err; int deflate_percent; z_stream zstream; Bytef inbuf[BUFSIZ]; Bytef outbuf[BUFSIZ]; if ( !fullname || !filename || !zipfile) { return - 1; } zipfp = zipfile->fp; if (!zipfp) return - 1; if ( (readfp = PR_Open(fullname, PR_RDONLY, 0777)) == NULL) { char *nsprErr; if (PR_GetErrorTextLength()) { nsprErr = PR_Malloc(PR_GetErrorTextLength()); PR_GetErrorText(nsprErr); } else { nsprErr = NULL; } PR_fprintf(errorFD, "%s: %s\n", fullname, nsprErr ? nsprErr : ""); errorCount++; if (nsprErr) PR_Free(nsprErr); exit(ERRX); } /* * Make sure the input file is not the output file. * Add a few bytes to the end of the JAR file and see if the input file * twitches */ { PRInt32 endOfJar; PRInt32 inputSize; PRBool isSame; inputSize = PR_Available(readfp); endOfJar = PR_Seek(zipfp, 0L, PR_SEEK_CUR); if (PR_Write(zipfp, "abcde", 5) < 5) { char *nsprErr; if (PR_GetErrorTextLength()) { nsprErr = PR_Malloc(PR_GetErrorTextLength()); PR_GetErrorText(nsprErr); } else { nsprErr = NULL; } PR_fprintf(errorFD, "Writing to zip file: %s\n", nsprErr ? nsprErr : ""); if (nsprErr) PR_Free(nsprErr); errorCount++; exit(ERRX); } isSame = (PR_Available(readfp) != inputSize); PR_Seek(zipfp, endOfJar, PR_SEEK_SET); if (isSame) { /* It's the same file! Forget it! */ PR_Close(readfp); return 0; } } if (verbosity >= 0) { PR_fprintf(outputFD, "adding %s to %s...", fullname, zipfile->filename); } entry = PORT_ZAlloc(sizeof(ZIPentry)); if (!entry) out_of_memory(); entry->filename = PORT_Strdup(filename); entry->comment = NULL; /* Set up local file header */ longtox(LSIG, entry->local.signature); inttox(strlen(filename), entry->local.filename_len); inttox(zipfile->time, entry->local.time); inttox(zipfile->date, entry->local.date); inttox(Z_DEFLATED, entry->local.method); /* Set up central directory entry */ longtox(CSIG, entry->central.signature); inttox(strlen(filename), entry->central.filename_len); if (entry->comment) { inttox(strlen(entry->comment), entry->central.commentfield_len); } longtox(PR_Seek(zipfile->fp, 0, PR_SEEK_CUR), entry->central.localhdr_offset); inttox(zipfile->time, entry->central.time); inttox(zipfile->date, entry->central.date); inttox(Z_DEFLATED, entry->central.method); /* Compute crc. Too bad we have to process the whole file to do this*/ crc = crc32(0L, NULL, 0); while ( (num = PR_Read(readfp, inbuf, BUFSIZ)) > 0) { crc = crc32(crc, inbuf, num); } PR_Seek(readfp, 0L, PR_SEEK_SET); /* Store CRC */ longtox(crc, entry->local.crc32); longtox(crc, entry->central.crc32); /* Stick this entry onto the end of the list */ entry->next = NULL; if ( zipfile->list == NULL ) { /* First entry */ zipfile->list = entry; } else { ZIPentry * pe; pe = zipfile->list; while (pe->next != NULL) { pe = pe->next; } pe->next = entry; } /* * Start writing stuff out */ local_size_pos = PR_Seek(zipfp, 0, PR_SEEK_CUR) + 18; /* File header */ if (PR_Write(zipfp, &entry->local, sizeof(struct ZipLocal )) < sizeof(struct ZipLocal )) { char *nsprErr; if (PR_GetErrorTextLength()) { nsprErr = PR_Malloc(PR_GetErrorTextLength()); PR_GetErrorText(nsprErr); } else { nsprErr = NULL; } PR_fprintf(errorFD, "Writing zip data: %s\n", nsprErr ? nsprErr : ""); if (nsprErr) PR_Free(nsprErr); errorCount++; exit(ERRX); } /* File Name */ if ( PR_Write(zipfp, filename, strlen(filename)) < strlen(filename)) { char *nsprErr; if (PR_GetErrorTextLength()) { nsprErr = PR_Malloc(PR_GetErrorTextLength()); PR_GetErrorText(nsprErr); } else { nsprErr = NULL; } PR_fprintf(errorFD, "Writing zip data: %s\n", nsprErr ? nsprErr : ""); if (nsprErr) PR_Free(nsprErr); errorCount++; exit(ERRX); } /* * File data */ /* Initialize zstream */ zstream.zalloc = my_alloc_func; zstream.zfree = my_free_func; zstream.opaque = NULL; zstream.next_in = inbuf; zstream.avail_in = BUFSIZ; zstream.next_out = outbuf; zstream.avail_out = BUFSIZ; /* Setting the windowBits to -MAX_WBITS is an undocumented feature of * zlib (see deflate.c in zlib). It is the same thing that Java does * when you specify the nowrap option for deflation in java.util.zip. * It causes zlib to leave out its headers and footers, which don't * work in PKZIP files. */ err = deflateInit2(&zstream, compression_level, Z_DEFLATED, -MAX_WBITS, 8 /*default*/, Z_DEFAULT_STRATEGY); if (err != Z_OK) { handle_zerror(err, zstream.msg); exit(ERRX); } while ( (zstream.avail_in = PR_Read(readfp, inbuf, BUFSIZ)) > 0) { zstream.next_in = inbuf; /* Process this chunk of data */ while (zstream.avail_in > 0) { err = deflate(&zstream, Z_NO_FLUSH); if (err != Z_OK) { handle_zerror(err, zstream.msg); exit(ERRX); } if (zstream.avail_out <= 0) { if ( PR_Write(zipfp, outbuf, BUFSIZ) < BUFSIZ) { char *nsprErr; if (PR_GetErrorTextLength()) { nsprErr = PR_Malloc(PR_GetErrorTextLength()); PR_GetErrorText(nsprErr); } else { nsprErr = NULL; } PR_fprintf(errorFD, "Writing zip data: %s\n", nsprErr ? nsprErr : ""); if (nsprErr) PR_Free(nsprErr); errorCount++; exit(ERRX); } zstream.next_out = outbuf; zstream.avail_out = BUFSIZ; } } } /* Now flush everything */ while (1) { err = deflate(&zstream, Z_FINISH); if (err == Z_STREAM_END) { break; } else if (err == Z_OK) { /* output buffer full, repeat */ } else { handle_zerror(err, zstream.msg); exit(ERRX); } if ( PR_Write(zipfp, outbuf, BUFSIZ) < BUFSIZ) { char *nsprErr; if (PR_GetErrorTextLength()) { nsprErr = PR_Malloc(PR_GetErrorTextLength()); PR_GetErrorText(nsprErr); } else { nsprErr = NULL; } PR_fprintf(errorFD, "Writing zip data: %s\n", nsprErr ? nsprErr : ""); if (nsprErr) PR_Free(nsprErr); errorCount++; exit(ERRX); } zstream.avail_out = BUFSIZ; zstream.next_out = outbuf; } /* If there's any output left, write it out. */ if (zstream.next_out != outbuf) { if ( PR_Write(zipfp, outbuf, zstream.next_out - outbuf) < zstream.next_out - outbuf) { char *nsprErr; if (PR_GetErrorTextLength()) { nsprErr = PR_Malloc(PR_GetErrorTextLength()); PR_GetErrorText(nsprErr); } else { nsprErr = NULL; } PR_fprintf(errorFD, "Writing zip data: %s\n", nsprErr ? nsprErr : ""); if (nsprErr) PR_Free(nsprErr); errorCount++; exit(ERRX); } zstream.avail_out = BUFSIZ; zstream.next_out = outbuf; } /* Now that we know the compressed size, write this to the headers */ longtox(zstream.total_in, entry->local.orglen); longtox(zstream.total_out, entry->local.size); if (PR_Seek(zipfp, local_size_pos, PR_SEEK_SET) == -1) { char *nsprErr; if (PR_GetErrorTextLength()) { nsprErr = PR_Malloc(PR_GetErrorTextLength()); PR_GetErrorText(nsprErr); } else { nsprErr = NULL; } PR_fprintf(errorFD, "Accessing zip file: %s\n", nsprErr ? nsprErr : ""); if (nsprErr) PR_Free(nsprErr); errorCount++; exit(ERRX); } if ( PR_Write(zipfp, entry->local.size, 8) != 8) { char *nsprErr; if (PR_GetErrorTextLength()) { nsprErr = PR_Malloc(PR_GetErrorTextLength()); PR_GetErrorText(nsprErr); } else { nsprErr = NULL; } PR_fprintf(errorFD, "Writing zip data: %s\n", nsprErr ? nsprErr : ""); if (nsprErr) PR_Free(nsprErr); errorCount++; exit(ERRX); } if (PR_Seek(zipfp, 0L, PR_SEEK_END) == -1) { char *nsprErr; if (PR_GetErrorTextLength()) { nsprErr = PR_Malloc(PR_GetErrorTextLength()); PR_GetErrorText(nsprErr); } else { nsprErr = NULL; } PR_fprintf(errorFD, "Accessing zip file: %s\n", nsprErr ? nsprErr : ""); if (nsprErr) PR_Free(nsprErr); errorCount++; exit(ERRX); } longtox(zstream.total_in, entry->central.orglen); longtox(zstream.total_out, entry->central.size); /* Close out the deflation operation */ err = deflateEnd(&zstream); if (err != Z_OK) { handle_zerror(err, zstream.msg); exit(ERRX); } PR_Close(readfp); if ((zstream.total_in > zstream.total_out) && (zstream.total_in > 0)) { deflate_percent = (int) ((zstream.total_in - zstream.total_out) *100 / zstream.total_in); } else { deflate_percent = 0; } if (verbosity >= 0) { PR_fprintf(outputFD, "(deflated %d%%)\n", deflate_percent); } return 0; } /******************************************************************** * J z i p C l o s e * * Finishes the ZipFile. ALSO DELETES THE ZIPFILE STRUCTURE PASSED IN!! */ int JzipClose(ZIPfile *zipfile) { ZIPentry * pe, *dead; PRFileDesc * zipfp; struct ZipEnd zipend; unsigned int entrycount = 0; if (!zipfile) { return - 1; } if (!zipfile->filename) { /* bogus */ return 0; } zipfp = zipfile->fp; zipfile->central_start = PR_Seek(zipfp, 0L, PR_SEEK_CUR); /* Write out all the central directories */ pe = zipfile->list; while (pe) { entrycount++; /* Write central directory info */ if ( PR_Write(zipfp, &pe->central, sizeof(struct ZipCentral )) < sizeof(struct ZipCentral )) { char *nsprErr; if (PR_GetErrorTextLength()) { nsprErr = PR_Malloc(PR_GetErrorTextLength()); PR_GetErrorText(nsprErr); } else { nsprErr = NULL; } PR_fprintf(errorFD, "Writing zip data: %s\n", nsprErr ? nsprErr : ""); if (nsprErr) PR_Free(nsprErr); errorCount++; exit(ERRX); } /* Write filename */ if ( PR_Write(zipfp, pe->filename, strlen(pe->filename)) < strlen(pe->filename)) { char *nsprErr; if (PR_GetErrorTextLength()) { nsprErr = PR_Malloc(PR_GetErrorTextLength()); PR_GetErrorText(nsprErr); } else { nsprErr = NULL; } PR_fprintf(errorFD, "Writing zip data: %s\n", nsprErr ? nsprErr : ""); if (nsprErr) PR_Free(nsprErr); errorCount++; exit(ERRX); } /* Write file comment */ if (pe->comment) { if ( PR_Write(zipfp, pe->comment, strlen(pe->comment)) < strlen(pe->comment)) { char *nsprErr; if (PR_GetErrorTextLength()) { nsprErr = PR_Malloc(PR_GetErrorTextLength()); PR_GetErrorText(nsprErr); } else { nsprErr = NULL; } PR_fprintf(errorFD, "Writing zip data: %s\n", nsprErr ? nsprErr : ""); if (nsprErr) PR_Free(nsprErr); errorCount++; exit(ERRX); } } /* Delete the structure */ dead = pe; pe = pe->next; if (dead->filename) { PORT_Free(dead->filename); } if (dead->comment) { PORT_Free(dead->comment); } PORT_Free(dead); } zipfile->central_end = PR_Seek(zipfile->fp, 0L, PR_SEEK_CUR); /* Create the ZipEnd structure */ PORT_Memset(&zipend, 0, sizeof(zipend)); longtox(ESIG, zipend.signature); inttox(entrycount, zipend.total_entries_disk); inttox(entrycount, zipend.total_entries_archive); longtox(zipfile->central_end - zipfile->central_start, zipend.central_dir_size); longtox(zipfile->central_start, zipend.offset_central_dir); if (zipfile->comment) { inttox(strlen(zipfile->comment), zipend.commentfield_len); } /* Write out ZipEnd xtructure */ if ( PR_Write(zipfp, &zipend, sizeof(zipend)) < sizeof(zipend)) { char *nsprErr; if (PR_GetErrorTextLength()) { nsprErr = PR_Malloc(PR_GetErrorTextLength()); PR_GetErrorText(nsprErr); } else { nsprErr = NULL; } PR_fprintf(errorFD, "Writing zip data: %s\n", nsprErr ? nsprErr : ""); if (nsprErr) PR_Free(nsprErr); errorCount++; exit(ERRX); } /* Write out Zipfile comment */ if (zipfile->comment) { if ( PR_Write(zipfp, zipfile->comment, strlen(zipfile->comment)) < strlen(zipfile->comment)) { char *nsprErr; if (PR_GetErrorTextLength()) { nsprErr = PR_Malloc(PR_GetErrorTextLength()); PR_GetErrorText(nsprErr); } else { nsprErr = NULL; } PR_fprintf(errorFD, "Writing zip data: %s\n", nsprErr ? nsprErr : ""); if (nsprErr) PR_Free(nsprErr); errorCount++; exit(ERRX); } } PR_Close(zipfp); /* Free the memory of the zipfile structure */ if (zipfile->filename) { PORT_Free(zipfile->filename); } if (zipfile->comment) { PORT_Free(zipfile->comment); } PORT_Free(zipfile); return 0; } /********************************************** * i n t t o x * * Converts a two byte ugly endianed integer * to our platform's integer. * */ static void inttox (int in, char *out) { out [0] = (in & 0xFF); out [1] = (in & 0xFF00) >> 8; } /********************************************* * l o n g t o x * * Converts a four byte ugly endianed integer * to our platform's integer. * */ static void longtox (long in, char *out) { out [0] = (in & 0xFF); out [1] = (in & 0xFF00) >> 8; out [2] = (in & 0xFF0000) >> 16; out [3] = (in & 0xFF000000) >> 24; }