/* $Id: zip.c,v 1.1.1.1 2009/02/21 21:25:21 khorben Exp $ */ /* Copyright (c) 2007 khorben of Uberwall */ /* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "plugin.h" /* zip */ /* public */ /* types */ #pragma pack(1) struct zip_lfh { unsigned char signature[4]; uint16_t version; uint16_t general; uint16_t method; uint16_t mtime; uint16_t mdate; uint32_t crc; uint32_t csize; uint32_t usize; uint16_t filename; uint16_t extra; }; struct zip_dd { unsigned char signature[4]; /* XXX may not be present */ uint32_t crc; uint32_t csize; uint32_t usize; }; struct zip64_dd { unsigned char signature[4]; /* XXX may not be present */ uint32_t crc; uint64_t csize; uint64_t usize; }; struct zip_xd { unsigned char signature[4]; uint32_t length; }; struct zip_cdfh { unsigned char signature[4]; uint16_t version; uint16_t needed; uint16_t general; uint16_t method; uint16_t mtime; uint16_t mdate; uint32_t crc; uint32_t csize; uint32_t usize; uint16_t filename; uint16_t extra; uint16_t comment; uint16_t disk; uint16_t internal; uint32_t external; uint32_t offset; }; struct zip_ds { unsigned char signature[4]; uint16_t length; }; struct zip_ecdr { unsigned char signature[4]; uint16_t disk; uint16_t start; uint16_t diskentries; uint16_t entries; uint32_t size; uint32_t offset; uint16_t comment; }; #pragma pack() /* constants */ enum { ZIP_LFH = 0, /* local file header */ ZIP_DD, /* data descriptor */ ZIP_XD, /* extra data */ ZIP_CDFH, /* central directory file header */ ZIP_DS, /* digital signature */ ZIP64_ECDR, /* zip64 end of central directory record */ ZIP64_ECDL, /* zip64 end of central directory locator */ ZIP_ECDR /* end of central directory record */ }; /* variables */ /* magic */ static unsigned char siglfh[] = "PK\x03\x04"; static unsigned char sigdd[] = "PK\x07\x08"; static unsigned char sigxd[] = "PK\x06\x08"; static unsigned char sigcdfh[] = "PK\x01\x02"; static unsigned char sigds[] = "PK\x05\x05"; static unsigned char sig64ecdr[] = "PK\x06\x06"; static unsigned char sig64ecdl[] = "PK\x06\x07"; static unsigned char sigecdr[] = "PK\x05\x06"; PluginMagic zip_magic[] = { { 4, 0, siglfh, sizeof(siglfh)-1 }, { 4, 0, sigdd, sizeof(sigdd)-1 }, { 4, 0, sigxd, sizeof(sigxd)-1 }, { 4, 0, sigcdfh, sizeof(sigcdfh)-1 }, { 4, 0, sigds, sizeof(sigds)-1 }, { 4, 0, sig64ecdr, sizeof(sig64ecdr)-1 }, { 4, 0, sig64ecdl, sizeof(sig64ecdl)-1 }, { 4, 0, sigecdr, sizeof(sigecdr)-1 }, { 0, 0, NULL, 0 } }; /* functions */ static int zip_callback(PluginHelper * ph, int signature, FILE * fp); /* plugin */ Plugin plugin = { PT_ARCHIVE | PT_COMPRESSION, "ZIP", zip_magic, zip_callback }; /* private */ /* functions */ static int _zip_version(PluginHelper * ph, uint16_t version); static int _zip_general(PluginHelper * ph, uint16_t general); static int _zip_method(PluginHelper * ph, uint16_t method); static int _zip_mdatetime(PluginHelper * ph, uint16_t date, uint16_t time); static int _zip_size(PluginHelper * ph, uint32_t csize, uint32_t usize); static int _zip_filename(PluginHelper * ph, uint16_t length, FILE * fp); static int _zip_comment(PluginHelper * ph, uint16_t length, FILE * fp); /* zip_callback */ static int _callback_lfh(PluginHelper * ph, FILE * fp); static int _callback_dd(PluginHelper * ph, FILE * fp); static int _callback_xd(PluginHelper * ph, FILE * fp); static int _callback_cdfh(PluginHelper * ph, FILE * fp); static int _callback_ds(PluginHelper * ph, FILE * fp); static int _callback_ecdr(PluginHelper * ph, FILE * fp); static int zip_callback(PluginHelper * ph, int signature, FILE * fp) { struct { int signature; char * string; } name[] = { { ZIP_LFH, "local file header" }, { ZIP_DD, "data descriptor" }, { ZIP_XD, "extra data", }, { ZIP_CDFH, "central directory file header" }, { ZIP_DS, "digital signature", }, { ZIP64_ECDR, "zip64 end of central directory record" }, { ZIP64_ECDL, "zip64 end of central directory locator"}, { ZIP_ECDR, "end of central directory record" }, { -1, "unknown" } }; size_t i; for(i = 0; name[i].signature != -1 && signature != name[i].signature; i++); ph->printf(ph, "%s", name[i].string); switch(signature) { case ZIP_LFH: return _callback_lfh(ph, fp); case ZIP_DD: return _callback_dd(ph, fp); case ZIP_XD: return _callback_xd(ph, fp); case ZIP_CDFH: return _callback_cdfh(ph, fp); case ZIP_DS: return _callback_ds(ph, fp); case ZIP_ECDR: return _callback_ecdr(ph, fp); default: break; /* FIXME implement */ } ph->printf(ph, "%s", ", not implemented\n"); return 0; } static int _callback_lfh(PluginHelper * ph, FILE * fp) { int score = 0; struct zip_lfh buf; if(fread(&buf, sizeof(buf), 1, fp) != 1) return -1; score += _zip_version(ph, buf.version); score += _zip_general(ph, buf.general); score += _zip_method(ph, buf.method); score += _zip_mdatetime(ph, buf.mdate, buf.mtime); score += _zip_size(ph, buf.csize, buf.usize); score += _zip_filename(ph, buf.filename, fp); ph->printf(ph, "\n"); return score / 6; } static int _callback_dd(PluginHelper * ph, FILE * fp) { int score = 0; struct zip_dd buf; if(fread(&buf, sizeof(buf), 1, fp) != 1) return -1; score += _zip_size(ph, buf.csize, buf.usize); ph->printf(ph, "\n"); return score; } static int _callback_xd(PluginHelper * ph, FILE * fp) { struct zip_xd buf; if(fread(&buf, sizeof(buf), 1, fp) != 1) return -1; ph->printf(ph, "length %u\n", htol32(buf.length)); return 100; } static int _callback_cdfh(PluginHelper * ph, FILE * fp) { int score = 0; struct zip_cdfh buf; if(fread(&buf, sizeof(buf), 1, fp) != 1) return -1; score += _zip_version(ph, buf.version); score += _zip_general(ph, buf.general); score += _zip_method(ph, buf.method); score += _zip_mdatetime(ph, buf.mdate, buf.mtime); score += _zip_size(ph, buf.csize, buf.usize); ph->printf(ph, "\n"); return score / 5; } static int _callback_ds(PluginHelper * ph, FILE * fp) { struct zip_ds buf; if(fread(&buf, sizeof(buf), 1, fp) != 1) return -1; ph->printf(ph, "length %u\n", htol16(buf.length)); return 100; } static int _callback_ecdr(PluginHelper * ph, FILE * fp) { int score = 0; struct zip_ecdr buf; if(fread(&buf, sizeof(buf), 1, fp) != 1) return -1; score += _zip_comment(ph, buf.comment, fp); ph->printf(ph, "\n"); return score; } /* _zip_version */ static int _zip_version(PluginHelper * ph, uint16_t version) { struct { int os; char * string; int score; } name[] = { { 0, "MS-DOS and OS/2 (FAT/VFAT/FAT32)", 100 }, { 1, "Amiga", 30 }, { 2, "OpenVMS", 30 }, { 3, "UNIX", 100 }, { 4, "VM/CMS", 30 }, { 5, "Atari ST", 30 }, { 6, "OS/2 HPFS", 100 }, { 7, "Macintosh", 100 }, { 8, "Z-System", 30 }, { 9, "CP/M", 30 }, { 10, "Windows NTFS", 100 }, { 11, "MVS (OS/390 - Z/OS)", 30 }, { 12, "VSE", 30 }, { 13, "Acorn Risc", 30 }, { 14, "VFAT", 100 }, { 15, "alternate MVS", 30 }, { 16, "BeOS", 30 }, { 17, "Tandem", 30 }, { 18, "OS/400", 30 }, { 19, "OS/X (Darwin)", 100 }, { -1, "unknown", 20 } }; size_t i; version = htol16(version); for(i = 0; name[i].os != -1 && (version >> 8) != name[i].os; i++); ph->printf(ph, "%s%s%s%u.%u", ", OS ", name[i].string, ", specification ", (version & 0xff) / 10, (version & 0xff) % 10); return name[i].score; } /* _zip_general */ static int _zip_general(PluginHelper * ph, uint16_t general) { general = htol16(general); if(general & 0x1) ph->printf(ph, "%s", ", encrypted"); if(general & 0x800) ph->printf(ph, "%s", ", filename in UTF-8"); if(general & 0x1000) ph->printf(ph, "%s", ", enhanced compression"); if(general & 0x2000) ph->printf(ph, "%s", ", central directory encrypted"); return 100; /* FIXME improve */ } /* _zip_method */ static int _zip_method(PluginHelper * ph, uint16_t method) { struct { int method; char * string; int score; } name[] = { { 0, "none", 100 }, { 1, "shrunk", 30 }, { 2, "factor 1", 30 }, { 3, "factor 2", 30 }, { 4, "factor 3", 30 }, { 5, "factor 4", 30 }, { 6, "imploded", 30 }, { 7, "tokenizing", 30 }, { 8, "deflated", 100 }, { 9, "Deflate64 (tm)", 30 }, { 10, "PKWARE DCLI/old IBM TERSE", 30 }, { 11, "reserved", 20 }, { 12, "BZIP2", 30 }, { 13, "reserved", 20 }, { 14, "LZMA", 30 }, { 15, "reserved", 20 }, { 16, "reserved", 20 }, { 17, "reserved", 20 }, { 18, "new IBM TERSE", 30 }, { 19, "IBM LZ77 z Architecture (PFS)", 30 }, { 98, "PPMd version I, Rev 1", 30 }, { -1, "unknown", 20 } }; size_t i; method = htol16(method); for(i = 0; name[i].method != -1 && method != name[i].method; i++); ph->printf(ph, "%s%s", ", compression ", name[i].string); return name[i].score; } /* _zip_mdatetime */ static int _zip_mdatetime(PluginHelper * ph, uint16_t mdate, uint16_t mtime) { static int day_n[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0 }; int month; /* from Linux fs/fat/misc.c date_dos2unix() */ int year; time_t secs; struct tm tm; char tmp[22] = ""; month = ((mdate >> 5) - 1) & 15; year = mdate >> 9; secs = (mtime & 31)*2+60*((mtime >> 5) & 63)+(mtime >> 11)*3600+86400* ((mdate & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 && month < 2 ? 1 : 0)+3653); if(secs == 0) return 0; if(gmtime_r(&secs, &tm) == NULL || strftime(tmp, sizeof(tmp), ", %d/%m/%Y %H:%M:%S", &tm) == 0) { ph->printf(ph, "%s", ", unknown date"); return 0; } ph->printf(ph, "%s", tmp); /* between Wed May 1 00:00:00 CEST 1996 and now */ return secs > 830901600 && secs < time(NULL) ? 100 : 0; } /* _zip_size */ static int _zip_size(PluginHelper * ph, uint32_t csize, uint32_t usize) { ph->printf(ph, "%s%u%s%u", ", compressed size ", csize, ", uncompressed size ", usize); if(htol32(usize) < htol32(csize)) return 0; return 100; } /* _zip_filename */ static int _zip_filename(PluginHelper * ph, uint16_t length, FILE * fp) { char * p; if((length = htol16(length)) == 0) { ph->printf(ph, "%s", ", no filename"); return 100; } ph->printf(ph, "%s%u", ", filename length ", length); if((p = malloc(length + 1)) == NULL) return 0; if(fread(p, sizeof(char), length, fp) != length) { free(p); return 0; } p[length] = '\0'; ph->printf(ph, "%s%s%s", " => \"", p, "\""); free(p); return 100; } /* _zip_comment */ static int _zip_comment(PluginHelper * ph, uint16_t length, FILE * fp) { char * p; if((length = htol16(length)) == 0) { ph->printf(ph, "%s", ", no comment"); return 100; } ph->printf(ph, "%s%u", ", comment length ", length); if((p = malloc(length + 1)) == NULL) return 0; if(fread(p, sizeof(char), length, fp) != length) { free(p); return 0; } p[length] = '\0'; ph->printf(ph, "%s%s%s", " => \"", p, "\""); free(p); return 100; }