html_extractor.c (8780B)
1 /* 2 This file is part of libextractor. 3 Copyright (C) 2002, 2003, 2004, 2005, 2009, 2012 Vidyut Samanta and Christian Grothoff 4 5 libextractor is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 2, or (at your 8 option) any later version. 9 10 libextractor is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with libextractor; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 Boston, MA 02110-1301, USA. 19 20 */ 21 /** 22 * @file plugins/html_extractor.c 23 * @brief plugin to support HTML files 24 * @author Christian Grothoff 25 */ 26 #include "platform.h" 27 #include "extractor.h" 28 #include <magic.h> 29 #if HAVE_TIDY_H 30 #include <tidy.h> 31 #include <tidybuffio.h> 32 #elif HAVE_TIDY_TIDY_H 33 #include <tidy/tidy.h> 34 #include <tidy/tidybuffio.h> 35 #else 36 Broken build, fix tidy detection. 37 #endif 38 39 /** 40 * Mapping of HTML META names to LE types. 41 */ 42 static struct 43 { 44 /** 45 * HTML META name. 46 */ 47 const char *name; 48 49 /** 50 * Corresponding LE type. 51 */ 52 enum EXTRACTOR_MetaType type; 53 } tagmap[] = { 54 { "author", EXTRACTOR_METATYPE_AUTHOR_NAME }, 55 { "dc.author", EXTRACTOR_METATYPE_AUTHOR_NAME }, 56 { "title", EXTRACTOR_METATYPE_TITLE }, 57 { "dc.title", EXTRACTOR_METATYPE_TITLE}, 58 { "description", EXTRACTOR_METATYPE_DESCRIPTION }, 59 { "dc.description", EXTRACTOR_METATYPE_DESCRIPTION }, 60 { "subject", EXTRACTOR_METATYPE_SUBJECT}, 61 { "dc.subject", EXTRACTOR_METATYPE_SUBJECT}, 62 { "date", EXTRACTOR_METATYPE_UNKNOWN_DATE }, 63 { "dc.date", EXTRACTOR_METATYPE_UNKNOWN_DATE}, 64 { "publisher", EXTRACTOR_METATYPE_PUBLISHER }, 65 { "dc.publisher", EXTRACTOR_METATYPE_PUBLISHER}, 66 { "rights", EXTRACTOR_METATYPE_RIGHTS }, 67 { "dc.rights", EXTRACTOR_METATYPE_RIGHTS }, 68 { "copyright", EXTRACTOR_METATYPE_COPYRIGHT }, 69 { "language", EXTRACTOR_METATYPE_LANGUAGE }, 70 { "keywords", EXTRACTOR_METATYPE_KEYWORDS }, 71 { "abstract", EXTRACTOR_METATYPE_ABSTRACT }, 72 { "formatter", EXTRACTOR_METATYPE_CREATED_BY_SOFTWARE }, 73 { "dc.creator", EXTRACTOR_METATYPE_CREATOR}, 74 { "dc.identifier", EXTRACTOR_METATYPE_URI }, 75 { "dc.format", EXTRACTOR_METATYPE_FORMAT }, 76 { NULL, EXTRACTOR_METATYPE_RESERVED } 77 }; 78 79 80 /** 81 * Global handle to MAGIC data. 82 */ 83 static magic_t magic; 84 85 86 /** 87 * Map 'meta' tag to LE type. 88 * 89 * @param tag tag to map 90 * @return EXTRACTOR_METATYPE_RESERVED if the type was not found 91 */ 92 static enum EXTRACTOR_MetaType 93 tag_to_type (const char *tag) 94 { 95 unsigned int i; 96 97 for (i = 0; NULL != tagmap[i].name; i++) 98 if (0 == strcasecmp (tag, 99 tagmap[i].name)) 100 return tagmap[i].type; 101 return EXTRACTOR_METATYPE_RESERVED; 102 } 103 104 105 /** 106 * Function called by libtidy for error reporting. 107 * 108 * @param doc tidy doc being processed 109 * @param lvl report level 110 * @param line input line 111 * @param col input column 112 * @param mssg message 113 * @return FALSE (no output) 114 */ 115 static Bool TIDY_CALL 116 report_cb (TidyDoc doc, 117 TidyReportLevel lvl, 118 uint line, 119 uint col, 120 ctmbstr mssg) 121 { 122 return 0; 123 } 124 125 126 /** 127 * Input callback: get next byte of input. 128 * 129 * @param sourceData our 'struct EXTRACTOR_ExtractContext' 130 * @return next byte of input, EndOfStream on errors and EOF 131 */ 132 static int TIDY_CALL 133 get_byte_cb (void *sourceData) 134 { 135 struct EXTRACTOR_ExtractContext *ec = sourceData; 136 void *data; 137 138 if (1 != 139 ec->read (ec->cls, 140 &data, 1)) 141 return EndOfStream; 142 return *(unsigned char*) data; 143 } 144 145 146 /** 147 * Input callback: unget last byte of input. 148 * 149 * @param sourceData our 'struct EXTRACTOR_ExtractContext' 150 * @param bt byte to unget (ignored) 151 */ 152 static void TIDY_CALL 153 unget_byte_cb (void *sourceData, byte bt) 154 { 155 struct EXTRACTOR_ExtractContext *ec = sourceData; 156 157 (void) ec->seek (ec->cls, -1, SEEK_CUR); 158 } 159 160 161 /** 162 * Input callback: check for EOF. 163 * 164 * @param sourceData our 'struct EXTRACTOR_ExtractContext' 165 * @return true if we are at the EOF 166 */ 167 static Bool TIDY_CALL 168 eof_cb (void *sourceData) 169 { 170 struct EXTRACTOR_ExtractContext *ec = sourceData; 171 172 return ec->seek (ec->cls, 0, SEEK_CUR) == ec->get_size (ec->cls); 173 } 174 175 176 /** 177 * Main entry method for the 'text/html' extraction plugin. 178 * 179 * @param ec extraction context provided to the plugin 180 */ 181 void 182 EXTRACTOR_html_extract_method (struct EXTRACTOR_ExtractContext *ec); 183 184 void 185 EXTRACTOR_html_extract_method (struct EXTRACTOR_ExtractContext *ec) 186 { 187 TidyDoc doc; 188 TidyNode head; 189 TidyNode child; 190 TidyNode title; 191 TidyInputSource src; 192 const char *name; 193 TidyBuffer tbuf; 194 TidyAttr attr; 195 enum EXTRACTOR_MetaType type; 196 ssize_t iret; 197 void *data; 198 const char *mime; 199 200 if (-1 == (iret = ec->read (ec->cls, 201 &data, 202 16 * 1024))) 203 return; 204 if (NULL == (mime = magic_buffer (magic, data, iret))) 205 return; 206 if (0 != strncmp (mime, 207 "text/html", 208 strlen ("text/html"))) 209 return; /* not HTML */ 210 211 if (0 != ec->seek (ec->cls, 0, SEEK_SET)) 212 return; /* seek failed !? */ 213 214 tidyInitSource (&src, ec, 215 &get_byte_cb, 216 &unget_byte_cb, 217 &eof_cb); 218 if (NULL == (doc = tidyCreate ())) 219 return; 220 tidySetReportFilter (doc, &report_cb); 221 tidySetAppData (doc, ec); 222 if (0 > tidyParseSource (doc, &src)) 223 { 224 tidyRelease (doc); 225 return; 226 } 227 if (1 != tidyStatus (doc)) 228 { 229 tidyRelease (doc); 230 return; 231 } 232 if (NULL == (head = tidyGetHead (doc))) 233 { 234 tidyRelease (doc); 235 return; 236 } 237 for (child = tidyGetChild (head); NULL != child; child = tidyGetNext (child)) 238 { 239 switch (tidyNodeGetType (child)) 240 { 241 case TidyNode_Root: 242 break; 243 case TidyNode_DocType: 244 break; 245 case TidyNode_Comment: 246 break; 247 case TidyNode_ProcIns: 248 break; 249 case TidyNode_Text: 250 break; 251 case TidyNode_CDATA: 252 break; 253 case TidyNode_Section: 254 break; 255 case TidyNode_Asp: 256 break; 257 case TidyNode_Jste: 258 break; 259 case TidyNode_Php: 260 break; 261 case TidyNode_XmlDecl: 262 break; 263 case TidyNode_Start: 264 case TidyNode_StartEnd: 265 name = tidyNodeGetName (child); 266 if (NULL == name) 267 break; 268 if ( (0 == strcasecmp (name, "title")) && 269 (NULL != (title = tidyGetChild (child))) ) 270 { 271 tidyBufInit (&tbuf); 272 tidyNodeGetValue (doc, title, &tbuf); 273 /* add 0-termination */ 274 tidyBufPutByte (&tbuf, 0); 275 if (0 != 276 ec->proc (ec->cls, 277 "html", 278 EXTRACTOR_METATYPE_TITLE, 279 EXTRACTOR_METAFORMAT_UTF8, 280 "text/plain", 281 (const char *) tbuf.bp, 282 tbuf.size)) 283 { 284 tidyBufFree (&tbuf); 285 goto CLEANUP; 286 } 287 tidyBufFree (&tbuf); 288 break; 289 } 290 if (0 == strcasecmp (name, "meta")) 291 { 292 if (NULL == (attr = tidyAttrGetById (child, 293 TidyAttr_NAME))) 294 break; 295 name = tidyAttrValue (attr); 296 if (NULL == name) 297 break; 298 if (EXTRACTOR_METATYPE_RESERVED == 299 (type = tag_to_type (name))) 300 break; 301 if (NULL == (attr = tidyAttrGetById (child, 302 TidyAttr_CONTENT))) 303 break; 304 name = tidyAttrValue (attr); 305 if (NULL == name) 306 break; 307 if (0 != 308 ec->proc (ec->cls, 309 "html", 310 type, 311 EXTRACTOR_METAFORMAT_UTF8, 312 "text/plain", 313 name, 314 strlen (name) + 1)) 315 goto CLEANUP; 316 break; 317 } 318 break; 319 case TidyNode_End: 320 break; 321 default: 322 break; 323 } 324 } 325 CLEANUP: 326 tidyRelease (doc); 327 } 328 329 330 /** 331 * Initialize glib and load magic file. 332 */ 333 void __attribute__ ((constructor)) 334 html_gobject_init (void); 335 336 void __attribute__ ((constructor)) 337 html_gobject_init () 338 { 339 magic = magic_open (MAGIC_MIME_TYPE); 340 if (0 != magic_load (magic, NULL)) 341 { 342 /* FIXME: how to deal with errors? */ 343 } 344 } 345 346 347 /** 348 * Destructor for the library, cleans up. 349 */ 350 void __attribute__ ((destructor)) 351 html_ltdl_fini (void); 352 353 void __attribute__ ((destructor)) 354 html_ltdl_fini () 355 { 356 if (NULL != magic) 357 { 358 magic_close (magic); 359 magic = NULL; 360 } 361 } 362 363 364 /* end of html_extractor.c */