Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright 2011 John-Mark Bell <jmb@netsurf-browser.org>
  3.  *
  4.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  5.  *
  6.  * NetSurf is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License as published by
  8.  * the Free Software Foundation; version 2 of the License.
  9.  *
  10.  * NetSurf is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17.  */
  18.  
  19. /** \file
  20.  * MIME type sniffer (implementation)
  21.  *
  22.  * Spec version: 2011-11-27
  23.  */
  24.  
  25. #include<string.h>
  26.  
  27. #include "content/content_factory.h"
  28. #include "content/llcache.h"
  29. #include "content/mimesniff.h"
  30. #include "utils/http.h"
  31. #include "utils/utils.h"
  32.  
  33. struct map_s {
  34.         const uint8_t *sig;
  35.         size_t len;
  36.         bool safe;
  37.         lwc_string **type;
  38. };
  39.  
  40. static lwc_string *unknown_unknown;
  41. static lwc_string *application_unknown;
  42. static lwc_string *any;
  43. static lwc_string *text_xml;
  44. static lwc_string *application_xml;
  45. static lwc_string *text_html;
  46. static lwc_string *text_plain;
  47. static lwc_string *application_octet_stream;
  48. static lwc_string *image_gif;
  49. static lwc_string *image_png;
  50. static lwc_string *image_jpeg;
  51. static lwc_string *image_bmp;
  52. static lwc_string *image_vnd_microsoft_icon;
  53. static lwc_string *image_webp;
  54. static lwc_string *application_rss_xml;
  55. static lwc_string *application_atom_xml;
  56. static lwc_string *audio_wave;
  57. static lwc_string *application_ogg;
  58. static lwc_string *video_webm;
  59. static lwc_string *application_x_rar_compressed;
  60. static lwc_string *application_zip;
  61. static lwc_string *application_x_gzip;
  62. static lwc_string *application_postscript;
  63. static lwc_string *application_pdf;
  64. static lwc_string *video_mp4;
  65. static lwc_string *image_svg;
  66.  
  67. nserror mimesniff_init(void)
  68. {
  69.         lwc_error lerror;
  70.  
  71. #define SINIT(v, s) \
  72.         lerror = lwc_intern_string(s, SLEN(s), &v); \
  73.         if (lerror != lwc_error_ok) \
  74.                 return NSERROR_NOMEM
  75.  
  76.         SINIT(unknown_unknown,              "unknown/unknown");
  77.         SINIT(application_unknown,          "application/unknown");
  78.         SINIT(any,                          "*/*");
  79.         SINIT(text_xml,                     "text/xml");
  80.         SINIT(application_xml,              "application/xml");
  81.         SINIT(text_html,                    "text/html");
  82.         SINIT(text_plain,                   "text/plain");
  83.         SINIT(application_octet_stream,     "application/octet-stream");
  84.         SINIT(image_gif,                    "image/gif");
  85.         SINIT(image_png,                    "image/png");
  86.         SINIT(image_jpeg,                   "image/jpeg");
  87.         SINIT(image_bmp,                    "image/bmp");
  88.         SINIT(image_vnd_microsoft_icon,     "image/vnd.microsoft.icon");
  89.         SINIT(image_webp,                   "image/webp");
  90.         SINIT(application_rss_xml,          "application/rss+xml");
  91.         SINIT(application_atom_xml,         "application/atom+xml");
  92.         SINIT(audio_wave,                   "audio/wave");
  93.         SINIT(application_ogg,              "application/ogg");
  94.         SINIT(video_webm,                   "video/webm");
  95.         SINIT(application_x_rar_compressed, "application/x-rar-compressed");
  96.         SINIT(application_zip,              "application/zip");
  97.         SINIT(application_x_gzip,           "application/x-gzip");
  98.         SINIT(application_postscript,       "application/postscript");
  99.         SINIT(application_pdf,              "application/pdf");
  100.         SINIT(video_mp4,                    "video/mp4");
  101.         SINIT(image_svg,                    "image/svg+xml");
  102. #undef SINIT
  103.  
  104.         return NSERROR_OK;
  105. }
  106.  
  107. void mimesniff_fini(void)
  108. {
  109.         lwc_string_unref(image_svg);
  110.         lwc_string_unref(video_mp4);
  111.         lwc_string_unref(application_pdf);
  112.         lwc_string_unref(application_postscript);
  113.         lwc_string_unref(application_x_gzip);
  114.         lwc_string_unref(application_zip);
  115.         lwc_string_unref(application_x_rar_compressed);
  116.         lwc_string_unref(video_webm);
  117.         lwc_string_unref(application_ogg);
  118.         lwc_string_unref(audio_wave);
  119.         lwc_string_unref(application_atom_xml);
  120.         lwc_string_unref(application_rss_xml);
  121.         lwc_string_unref(image_webp);
  122.         lwc_string_unref(image_vnd_microsoft_icon);
  123.         lwc_string_unref(image_bmp);
  124.         lwc_string_unref(image_jpeg);
  125.         lwc_string_unref(image_png);
  126.         lwc_string_unref(image_gif);
  127.         lwc_string_unref(application_octet_stream);
  128.         lwc_string_unref(text_plain);
  129.         lwc_string_unref(text_html);
  130.         lwc_string_unref(application_xml);
  131.         lwc_string_unref(text_xml);
  132.         lwc_string_unref(any);
  133.         lwc_string_unref(application_unknown);
  134.         lwc_string_unref(unknown_unknown);
  135. }
  136.  
  137. static bool mimesniff__has_binary_octets(const uint8_t *data, size_t len)
  138. {
  139.         const uint8_t *end = data + len;
  140.  
  141.         while (data != end) {
  142.                 const uint8_t c = *data;
  143.  
  144.                 /* Binary iff in C0 and not ESC, CR, FF, LF, HT */
  145.                 if (c <= 0x1f && c != 0x1b && c != '\r' && c != '\f' &&
  146.                                 c != '\n' && c != '\t')
  147.                         break;
  148.  
  149.                 data++;
  150.         }
  151.  
  152.         return data != end;
  153. }
  154.  
  155. static nserror mimesniff__match_mp4(const uint8_t *data, size_t len,
  156.                 lwc_string **effective_type)
  157. {
  158.         size_t box_size, i;
  159.  
  160.         /* ISO/IEC 14496-12:2008 $4.3 says (effectively):
  161.          *
  162.          * struct ftyp_box {
  163.          *   uint32_t size; (in octets, including size+type words)
  164.          *   uint32_t type; (== 'ftyp')
  165.          *   uint32_t major_brand;
  166.          *   uint32_t minor_version;
  167.          *   uint32_t compatible_brands[];
  168.          * }
  169.          *
  170.          * Note 1: A size of 0 implies that the length of the box is designated
  171.          * by the remaining input data (and thus may only occur in the last
  172.          * box in the input). We'll reject this below, as it's pointless
  173.          * sniffing input that contains no boxes other than 'ftyp'.
  174.          *
  175.          * Note 2: A size of 1 implies an additional uint64_t field after
  176.          * the type which contains the extended box size. We'll reject this,
  177.          * too, as it implies a minimum of (2^32 - 24) / 4 compatible brands,
  178.          * which is decidely unlikely.
  179.          */
  180.  
  181.         /* 12 reflects the minimum number of octets needed to sniff useful
  182.          * information out of an 'ftyp' box (i.e. the size, type,
  183.          * and major_brand words). */
  184.         if (len < 12)
  185.                 return NSERROR_NOT_FOUND;
  186.  
  187.         /* Box size is big-endian */
  188.         box_size = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
  189.  
  190.         /* Require that we can read the entire box, and reject bad box sizes */
  191.         if (len < box_size || box_size % 4 != 0)
  192.                 return NSERROR_NOT_FOUND;
  193.  
  194.         /* Ensure this is an 'ftyp' box */
  195.         if (data[4] != 'f' || data[5] != 't' ||
  196.                         data[6] != 'y' || data[7] != 'p')
  197.                 return NSERROR_NOT_FOUND;
  198.  
  199.         /* Check if major brand begins with 'mp4' */
  200.         if (data[8] == 'm' && data[9] == 'p' && data[10] == '4') {
  201.                 *effective_type = lwc_string_ref(video_mp4);
  202.                 return NSERROR_OK;
  203.         }
  204.  
  205.         /* Search each compatible brand in the box for "mp4" */
  206.         for (i = 16; i <= box_size - 4; i += 4) {
  207.                 if (data[i] == 'm' && data[i+1] == 'p' && data[i+2] == '4') {
  208.                         *effective_type = lwc_string_ref(video_mp4);
  209.                         return NSERROR_OK;
  210.                 }
  211.         }
  212.  
  213.         return NSERROR_NOT_FOUND;
  214. }
  215.  
  216. static nserror mimesniff__match_unknown_ws(const uint8_t *data, size_t len,
  217.                 lwc_string **effective_type)
  218. {
  219. #define SIG(t, s, x) { (const uint8_t *) s, SLEN(s), x, t }
  220.         static const struct map_s ws_exact_match_types[] = {
  221.                 SIG(&text_xml, "<?xml", false),
  222.                 { NULL, 0, false, NULL }
  223.         };
  224.  
  225.         static const struct map_s ws_inexact_match_types[] = {
  226.                 SIG(&text_html, "<!DOCTYPE HTML", false),
  227.                 SIG(&text_html, "<HTML",          false),
  228.                 SIG(&text_html, "<HEAD",          false),
  229.                 SIG(&text_html, "<SCRIPT",        false),
  230.                 SIG(&text_html, "<IFRAME",        false),
  231.                 SIG(&text_html, "<H1",            false),
  232.                 SIG(&text_html, "<DIV",           false),
  233.                 SIG(&text_html, "<FONT",          false),
  234.                 SIG(&text_html, "<TABLE",         false),
  235.                 SIG(&text_html, "<A",             false),
  236.                 SIG(&text_html, "<STYLE",         false),
  237.                 SIG(&text_html, "<TITLE",         false),
  238.                 SIG(&text_html, "<B",             false),
  239.                 SIG(&text_html, "<BODY",          false),
  240.                 SIG(&text_html, "<BR",            false),
  241.                 SIG(&text_html, "<P",             false),
  242.                 SIG(&text_html, "<!--",           false),
  243.                 { NULL, 0, false, NULL }
  244.         };
  245. #undef SIG
  246.         const uint8_t *end = data + len;
  247.         const struct map_s *it;
  248.  
  249.         /* Skip leading whitespace */
  250.         while (data != end) {
  251.                 const uint8_t c = *data;
  252.  
  253.                 if (c != '\t' && c != '\n' && c != '\f' &&
  254.                                 c != '\r' && c != ' ')
  255.                         break;
  256.  
  257.                 data++;
  258.         }
  259.  
  260.         if (data == end)
  261.                 return NSERROR_NOT_FOUND;
  262.  
  263.         len = end - data;
  264.  
  265.         for (it = ws_exact_match_types; it->sig != NULL; it++) {
  266.                 if (it->len <= len && memcmp(data, it->sig, it->len) == 0) {
  267.                         *effective_type = lwc_string_ref(*it->type);
  268.                         return NSERROR_OK;
  269.                 }
  270.         }
  271.  
  272.         for (it = ws_inexact_match_types; it->sig != NULL; it++) {
  273.                 /* +1 for trailing space or > */
  274.                 if (len < it->len + 1)
  275.                         continue;
  276.  
  277.                 if (strncasecmp((const char *) data,
  278.                                 (const char *) it->sig, it->len) == 0 &&
  279.                                 (data[it->len] == ' ' ||
  280.                                 data[it->len] == '>')) {
  281.                         *effective_type = lwc_string_ref(*it->type);
  282.                         return NSERROR_OK;
  283.                 }
  284.         }
  285.  
  286.         return NSERROR_NOT_FOUND;
  287. }
  288.  
  289. static nserror mimesniff__match_unknown_bom(const uint8_t *data, size_t len,
  290.                 lwc_string **effective_type)
  291. {
  292. #define SIG(t, s, x) { (const uint8_t *) s, SLEN(s), x, t }
  293.         static const struct map_s bom_match_types[] = {
  294.                 SIG(&text_plain, "\xfe\xff",     false),
  295.                 SIG(&text_plain, "\xff\xfe",     false),
  296.                 SIG(&text_plain, "\xef\xbb\xbf", false),
  297.                 { NULL, 0, false, NULL }
  298.         };
  299. #undef SIG
  300.         const struct map_s *it;
  301.  
  302.         for (it = bom_match_types; it->sig != NULL; it++) {
  303.                 if (it->len <= len && memcmp(data, it->sig, it->len) == 0) {
  304.                         *effective_type = lwc_string_ref(*it->type);
  305.                         return NSERROR_OK;
  306.                 }
  307.         }
  308.  
  309.         return NSERROR_NOT_FOUND;
  310. }
  311.  
  312. static nserror mimesniff__match_unknown_riff(const uint8_t *data, size_t len,
  313.                 lwc_string **effective_type)
  314. {
  315. #define SIG(t, s, x) { (const uint8_t *) s, SLEN(s), x, t }
  316.         static const struct map_s riff_match_types[] = {
  317.                 SIG(&image_webp, "WEBPVP", true),
  318.                 SIG(&audio_wave, "WAVE",   true),
  319.                 { NULL, 0, false, NULL }
  320.         };
  321. #undef SIG
  322.         const struct map_s *it;
  323.  
  324.         for (it = riff_match_types; it->sig != NULL; it++) {
  325.                 if (it->len + SLEN("RIFF????") <= len &&
  326.                                 memcmp(data, "RIFF", SLEN("RIFF")) == 0 &&
  327.                                 memcmp(data + SLEN("RIFF????"),
  328.                                                 it->sig, it->len) == 0) {
  329.                         *effective_type = lwc_string_ref(*it->type);
  330.                         return NSERROR_OK;
  331.                 }
  332.         }
  333.  
  334.         return NSERROR_NOT_FOUND;
  335. }
  336.  
  337. static nserror mimesniff__match_unknown_exact(const uint8_t *data, size_t len,
  338.                 bool allow_unsafe, lwc_string **effective_type)
  339. {
  340. #define SIG(t, s, x) { (const uint8_t *) s, SLEN(s), x, t }
  341.         static const struct map_s exact_match_types[] = {
  342.                 SIG(&image_gif,                    "GIF87a",            true),
  343.                 SIG(&image_gif,                    "GIF89a",            true),
  344.                 SIG(&image_png,                    "\x89PNG\r\n\x1a\n", true),
  345.                 SIG(&image_jpeg,                   "\xff\xd8\xff",      true),
  346.                 SIG(&image_bmp,                    "BM",                true),
  347.                 SIG(&image_vnd_microsoft_icon,     "\x00\x00\x01\x00",  true),
  348.                 SIG(&application_ogg,              "OggS\x00",          true),
  349.                 SIG(&video_webm,                   "\x1a\x45\xdf\xa3",  true),
  350.                 SIG(&application_x_rar_compressed, "Rar \x1a\x07\x00",  true),
  351.                 SIG(&application_zip,              "PK\x03\x04",        true),
  352.                 SIG(&application_x_gzip,           "\x1f\x8b\x08",      true),
  353.                 SIG(&application_postscript,       "%!PS-Adobe-",       true),
  354.                 SIG(&application_pdf,              "%PDF-",             false),
  355.                 { NULL, 0, false, NULL }
  356.         };
  357. #undef SIG
  358.         const struct map_s *it;
  359.  
  360.         for (it = exact_match_types; it->sig != NULL; it++) {
  361.                 if (it->len <= len && memcmp(data, it->sig, it->len) == 0 &&
  362.                                 (allow_unsafe || it->safe)) {
  363.                         *effective_type = lwc_string_ref(*it->type);
  364.                         return NSERROR_OK;
  365.                 }
  366.         }
  367.  
  368.         return NSERROR_NOT_FOUND;
  369. }
  370.  
  371. static nserror mimesniff__match_unknown(const uint8_t *data, size_t len,
  372.                 bool allow_unsafe, lwc_string **effective_type)
  373. {
  374.         if (mimesniff__match_unknown_exact(data, len, allow_unsafe,
  375.                         effective_type) == NSERROR_OK)
  376.                 return NSERROR_OK;
  377.  
  378.         if (mimesniff__match_unknown_riff(data, len,
  379.                         effective_type) == NSERROR_OK)
  380.                 return NSERROR_OK;
  381.  
  382.         if (allow_unsafe == false)
  383.                 return NSERROR_NOT_FOUND;
  384.  
  385.         if (mimesniff__match_unknown_bom(data, len,
  386.                         effective_type) == NSERROR_OK)
  387.                 return NSERROR_OK;
  388.  
  389.         if (mimesniff__match_unknown_ws(data, len,
  390.                         effective_type) == NSERROR_OK)
  391.                 return NSERROR_OK;
  392.  
  393.         if (mimesniff__match_mp4(data, len, effective_type) == NSERROR_OK)
  394.                 return NSERROR_OK;
  395.  
  396.         return NSERROR_NOT_FOUND;
  397. }
  398.  
  399. static nserror mimesniff__compute_unknown(const uint8_t *data, size_t len,
  400.                 lwc_string **effective_type)
  401. {
  402.         if (data == NULL)
  403.                 return NSERROR_NEED_DATA;
  404.  
  405.         len = min(len, 512);
  406.  
  407.         if (mimesniff__match_unknown(data, len, true,
  408.                         effective_type) == NSERROR_OK)
  409.                 return NSERROR_OK;
  410.  
  411.         if (mimesniff__has_binary_octets(data, len) == false) {
  412.                 /* No binary octets => text/plain */
  413.                 *effective_type = lwc_string_ref(text_plain);
  414.                 return NSERROR_OK;
  415.         }
  416.  
  417.         *effective_type = lwc_string_ref(application_octet_stream);
  418.  
  419.         return NSERROR_OK;
  420. }
  421.  
  422. static nserror mimesniff__compute_text_or_binary(const uint8_t *data,
  423.                 size_t len, lwc_string **effective_type)
  424. {
  425.         if (data == NULL)
  426.                 return NSERROR_NEED_DATA;
  427.  
  428.         len = min(len, 512);
  429.  
  430.         if (len >= 3 && ((data[0] == 0xfe && data[1] == 0xff) ||
  431.                         (data[0] == 0xff && data[1] == 0xfe) ||
  432.                         (data[0] == 0xef && data[1] == 0xbb &&
  433.                                 data[2] == 0xbf))) {
  434.                 /* Found a BOM => text/plain */
  435.                 *effective_type = lwc_string_ref(text_plain);
  436.                 return NSERROR_OK;
  437.         }
  438.  
  439.         if (mimesniff__has_binary_octets(data, len) == false) {
  440.                 /* No binary octets => text/plain */
  441.                 *effective_type = lwc_string_ref(text_plain);
  442.                 return NSERROR_OK;
  443.         }
  444.  
  445.         if (mimesniff__match_unknown(data, len, false,
  446.                         effective_type) == NSERROR_OK)
  447.                 return NSERROR_OK;
  448.  
  449.         *effective_type = lwc_string_ref(application_octet_stream);
  450.  
  451.         return NSERROR_OK;
  452. }
  453.  
  454. static nserror mimesniff__compute_image(lwc_string *official_type,
  455.                 const uint8_t *data, size_t len, lwc_string **effective_type)
  456. {
  457. #define SIG(t, s) { (const uint8_t *) s, SLEN(s), t }
  458.         static const struct it_s {
  459.                 const uint8_t *sig;
  460.                 size_t len;
  461.                 lwc_string **type;
  462.         } image_types[] = {
  463.                 SIG(&image_gif,                "GIF87a"),
  464.                 SIG(&image_gif,                "GIF89a"),
  465.                 SIG(&image_png,                "\x89PNG\r\n\x1a\n"),
  466.                 SIG(&image_jpeg,               "\xff\xd8\xff"),
  467.                 SIG(&image_bmp,                "BM"),
  468.                 SIG(&image_vnd_microsoft_icon, "\x00\x00\x01\x00"),
  469.                 { NULL, 0, NULL }
  470.         };
  471. #undef SIG
  472.  
  473.         const struct it_s *it;
  474.  
  475.         if (data == NULL) {
  476.                 lwc_string_unref(official_type);
  477.                 return NSERROR_NEED_DATA;
  478.         }
  479.  
  480.         for (it = image_types; it->sig != NULL; it++) {
  481.                 if (it->len <= len && memcmp(data, it->sig, it->len) == 0) {
  482.                         lwc_string_unref(official_type);
  483.                         *effective_type = lwc_string_ref(*it->type);
  484.                         return NSERROR_OK;
  485.                 }
  486.         }
  487.  
  488.         /* WebP has a signature that doesn't fit into the above table */
  489.         if (SLEN("RIFF????WEBPVP") <= len &&
  490.                         memcmp(data, "RIFF", SLEN("RIFF")) == 0 &&
  491.                         memcmp(data + SLEN("RIFF????"),
  492.                                         "WEBPVP", SLEN("WEBPVP")) == 0 ) {
  493.                 lwc_string_unref(official_type);
  494.                 *effective_type = lwc_string_ref(image_webp);
  495.                 return NSERROR_OK;
  496.         }
  497.  
  498.         *effective_type = official_type;
  499.  
  500.         return NSERROR_OK;
  501. }
  502.  
  503. static nserror mimesniff__compute_feed_or_html(const uint8_t *data,
  504.                 size_t len, lwc_string **effective_type)
  505. {
  506. #define RDF_NS "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  507. #define RSS_NS "http://purl.org/rss/1.0"
  508.  
  509.         enum state_e {
  510.                 BEFORE_BOM,
  511.                 BEFORE_MARKUP,
  512.                 MARKUP_START,
  513.                 COMMENT_OR_DOCTYPE,
  514.                 IN_COMMENT,
  515.                 IN_DOCTYPE,
  516.                 IN_PI,
  517.                 IN_TAG,
  518.                 IN_RDF
  519.         } state = BEFORE_BOM;
  520.  
  521.         bool rdf = false, rss = false;
  522.         const uint8_t *end;
  523.  
  524.         if (data == NULL)
  525.                 return NSERROR_NEED_DATA;
  526.  
  527.         end = data + min(len, 512);
  528.  
  529.         while (data < end) {
  530.                 const uint8_t c = *data;
  531.  
  532. #define MATCH(s) SLEN(s) <= (size_t) (end - data) && \
  533.                         memcmp(data, s, SLEN(s)) == 0
  534.  
  535.                 switch (state) {
  536.                 case BEFORE_BOM:
  537.                         if (3 <= end - data && c == 0xef && data[1] == 0xbb &&
  538.                                         data[2] == 0xbf) {
  539.                                 data += 3;
  540.                         }
  541.  
  542.                         state = BEFORE_MARKUP;
  543.                         break;
  544.                 case BEFORE_MARKUP:
  545.                         if (c == '\t' || c == '\n' || c == '\r' || c == ' ')
  546.                                 data++;
  547.                         else if (c != '<')
  548.                                 data = end;
  549.                         else {
  550.                                 state = MARKUP_START;
  551.                                 data++;
  552.                         }
  553.                         break;
  554.                 case MARKUP_START:
  555.                         if (c == '!') {
  556.                                 state = COMMENT_OR_DOCTYPE;
  557.                                 data++;
  558.                         } else if (c == '?') {
  559.                                 state = IN_PI;
  560.                                 data++;
  561.                         } else {
  562.                                 /* Reconsume input */
  563.                                 state = IN_TAG;
  564.                         }
  565.                         break;
  566.                 case COMMENT_OR_DOCTYPE:
  567.                         if (2 <= end - data && c == '-' && data[1] == '-') {
  568.                                 state = IN_COMMENT;
  569.                                 data += 2;
  570.                         } else {
  571.                                 /* Reconsume input */
  572.                                 state = IN_DOCTYPE;
  573.                         }
  574.                         break;
  575.                 case IN_COMMENT:
  576.                         if (3 <= end - data && c == '-' && data[1] == '-' &&
  577.                                         data[2] == '>') {
  578.                                 state = BEFORE_MARKUP;
  579.                                 data += 3;
  580.                         } else
  581.                                 data++;
  582.                         break;
  583.                 case IN_DOCTYPE:
  584.                         if (c == '>')
  585.                                 state = BEFORE_MARKUP;
  586.                         data++;
  587.                         break;
  588.                 case IN_PI:
  589.                         if (2 <= end - data && c == '?' && data[1] == '>') {
  590.                                 state = BEFORE_MARKUP;
  591.                                 data += 2;
  592.                         } else
  593.                                 data++;
  594.                         break;
  595.                 case IN_TAG:
  596.                         if (MATCH("rss")) {
  597.                                 *effective_type =
  598.                                         lwc_string_ref(application_rss_xml);
  599.                                 return NSERROR_OK;
  600.                         } else if (MATCH("feed")) {
  601.                                 *effective_type =
  602.                                         lwc_string_ref(application_atom_xml);
  603.                                 return NSERROR_OK;
  604.                         } else if (MATCH("rdf:RDF")) {
  605.                                 state = IN_RDF;
  606.                                 data += SLEN("rdf:RDF");
  607.                         } else
  608.                                 data = end;
  609.                         break;
  610.                 case IN_RDF:
  611.                         if (MATCH(RSS_NS)) {
  612.                                 rss = true;
  613.                                 data += SLEN(RSS_NS);
  614.                         } else if (MATCH(RDF_NS)) {
  615.                                 rdf = true;
  616.                                 data += SLEN(RDF_NS);
  617.                         } else
  618.                                 data++;
  619.  
  620.                         if (rdf && rss) {
  621.                                 *effective_type =
  622.                                         lwc_string_ref(application_rss_xml);
  623.                                 return NSERROR_OK;
  624.                         }
  625.  
  626.                         break;
  627.                 }
  628. #undef MATCH
  629.         }
  630.  
  631.         *effective_type = lwc_string_ref(text_html);
  632.  
  633.         return NSERROR_OK;
  634.  
  635. #undef RSS_NS
  636. #undef RDF_NS
  637. }
  638.  
  639. /* See mimesniff.h for documentation */
  640. nserror mimesniff_compute_effective_type(llcache_handle *handle,
  641.                 const uint8_t *data, size_t len, bool sniff_allowed,
  642.                 bool image_only, lwc_string **effective_type)
  643. {
  644. #define S(s) { s, SLEN(s) }
  645.         static const struct tt_s {
  646.                 const char *data;
  647.                 size_t len;
  648.         } text_types[] = {
  649.                 S("text/plain"),
  650.                 S("text/plain; charset=ISO-8859-1"),
  651.                 S("text/plain; charset=iso-8859-1"),
  652.                 S("text/plain; charset=UTF-8"),
  653.                 { NULL, 0 }
  654.         };
  655. #undef S
  656.  
  657.         const char *content_type_header;
  658.         size_t content_type_header_len;
  659.         http_content_type *ct;
  660.         const struct tt_s *tt;
  661.         bool match;
  662.         nserror error;
  663.  
  664.         content_type_header =
  665.                         llcache_handle_get_header(handle, "Content-Type");
  666.         if (content_type_header == NULL) {
  667.                 if (sniff_allowed == false)
  668.                         return NSERROR_NOT_FOUND;
  669.  
  670.                 /* No official type => unknown */
  671.                 return mimesniff__compute_unknown(data, len, effective_type);
  672.         }
  673.  
  674.         error = http_parse_content_type(content_type_header, &ct);
  675.         if (error != NSERROR_OK) {
  676.                 if (sniff_allowed == false)
  677.                         return NSERROR_NOT_FOUND;
  678.  
  679.                 /* Unparseable => unknown */
  680.                 return mimesniff__compute_unknown(data, len, effective_type);
  681.         }
  682.  
  683.         if (sniff_allowed == false) {
  684.                 *effective_type = lwc_string_ref(ct->media_type);
  685.                 http_content_type_destroy(ct);
  686.                 return NSERROR_OK;
  687.         }
  688.  
  689.         if (image_only) {
  690.                 lwc_string *official_type;
  691.  
  692.                 if (lwc_string_caseless_isequal(ct->media_type, image_svg,
  693.                                 &match) == lwc_error_ok && match) {
  694.                         *effective_type = lwc_string_ref(image_svg);
  695.                         http_content_type_destroy(ct);
  696.                         return NSERROR_OK;
  697.                 }
  698.  
  699.                 official_type = lwc_string_ref(ct->media_type);
  700.                 http_content_type_destroy(ct);
  701.                 return mimesniff__compute_image(official_type,
  702.                                 data, len, effective_type);
  703.         }
  704.  
  705.         content_type_header_len = strlen(content_type_header);
  706.  
  707.         /* Look for text types */
  708.         for (tt = text_types; tt->data != NULL; tt++) {
  709.                 if (tt->len == content_type_header_len &&
  710.                                 memcmp(tt->data, content_type_header,
  711.                                         content_type_header_len) == 0) {
  712.                         http_content_type_destroy(ct);
  713.                         return mimesniff__compute_text_or_binary(data, len,
  714.                                         effective_type);
  715.                 }
  716.         }
  717.  
  718.         /* unknown/unknown, application/unknown, * / * */
  719.         if ((lwc_string_caseless_isequal(ct->media_type, unknown_unknown,
  720.                                 &match) == lwc_error_ok && match) ||
  721.                         (lwc_string_caseless_isequal(ct->media_type,
  722.                                 application_unknown, &match) == lwc_error_ok &&
  723.                                 match) ||
  724.                         (lwc_string_caseless_isequal(ct->media_type, any,
  725.                                 &match) == lwc_error_ok && match)) {
  726.                 http_content_type_destroy(ct);
  727.                 return mimesniff__compute_unknown(data, len, effective_type);
  728.         }
  729.  
  730.         /* +xml */
  731.         if (lwc_string_length(ct->media_type) > SLEN("+xml") &&
  732.                         strncasecmp(lwc_string_data(ct->media_type) +
  733.                                 lwc_string_length(ct->media_type) -
  734.                                 SLEN("+xml"),
  735.                                 "+xml", SLEN("+xml")) == 0) {
  736.                 /* Use official type */
  737.                 *effective_type = lwc_string_ref(ct->media_type);
  738.                 http_content_type_destroy(ct);
  739.                 return NSERROR_OK;
  740.         }
  741.  
  742.         /* text/xml, application/xml */
  743.         if ((lwc_string_caseless_isequal(ct->media_type, text_xml,
  744.                                 &match) == lwc_error_ok && match) ||
  745.                         (lwc_string_caseless_isequal(ct->media_type,
  746.                                 application_xml, &match) == lwc_error_ok &&
  747.                                 match)) {
  748.                 /* Use official type */
  749.                 *effective_type = lwc_string_ref(ct->media_type);
  750.                 http_content_type_destroy(ct);
  751.                 return NSERROR_OK;
  752.         }
  753.        
  754.         /* Image types */
  755.         if (content_factory_type_from_mime_type(ct->media_type) ==
  756.                         CONTENT_IMAGE) {
  757.                 lwc_string *official_type = lwc_string_ref(ct->media_type);
  758.                 http_content_type_destroy(ct);
  759.                 return mimesniff__compute_image(official_type,
  760.                                 data, len, effective_type);
  761.         }
  762.  
  763.         /* text/html */
  764.         if ((lwc_string_caseless_isequal(ct->media_type, text_html,
  765.                         &match) == lwc_error_ok && match)) {
  766.                 http_content_type_destroy(ct);
  767.                 return mimesniff__compute_feed_or_html(data, len,
  768.                                 effective_type);
  769.         }
  770.  
  771.         /* Use official type */
  772.         *effective_type = lwc_string_ref(ct->media_type);
  773.  
  774.         http_content_type_destroy(ct);
  775.  
  776.         return NSERROR_OK;
  777. }
  778.  
  779.