applefile_extractor.c (8448B)
1 /* 2 This file is part of libextractor. 3 Copyright (C) 2008 Heikki Lindholm 4 Copyright (C) 2012 Vidyut Samanta and Christian Grothoff 5 6 libextractor is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published 8 by the Free Software Foundation; either version 3, or (at your 9 option) any later version. 10 11 libextractor is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with libextractor; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 Boston, MA 02110-1301, USA. 20 */ 21 /** 22 * @file plugins/applefile_extractor.c 23 * @brief plugin to support AppleSingle and AppleDouble files (RFC 1740) 24 * @author Heikki Lindholm 25 * @author Christian Grothoff 26 */ 27 #include "platform.h" 28 #include "extractor.h" 29 #include "pack.h" 30 31 32 #define APPLESINGLE_SIGNATURE "\x00\x05\x16\x00" 33 #define APPLEDOUBLE_SIGNATURE "\x00\x05\x16\x07" 34 35 36 typedef struct 37 { 38 unsigned char magic[4]; 39 unsigned int version; 40 char homeFileSystem[16]; /* v1: ASCII v2: zero-filled */ 41 unsigned short entries; 42 } ApplefileHeader; 43 44 #define APPLEFILE_HEADER_SIZE 26 45 #define APPLEFILE_HEADER_SPEC "4bW16bH" 46 #define APPLEFILE_HEADER_FIELDS(p) \ 47 & (p)->magic, \ 48 &(p)->version, \ 49 &(p)->homeFileSystem, \ 50 &(p)->entries 51 52 53 typedef struct 54 { 55 unsigned int id; 56 unsigned int offset; 57 unsigned int length; 58 } ApplefileEntryDescriptor; 59 60 #define APPLEFILE_ENTRY_DESCRIPTOR_SIZE 12 61 #define APPLEFILE_ENTRY_DESCRIPTOR_SPEC "WWW" 62 #define APPLEFILE_ENTRY_DESCRIPTOR_FIELDS(p) \ 63 & (p)->id, \ 64 &(p)->offset, \ 65 &(p)->length 66 67 68 /* Entry type IDs (RFC 1740 ยง5) */ 69 #define AED_ID_DATA_FORK 1 70 #define AED_ID_RESOURCE_FORK 2 71 #define AED_ID_REAL_NAME 3 72 #define AED_ID_COMMENT 4 73 #define AED_ID_ICON_BW 5 74 #define AED_ID_ICON_COLOUR 6 75 #define AED_ID_FILE_DATES_INFO 8 76 #define AED_ID_FINDER_INFO 9 77 #define AED_ID_MACINTOSH_FILE_INFO 10 78 #define AED_ID_PRODOS_FILE_INFO 11 79 #define AED_ID_MSDOS_FILE_INFO 12 80 #define AED_ID_SHORT_NAME 13 81 #define AED_ID_AFP_FILE_INFO 14 82 #define AED_ID_DIRECTORY_ID 15 83 84 85 /** 86 * Emit a UTF-8 string as metadata of type @a t. 87 * Returns from the enclosing function if proc signals abort. 88 */ 89 #define ADD(s, t) do { \ 90 if (0 != ec->proc (ec->cls, \ 91 "applefile", \ 92 (t), \ 93 EXTRACTOR_METAFORMAT_UTF8, \ 94 "text/plain", \ 95 (s), \ 96 strlen (s) + 1)) \ 97 return; \ 98 } while (0) 99 100 101 /** 102 * Main entry method for the 'application/applefile' extraction plugin. 103 * 104 * @param ec extraction context provided to the plugin 105 */ 106 void 107 EXTRACTOR_applefile_extract_method (struct EXTRACTOR_ExtractContext *ec) 108 { 109 void *data; 110 ssize_t got; 111 ApplefileHeader header; 112 uint64_t file_size; 113 114 /* read and unpack the 26-byte file header */ 115 got = ec->read (ec->cls, &data, APPLEFILE_HEADER_SIZE); 116 if (got < APPLEFILE_HEADER_SIZE) 117 return; 118 EXTRACTOR_common_cat_unpack (data, 119 APPLEFILE_HEADER_SPEC, 120 APPLEFILE_HEADER_FIELDS (&header)); 121 122 if ((0 != memcmp (header.magic, APPLESINGLE_SIGNATURE, 4)) && 123 (0 != memcmp (header.magic, APPLEDOUBLE_SIGNATURE, 4))) 124 return; 125 126 if (0 != ec->proc (ec->cls, 127 "applefile", 128 EXTRACTOR_METATYPE_MIMETYPE, 129 EXTRACTOR_METAFORMAT_UTF8, 130 "text/plain", 131 "application/applefile", 132 strlen ("application/applefile") + 1)) 133 return; 134 135 if ((header.version != 0x00010000) && (header.version != 0x00020000)) 136 return; 137 138 file_size = ec->get_size (ec->cls); 139 140 for (unsigned int i = 0; i < header.entries; i++) 141 { 142 ApplefileEntryDescriptor dsc; 143 uint64_t desc_pos = (uint64_t) APPLEFILE_HEADER_SIZE 144 + (uint64_t) i * APPLEFILE_ENTRY_DESCRIPTOR_SIZE; 145 146 if ((int64_t) desc_pos != 147 ec->seek (ec->cls, (int64_t) desc_pos, SEEK_SET)) 148 return; 149 got = ec->read (ec->cls, &data, APPLEFILE_ENTRY_DESCRIPTOR_SIZE); 150 if (got < APPLEFILE_ENTRY_DESCRIPTOR_SIZE) 151 return; 152 EXTRACTOR_common_cat_unpack (data, 153 APPLEFILE_ENTRY_DESCRIPTOR_SPEC, 154 APPLEFILE_ENTRY_DESCRIPTOR_FIELDS (&dsc)); 155 156 switch (dsc.id) 157 { 158 case AED_ID_DATA_FORK: 159 { 160 /* Report the data-fork size; no seek needed, length is in the 161 descriptor itself. */ 162 char s[14]; 163 164 if (dsc.length >= 1000000000) 165 snprintf (s, 13, "%.2f %s", dsc.length / 1000000000.0, _ ("GB")); 166 else if (dsc.length >= 1000000) 167 snprintf (s, 13, "%.2f %s", dsc.length / 1000000.0, _ ("MB")); 168 else if (dsc.length >= 1000) 169 snprintf (s, 13, "%.2f %s", dsc.length / 1000.0, _ ("KB")); 170 else 171 snprintf (s, 13, "%.2f %s", (double) dsc.length, _ ("Bytes")); 172 ADD (s, EXTRACTOR_METATYPE_EMBEDDED_FILE_SIZE); 173 break; 174 } 175 176 case AED_ID_REAL_NAME: 177 if ((dsc.length > 0) && 178 (dsc.length < 2048) && 179 ((uint64_t) dsc.offset + dsc.length < file_size)) 180 { 181 char s[2048]; 182 183 if ((int64_t) dsc.offset != 184 ec->seek (ec->cls, (int64_t) dsc.offset, SEEK_SET)) 185 return; 186 got = ec->read (ec->cls, &data, dsc.length); 187 if (got > 0) 188 { 189 memcpy (s, data, got); 190 s[got] = '\0'; 191 ADD (s, EXTRACTOR_METATYPE_FILENAME); 192 } 193 } 194 break; 195 196 case AED_ID_COMMENT: 197 if ((dsc.length > 0) && 198 (dsc.length < 65536) && 199 ((uint64_t) dsc.offset + dsc.length < file_size)) 200 { 201 if ((int64_t) dsc.offset != 202 ec->seek (ec->cls, (int64_t) dsc.offset, SEEK_SET)) 203 return; 204 got = ec->read (ec->cls, &data, dsc.length); 205 if (got > 0) 206 { 207 char *s = malloc ((size_t) got + 1); 208 209 if (NULL != s) 210 { 211 memcpy (s, data, got); 212 s[got] = '\0'; 213 if (0 != ec->proc (ec->cls, 214 "applefile", 215 EXTRACTOR_METATYPE_COMMENT, 216 EXTRACTOR_METAFORMAT_UTF8, 217 "text/plain", 218 s, 219 (size_t) got + 1)) 220 { 221 free (s); 222 return; 223 } 224 free (s); 225 } 226 } 227 } 228 break; 229 230 case AED_ID_FINDER_INFO: 231 /* Finder info block: first 4 bytes = file type, next 4 = creator */ 232 if ((dsc.length >= 8) && 233 ((uint64_t) dsc.offset + dsc.length < file_size)) 234 { 235 char type_s[5]; 236 char creator_s[5]; 237 238 if ((int64_t) dsc.offset != 239 ec->seek (ec->cls, (int64_t) dsc.offset, SEEK_SET)) 240 return; 241 got = ec->read (ec->cls, &data, 8); 242 if (got < 8) 243 break; 244 /* copy both before any further read or proc call */ 245 memcpy (type_s, data, 4); 246 type_s[4] = '\0'; 247 memcpy (creator_s, (const char *) data + 4, 4); 248 creator_s[4] = '\0'; 249 250 if (0 != ec->proc (ec->cls, 251 "applefile", 252 EXTRACTOR_METATYPE_FINDER_FILE_TYPE, 253 EXTRACTOR_METAFORMAT_C_STRING, 254 "text/plain", 255 type_s, 256 strlen (type_s) + 1)) 257 return; 258 if (0 != ec->proc (ec->cls, 259 "applefile", 260 EXTRACTOR_METATYPE_FINDER_FILE_CREATOR, 261 EXTRACTOR_METAFORMAT_C_STRING, 262 "text/plain", 263 creator_s, 264 strlen (creator_s) + 1)) 265 return; 266 } 267 break; 268 269 default: 270 break; 271 } 272 } 273 } 274 275 276 /* end of applefile_extractor.c */