blob: 987c66cb5663dfe6470bf0bfa90b853563fd2411 [file] [log] [blame] [raw]
/*
Copyright: Boaz Segev, 2016-2017
License: MIT
Feel free to copy, use and enjoy according to the license provided.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "base64.h"
#include "http.h"
#include "http1_response.h"
#include "siphash.h"
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
/* *****************************************************************************
Fallbacks
***************************************************************************** */
static http_response_s *fallback_http_response_create(http_request_s *request) {
(void)request;
return NULL;
}
static void fallback_http_response_dest(http_response_s *res) {
(void)res;
return;
}
/* *****************************************************************************
Initialization
***************************************************************************** */
/** Creates / allocates a protocol version's response object. */
http_response_s *http_response_create(http_request_s *request) {
static http_response_s *(*const vtable[2])(http_request_s *) = {
http1_response_create /* HTTP_V1 */,
fallback_http_response_create /* HTTP_V2 */};
return vtable[request->http_version](request);
}
/** Destroys the response object. No data is sent.*/
void http_response_destroy(http_response_s *response) {
if (!response)
return;
static void (*const vtable[2])(http_response_s *) = {
http1_response_destroy /* HTTP_V1 */,
fallback_http_response_dest /* HTTP_V2 */};
vtable[response->http_version](response);
}
/* we declare it in advance, because we reference it soon. */
static void http_response_log_finish(http_response_s *response);
/** Sends the data and destroys the response object.*/
void http_response_finish(http_response_s *response) {
static void (*const vtable[2])(http_response_s *) = {
http1_response_finish /* HTTP_V1 */,
fallback_http_response_dest /* HTTP_V2 */};
if (response->logged)
http_response_log_finish(response);
vtable[response->http_version](response);
}
/* *****************************************************************************
Writing data to the response object
***************************************************************************** */
#define is_num(c) ((c) >= '0' && (c) <= '9')
#define num_val(c) ((c)-48)
#define invalid_cookie_char(c) \
((c) < '!' || (c) > '~' || (c) == '=' || (c) == ' ' || (c) == ',' || \
(c) == ';')
/**
If the header buffer is full or the headers were already sent (new headers
cannot be sent), the function will return -1.
On success, the function returns 0.
*/
int http_response_write_header_fn(http_response_s *response,
http_header_s header) {
static int (*const vtable[2])(http_response_s *, http_header_s) = {
http1_response_write_header_fn /* HTTP_V1 */, NULL /* HTTP_V2 */,
};
if (!header.name || response->headers_sent)
return -1;
if (header.value && !header.value_len)
header.value_len = strlen(header.value);
if (header.name && !header.name_len)
header.name_len = strlen(header.name);
if (header.name_len == 4 && !strncasecmp(header.name, "Date", 4))
response->date_written = 1;
else if (header.name_len == 14 &&
!strncasecmp(header.name, "content-length", 14))
response->content_length_written = 1;
else if (header.name_len == 13 &&
!strncasecmp(header.name, "Last-Modified", 13))
response->date_written = 1;
else if (header.name_len == 10 &&
!strncasecmp(header.name, "connection", 10)) {
response->connection_written = 1;
if (header.value_len == 5 && !strncasecmp(header.value, "close", 5))
response->should_close = 1;
}
return vtable[response->http_version](response, header);
}
/**
Set / Delete a cookie using this helper function.
If the header buffer is full or the headers were already sent (new headers
cannot be sent), the function will return -1.
On success, the function returns 0.
*/
#undef http_response_set_cookie
int http_response_set_cookie(http_response_s *response, http_cookie_s cookie) {
/* validate common requirements. */
if (!cookie.name || response->headers_sent)
return -1;
ssize_t tmp = cookie.name_len;
if (cookie.name_len) {
do {
tmp--;
if (!cookie.name[tmp] || invalid_cookie_char(cookie.name[tmp]))
goto error;
} while (tmp);
} else {
while (cookie.name[cookie.name_len] &&
!invalid_cookie_char(cookie.name[cookie.name_len]))
cookie.name_len++;
if (cookie.name[cookie.name_len])
goto error;
}
if (cookie.value_len) {
ssize_t tmp = cookie.value_len;
do {
tmp--;
if (!cookie.value[tmp] || invalid_cookie_char(cookie.value[tmp]))
goto error;
} while (tmp);
} else {
while (cookie.value[cookie.value_len] &&
!invalid_cookie_char(cookie.value[cookie.value_len]))
cookie.value_len++;
if (cookie.value[cookie.value_len])
return -1;
}
static int (*const vtable[2])(http_response_s *, http_cookie_s) = {
http1_response_set_cookie /* HTTP_V1 */, NULL /* HTTP_V2 */,
};
return vtable[response->http_version](response, cookie);
error:
fprintf(stderr, "ERROR: Invalid cookie value cookie value character: %c\n",
cookie.value[tmp]);
return -1;
}
/**
Sends the headers (if they weren't previously sent) and writes the data to the
underlying socket.
The body will be copied to the server's outgoing buffer.
If the connection was already closed, the function will return -1. On success,
the function returns 0.
*/
int http_response_write_body(http_response_s *response, const char *body,
size_t length) {
static int (*const vtable[2])(http_response_s *, const char *, size_t) = {
http1_response_write_body /* HTTP_V1 */, NULL /* HTTP_V2 */,
};
if (!response->content_length)
response->content_length = length;
return vtable[response->http_version](response, body, length);
}
/**
Sends the headers (if they weren't previously sent) and writes the data to the
underlying socket.
If the connection was already closed, the function will return -1. On success,
the function returns 0.
*/
int http_response_sendfile(http_response_s *response, int source_fd,
off_t offset, size_t length) {
static int (*const vtable[2])(http_response_s *, int, off_t, size_t) = {
http1_response_sendfile /* HTTP_V1 */, NULL /* HTTP_V2 */,
};
if (!response->content_length)
response->content_length = length;
return vtable[response->http_version](response, source_fd, offset, length);
}
/**
Attempts to send the file requested using an **optional** response object (if
no response object is pointed to, a temporary response object will be
created).
This function will honor Ranged requests by setting the byte range
appropriately.
On failure, the function will return -1 (no response will be sent).
On success, the function returns 0.
*/
int http_response_sendfile2(http_response_s *response, http_request_s *request,
const char *file_path_safe, size_t path_safe_len,
const char *file_path_unsafe,
size_t path_unsafe_len, uint8_t log) {
static char *HEAD = "HEAD";
char buffer[64]; /* we'll need this a few times along the way */
if (request == NULL || (file_path_safe == NULL && file_path_unsafe == NULL))
return -1;
if (file_path_safe && path_safe_len == 0)
path_safe_len = strlen(file_path_safe);
if (file_path_unsafe && path_unsafe_len == 0)
path_unsafe_len = strlen(file_path_unsafe);
const char *mime = NULL;
const char *ext = NULL;
int8_t should_free_response = 0;
struct stat file_data = {.st_size = 0};
// fprintf(stderr, "\n\noriginal request path: %s\n", req->path);
// char *fname = malloc(path_safe_len + path_unsafe_len + 1 + 11);
if ((path_safe_len + path_unsafe_len) >= (PATH_MAX - 1 - 11))
return -1;
char fname[path_safe_len + path_unsafe_len + (1 + 11 + 3)];
// if (fname == NULL)
// return -1;
if (file_path_safe)
memcpy(fname, file_path_safe, path_safe_len);
fname[path_safe_len] = 0;
// if the last character is a '/', step back.
if (file_path_unsafe) {
if (fname[path_safe_len - 1] == '/')
path_safe_len--;
ssize_t tmp = http_decode_path(fname + path_safe_len, file_path_unsafe,
path_unsafe_len);
if (tmp < 0)
goto no_file;
path_safe_len += tmp;
if (fname[path_safe_len - 1] == '/') {
memcpy(fname + path_safe_len, "index.html", 10);
fname[path_safe_len += 10] = 0;
}
// scan path string for double dots (security - prevent path manipulation)
// set the extention point value, while were doing so.
tmp = 0;
while (fname[tmp]) {
if (fname[tmp] == '.')
ext = fname + tmp;
// return false if we found a "/.." or "/" in our string.
if (fname[tmp++] == '/' &&
((fname[tmp++] == '.' && fname[tmp++] == '.') || fname[tmp] == '/'))
goto no_file;
}
}
// fprintf(stderr, "file name: %s\noriginal request path: %s\n", fname,
// req->path);
// get file data (prevent folder access and get modification date)
{
http_header_s accept =
http_request_header_find(request, "accept-encoding", 15);
if (accept.data && strcasestr(accept.data, "gzip")) {
buffer[0] = 1;
fname[path_safe_len] = '.';
fname[path_safe_len + 1] = 'g';
fname[path_safe_len + 2] = 'z';
fname[path_safe_len + 3] = 0;
if (stat(fname, &file_data)) {
buffer[0] = 0;
fname[path_safe_len] = 0;
if (stat(fname, &file_data))
goto no_file;
}
} else {
buffer[0] = 0;
if (stat(fname, &file_data))
goto no_file;
}
}
// check that we have a file and not something else
if (!S_ISREG(file_data.st_mode) && !S_ISLNK(file_data.st_mode))
goto no_file;
if (response == NULL) {
should_free_response = 1;
response = http_response_create(request);
if (log)
http_response_log_start(response);
}
// we have a file, time to handle response details.
int file = open(fname, O_RDONLY);
// free the allocated fname memory
// free(fname);
// fname = NULL;
if (file == -1) {
goto no_fd_available;
}
if (buffer[0]) {
fname[path_safe_len] = 0;
http_response_write_header(response, .name = "Content-Encoding",
.name_len = 16, .value = "gzip", .value_len = 4);
}
// get the mime type (we have an ext pointer and the string isn't empty)
if (ext && ext[1]) {
mime = http_response_ext2mime(ext + 1);
if (mime) {
http_response_write_header(response, .name = "Content-Type",
.name_len = 12, .value = mime);
}
}
/* add ETag */
uint64_t sip = (uint64_t)file_data.st_size;
sip ^= (uint64_t)file_data.st_mtime;
sip = siphash24(&sip, sizeof(uint64_t), SIPHASH_DEFAULT_KEY);
bscrypt_base64_encode(buffer, (void *)&sip, 8);
http_response_write_header(response, .name = "ETag", .name_len = 4,
.value = buffer, .value_len = 12);
response->last_modified = file_data.st_mtime;
http_response_write_header(response, .name = "Cache-Control", .name_len = 13,
.value = "max-age=3600", .value_len = 12);
/* check etag */
if ((ext = http_request_header_find(request, "if-none-match", 13).value) &&
memcmp(ext, buffer, 12) == 0) {
/* send back 304 */
response->status = 304;
close(file);
http_response_finish(response);
return 0;
}
// Range handling
if ((ext = http_request_header_find(request, "range", 5).value) &&
(ext[0] | 32) == 'b' && (ext[1] | 32) == 'y' && (ext[2] | 32) == 't' &&
(ext[3] | 32) == 'e' && (ext[4] | 32) == 's' && (ext[5] | 32) == '=') {
// ext holds the first range, starting on index 6 i.e. RANGE: bytes=0-1
// "HTTP/1.1 206 Partial content\r\n"
// "Accept-Ranges: bytes\r\n"
// "Content-Range: bytes %lu-%lu/%lu\r\n"
// fprintf(stderr, "Got a range request %s\n", ext);
size_t start = 0, finish = 0;
ext = ext + 6;
while (is_num(*ext)) {
start = start * 10;
start += num_val(*ext);
ext++;
}
// fprintf(stderr, "Start: %lu / %lld\n", start, file_data.st_size);
if ((off_t)start >= file_data.st_size - 1)
goto invalid_range;
ext++;
while (is_num(*ext)) {
finish = finish * 10;
finish += num_val(*ext);
ext++;
}
// going to the EOF (big chunk or EOL requested) - send as file
if ((off_t)finish >= file_data.st_size)
finish = file_data.st_size;
char *pos = buffer + 6;
memcpy(buffer, "bytes ", 6);
pos += http_ul2a(pos, start);
*(pos++) = '-';
pos += http_ul2a(pos, finish);
*(pos++) = '/';
pos += http_ul2a(pos, file_data.st_size);
http_response_write_header(response, .name = "Content-Range",
.name_len = 13, .value = buffer,
.value_len = pos - buffer);
response->status = 206;
http_response_write_header(response, .name = "Accept-Ranges",
.name_len = 13, .value = "bytes",
.value_len = 5);
if (*((uint32_t *)request->method) == *((uint32_t *)HEAD)) {
response->content_length = 0;
close(file);
http_response_finish(response);
return 0;
}
http_response_sendfile(response, file, start, finish - start + 1);
http_response_finish(response);
return 0;
}
invalid_range:
http_response_write_header(response, .name = "Accept-Ranges", .name_len = 13,
.value = "none", .value_len = 4);
if (*((uint32_t *)request->method) == *((uint32_t *)HEAD)) {
response->content_length = 0;
close(file);
http_response_finish(response);
return 0;
}
http_response_sendfile(response, file, 0, file_data.st_size);
http_response_finish(response);
return 0;
no_fd_available:
response->status = 503;
const char *body = http_response_status_str(503);
http_response_write_body(response, body, strlen(body));
http_response_finish(response);
no_file:
if (should_free_response && response)
http_response_destroy(response);
// free(fname);
return -1;
}
/* *****************************************************************************
Logging
***************************************************************************** */
#ifdef RUSAGE_SELF
static const size_t CLOCK_RESOLUTION = 1000; /* in miliseconds */
static size_t get_clock_mili(void) {
struct rusage rusage;
getrusage(RUSAGE_SELF, &rusage);
return ((rusage.ru_utime.tv_sec + rusage.ru_stime.tv_sec) * 1000000) +
(rusage.ru_utime.tv_usec + rusage.ru_stime.tv_usec);
}
#elif defined CLOCKS_PER_SEC
#define get_clock_mili() (size_t) clock()
#define CLOCK_RESOLUTION (CLOCKS_PER_SEC / 1000)
#else
#define get_clock_mili() 0
#define CLOCK_RESOLUTION 1
#endif
/**
Starts counting miliseconds for log results.
*/
void http_response_log_start(http_response_s *response) {
response->clock_start = get_clock_mili();
response->logged = 1;
}
/**
prints out the log to stderr.
*/
static void http_response_log_finish(http_response_s *response) {
#define HTTP_REQUEST_LOG_LIMIT 128
char buffer[HTTP_REQUEST_LOG_LIMIT];
http_request_s *request = response->request;
intptr_t bytes_sent = response->content_length;
size_t mili =
response->logged
? ((get_clock_mili() - response->clock_start) / CLOCK_RESOLUTION)
: 0;
struct tm tm;
time_t last_tick = facil_last_tick();
http_gmtime(&last_tick, &tm);
// TODO Guess IP address from headers (forwarded) where possible
sock_peer_addr_s addrinfo = sock_peer_addr(response->fd);
size_t pos = 0;
if (addrinfo.addrlen) {
if (inet_ntop(
addrinfo.addr->sa_family,
addrinfo.addr->sa_family == AF_INET
? (void *)&((struct sockaddr_in *)addrinfo.addr)->sin_addr
: (void *)&((struct sockaddr_in6 *)addrinfo.addr)->sin6_addr,
buffer, 128))
pos = strlen(buffer);
// pos = addrinfo.addr->sa_family == AF_INET ?: fmt_ip6()
}
if (pos == 0) {
memcpy(buffer, "[unknown]", 9);
pos = 9;
}
memcpy(buffer + pos, " - - [", 6);
pos += 6;
pos += http_date2str(buffer + pos, &tm);
buffer[pos++] = ']';
buffer[pos++] = ' ';
buffer[pos++] = '"';
// limit method to 10 chars
if (request->method_len <= 10) {
memcpy(buffer + pos, request->method, request->method_len);
pos += request->method_len;
} else {
const char *j = request->method;
// copy first 7 chars
while (j < request->method + 7)
buffer[pos++] = *(j++);
// add three dots.
buffer[pos++] = '.';
buffer[pos++] = '.';
buffer[pos++] = '.';
}
buffer[pos++] = ' ';
// limit path to 24 chars
if (request->path_len <= 24) {
memcpy(buffer + pos, request->path, request->path_len);
pos += request->path_len;
} else {
const char *j = request->path;
// copy first 7 chars
while (j < request->path + 21)
buffer[pos++] = *(j++);
// add three dots.
buffer[pos++] = '.';
buffer[pos++] = '.';
buffer[pos++] = '.';
}
buffer[pos++] = ' ';
// limit version to 10 chars
if (request->version_len <= 10) {
memcpy(buffer + pos, request->version, request->version_len);
pos += request->version_len;
} else {
const char *j = request->version;
// copy first 7 chars
while (j < request->version + 7)
buffer[pos++] = *(j++);
// add three dots.
buffer[pos++] = '.';
buffer[pos++] = '.';
buffer[pos++] = '.';
}
buffer[pos++] = '"';
buffer[pos++] = ' ';
pos += http_ul2a(buffer + pos, response->status > 0 && response->status < 1000
? response->status
: 0);
buffer[pos++] = ' ';
if (bytes_sent > 0)
pos += http_ul2a(buffer + pos, bytes_sent);
else {
buffer[pos++] = '-';
buffer[pos++] = '-';
}
if (response->logged) {
buffer[pos++] = ' ';
pos += http_ul2a(buffer + pos, mili);
buffer[pos++] = 'm';
buffer[pos++] = 's';
}
buffer[pos++] = '\n';
response->logged = 0;
fwrite(buffer, 1, pos, stderr);
}
/* *****************************************************************************
List matching (status + mime-type)
*****************************************************************************
*/
/** Gets a response status, as a string */
const char *http_response_status_str(uint16_t status) {
static struct {
int i_status;
char *s_status;
} List[] = {{200, "OK"},
{301, "Moved Permanently"},
{302, "Found"},
{100, "Continue"},
{101, "Switching Protocols"},
{403, "Forbidden"},
{404, "Not Found"},
{400, "Bad Request"},
{500, "Internal Server Error"},
{501, "Not Implemented"},
{502, "Bad Gateway"},
{503, "Service Unavailable"},
{102, "Processing"},
{201, "Created"},
{202, "Accepted"},
{203, "Non-Authoritative Information"},
{204, "No Content"},
{205, "Reset Content"},
{206, "Partial Content"},
{207, "Multi-Status"},
{208, "Already Reported"},
{226, "IM Used"},
{300, "Multiple Choices"},
{303, "See Other"},
{304, "Not Modified"},
{305, "Use Proxy"},
{306, "(Unused) "},
{307, "Temporary Redirect"},
{308, "Permanent Redirect"},
{401, "Unauthorized"},
{402, "Payment Required"},
{405, "Method Not Allowed"},
{406, "Not Acceptable"},
{407, "Proxy Authentication Required"},
{408, "Request Timeout"},
{409, "Conflict"},
{410, "Gone"},
{411, "Length Required"},
{412, "Precondition Failed"},
{413, "Payload Too Large"},
{414, "URI Too Long"},
{415, "Unsupported Media Type"},
{416, "Range Not Satisfiable"},
{417, "Expectation Failed"},
{421, "Misdirected Request"},
{422, "Unprocessable Entity"},
{423, "Locked"},
{424, "Failed Dependency"},
{425, "Unassigned"},
{426, "Upgrade Required"},
{427, "Unassigned"},
{428, "Precondition Required"},
{429, "Too Many Requests"},
{430, "Unassigned"},
{431, "Request Header Fields Too Large"},
{504, "Gateway Timeout"},
{505, "HTTP Version Not Supported"},
{506, "Variant Also Negotiates"},
{507, "Insufficient Storage"},
{508, "Loop Detected"},
{509, "Unassigned"},
{510, "Not Extended"},
{511, "Network Authentication Required"},
{0, 0}};
int pos = 0;
while (List[pos].i_status) {
if (List[pos].i_status == status)
return List[pos].s_status;
pos++;
}
return NULL;
}
/** Gets the mime-type string (C string) associated with the file extension.
*/
const char *http_response_ext2mime(const char *ext) {
static struct {
char ext[12];
char *mime;
} List[] = {
{"123", "application/vnd.lotus-1-2-3"},
{"3dml", "text/vnd.in3d.3dml"},
{"3ds", "image/x-3ds"},
{"3g2", "video/3gpp2"},
{"3gp", "video/3gpp"},
{"7z", "application/x-7z-compressed"},
{"aab", "application/x-authorware-bin"},
{"aac", "audio/x-aac"},
{"aam", "application/x-authorware-map"},
{"aas", "application/x-authorware-seg"},
{"abw", "application/x-abiword"},
{"ac", "application/pkix-attr-cert"},
{"acc", "application/vnd.americandynamics.acc"},
{"ace", "application/x-ace-compressed"},
{"acu", "application/vnd.acucobol"},
{"acutc", "application/vnd.acucorp"},
{"adp", "audio/adpcm"},
{"aep", "application/vnd.audiograph"},
{"afm", "application/x-font-type1"},
{"afp", "application/vnd.ibm.modcap"},
{"ahead", "application/vnd.ahead.space"},
{"ai", "application/postscript"},
{"aif", "audio/x-aiff"},
{"aifc", "audio/x-aiff"},
{"aiff", "audio/x-aiff"},
{"air", "application/vnd.adobe.air-application-installer-package+zip"},
{"ait", "application/vnd.dvb.ait"},
{"ami", "application/vnd.amiga.ami"},
{"apk", "application/vnd.android.package-archive"},
{"appcache", "text/cache-manifest"},
{"application", "application/x-ms-application"},
{
"pptx",
"application/"
"vnd.openxmlformats-officedocument.presentationml.presentation",
},
{"apr", "application/vnd.lotus-approach"},
{"arc", "application/x-freearc"},
{"asc", "application/pgp-signature"},
{"asf", "video/x-ms-asf"},
{"asm", "text/x-asm"},
{"aso", "application/vnd.accpac.simply.aso"},
{"asx", "video/x-ms-asf"},
{"atc", "application/vnd.acucorp"},
{"atom", "application/atom+xml"},
{"atomcat", "application/atomcat+xml"},
{"atomsvc", "application/atomsvc+xml"},
{"atx", "application/vnd.antix.game-component"},
{"au", "audio/basic"},
{"avi", "video/x-msvideo"},
{"aw", "application/applixware"},
{"azf", "application/vnd.airzip.filesecure.azf"},
{"azs", "application/vnd.airzip.filesecure.azs"},
{"azw", "application/vnd.amazon.ebook"},
{"bat", "application/x-msdownload"},
{"bcpio", "application/x-bcpio"},
{"bdf", "application/x-font-bdf"},
{"bdm", "application/vnd.syncml.dm+wbxml"},
{"bed", "application/vnd.realvnc.bed"},
{"bh2", "application/vnd.fujitsu.oasysprs"},
{"bin", "application/octet-stream"},
{"blb", "application/x-blorb"},
{"blorb", "application/x-blorb"},
{"bmi", "application/vnd.bmi"},
{"bmp", "image/bmp"},
{"book", "application/vnd.framemaker"},
{"box", "application/vnd.previewsystems.box"},
{"boz", "application/x-bzip2"},
{"bpk", "application/octet-stream"},
{"btif", "image/prs.btif"},
{"bz", "application/x-bzip"},
{"bz2", "application/x-bzip2"},
{"c", "text/x-c"},
{"c11amc", "application/vnd.cluetrust.cartomobile-config"},
{"c11amz", "application/vnd.cluetrust.cartomobile-config-pkg"},
{"c4d", "application/vnd.clonk.c4group"},
{"c4f", "application/vnd.clonk.c4group"},
{"c4g", "application/vnd.clonk.c4group"},
{"c4p", "application/vnd.clonk.c4group"},
{"c4u", "application/vnd.clonk.c4group"},
{"cab", "application/vnd.ms-cab-compressed"},
{"caf", "audio/x-caf"},
{"cap", "application/vnd.tcpdump.pcap"},
{"car", "application/vnd.curl.car"},
{"cat", "application/vnd.ms-pki.seccat"},
{"cb7", "application/x-cbr"},
{"cba", "application/x-cbr"},
{"cbr", "application/x-cbr"},
{"cbt", "application/x-cbr"},
{"cbz", "application/x-cbr"},
{"cc", "text/x-c"},
{"cct", "application/x-director"},
{"ccxml", "application/ccxml+xml"},
{"cdbcmsg", "application/vnd.contact.cmsg"},
{"cdf", "application/x-netcdf"},
{"cdkey", "application/vnd.mediastation.cdkey"},
{"cdmia", "application/cdmi-capability"},
{"cdmic", "application/cdmi-container"},
{"cdmid", "application/cdmi-domain"},
{"cdmio", "application/cdmi-object"},
{"cdmiq", "application/cdmi-queue"},
{"cdx", "chemical/x-cdx"},
{"cdxml", "application/vnd.chemdraw+xml"},
{"cdy", "application/vnd.cinderella"},
{"cer", "application/pkix-cert"},
{"cfs", "application/x-cfs-compressed"},
{"cgm", "image/cgm"},
{"chat", "application/x-chat"},
{"chm", "application/vnd.ms-htmlhelp"},
{"chrt", "application/vnd.kde.kchart"},
{"cif", "chemical/x-cif"},
{"cii", "application/vnd.anser-web-certificate-issue-initiation"},
{"cil", "application/vnd.ms-artgalry"},
{"cla", "application/vnd.claymore"},
{"class", "application/java-vm"},
{"clkk", "application/vnd.crick.clicker.keyboard"},
{"clkp", "application/vnd.crick.clicker.palette"},
{"clkt", "application/vnd.crick.clicker.template"},
{"clkw", "application/vnd.crick.clicker.wordbank"},
{"clkx", "application/vnd.crick.clicker"},
{"clp", "application/x-msclip"},
{"cmc", "application/vnd.cosmocaller"},
{"cmdf", "chemical/x-cmdf"},
{"cml", "chemical/x-cml"},
{"cmp", "application/vnd.yellowriver-custom-menu"},
{"cmx", "image/x-cmx"},
{"cod", "application/vnd.rim.cod"},
{"com", "application/x-msdownload"},
{"conf", "text/plain"},
{"cpio", "application/x-cpio"},
{"cpp", "text/x-c"},
{"cpt", "application/mac-compactpro"},
{"crd", "application/x-mscardfile"},
{"crl", "application/pkix-crl"},
{"crt", "application/x-x509-ca-cert"},
{"cryptonote", "application/vnd.rig.cryptonote"},
{"csh", "application/x-csh"},
{"csml", "chemical/x-csml"},
{"csp", "application/vnd.commonspace"},
{"css", "text/css"},
{"cst", "application/x-director"},
{"csv", "text/csv"},
{"cu", "application/cu-seeme"},
{"curl", "text/vnd.curl"},
{"cww", "application/prs.cww"},
{"cxt", "application/x-director"},
{"cxx", "text/x-c"},
{"dae", "model/vnd.collada+xml"},
{"daf", "application/vnd.mobius.daf"},
{"dart", "application/vnd.dart"},
{"dataless", "application/vnd.fdsn.seed"},
{"davmount", "application/davmount+xml"},
{"dbk", "application/docbook+xml"},
{"dcr", "application/x-director"},
{"dcurl", "text/vnd.curl.dcurl"},
{"dd2", "application/vnd.oma.dd2+xml"},
{"ddd", "application/vnd.fujixerox.ddd"},
{"deb", "application/x-debian-package"},
{"def", "text/plain"},
{"deploy", "application/octet-stream"},
{"der", "application/x-x509-ca-cert"},
{"dfac", "application/vnd.dreamfactory"},
{"dgc", "application/x-dgc-compressed"},
{"dic", "text/x-c"},
{"dir", "application/x-director"},
{"dis", "application/vnd.mobius.dis"},
{"dist", "application/octet-stream"},
{"distz", "application/octet-stream"},
{"djv", "image/vnd.djvu"},
{"djvu", "image/vnd.djvu"},
{"dll", "application/x-msdownload"},
{"dmg", "application/x-apple-diskimage"},
{"dmp", "application/vnd.tcpdump.pcap"},
{"dms", "application/octet-stream"},
{"dna", "application/vnd.dna"},
{"doc", "application/msword"},
{"docm", "application/vnd.ms-word.document.macroenabled.12"},
{"docx", "application/"
"vnd.openxmlformats-officedocument.wordprocessingml.document"},
{"dot", "application/msword"},
{"dotm", "application/vnd.ms-word.template.macroenabled.12"},
{"dotx", "application/"
"vnd.openxmlformats-officedocument.wordprocessingml.template"},
{"dp", "application/vnd.osgi.dp"},
{"dpg", "application/vnd.dpgraph"},
{"dra", "audio/vnd.dra"},
{"dsc", "text/prs.lines.tag"},
{"dssc", "application/dssc+der"},
{"dtb", "application/x-dtbook+xml"},
{"dtd", "application/xml-dtd"},
{"dts", "audio/vnd.dts"},
{"dtshd", "audio/vnd.dts.hd"},
{"dump", "application/octet-stream"},
{"dvb", "video/vnd.dvb.file"},
{"dvi", "application/x-dvi"},
{"dwf", "model/vnd.dwf"},
{"dwg", "image/vnd.dwg"},
{"dxf", "image/vnd.dxf"},
{"dxp", "application/vnd.spotfire.dxp"},
{"dxr", "application/x-director"},
{"ecelp4800", "audio/vnd.nuera.ecelp4800"},
{"ecelp7470", "audio/vnd.nuera.ecelp7470"},
{"ecelp9600", "audio/vnd.nuera.ecelp9600"},
{"ecma", "application/ecmascript"},
{"edm", "application/vnd.novadigm.edm"},
{"edx", "application/vnd.novadigm.edx"},
{"efif", "application/vnd.picsel"},
{"ei6", "application/vnd.pg.osasli"},
{"elc", "application/octet-stream"},
{"emf", "application/x-msmetafile"},
{"eml", "message/rfc822"},
{"emma", "application/emma+xml"},
{"emz", "application/x-msmetafile"},
{"eol", "audio/vnd.digital-winds"},
{"eot", "application/vnd.ms-fontobject"},
{"eps", "application/postscript"},
{"epub", "application/epub+zip"},
{"es3", "application/vnd.eszigno3+xml"},
{"esa", "application/vnd.osgi.subsystem"},
{"esf", "application/vnd.epson.esf"},
{"et3", "application/vnd.eszigno3+xml"},
{"etx", "text/x-setext"},
{"eva", "application/x-eva"},
{"evy", "application/x-envoy"},
{"exe", "application/x-msdownload"},
{"exi", "application/exi"},
{"ext", "application/vnd.novadigm.ext"},
{"ez", "application/andrew-inset"},
{"ez2", "application/vnd.ezpix-album"},
{"ez3", "application/vnd.ezpix-package"},
{"f", "text/x-fortran"},
{"f4v", "video/x-f4v"},
{"f77", "text/x-fortran"},
{"f90", "text/x-fortran"},
{"fbs", "image/vnd.fastbidsheet"},
{"fcdt", "application/vnd.adobe.formscentral.fcdt"},
{"fcs", "application/vnd.isac.fcs"},
{"fdf", "application/vnd.fdf"},
{"fe_launch", "application/vnd.denovo.fcselayout-link"},
{"fg5", "application/vnd.fujitsu.oasysgp"},
{"fgd", "application/x-director"},
{"fh", "image/x-freehand"},
{"fh4", "image/x-freehand"},
{"fh5", "image/x-freehand"},
{"fh7", "image/x-freehand"},
{"fhc", "image/x-freehand"},
{"fig", "application/x-xfig"},
{"flac", "audio/x-flac"},
{"fli", "video/x-fli"},
{"flo", "application/vnd.micrografx.flo"},
{"flv", "video/x-flv"},
{"flw", "application/vnd.kde.kivio"},
{"flx", "text/vnd.fmi.flexstor"},
{"fly", "text/vnd.fly"},
{"fm", "application/vnd.framemaker"},
{"fnc", "application/vnd.frogans.fnc"},
{"for", "text/x-fortran"},
{"fpx", "image/vnd.fpx"},
{"frame", "application/vnd.framemaker"},
{"fsc", "application/vnd.fsc.weblaunch"},
{"fst", "image/vnd.fst"},
{"ftc", "application/vnd.fluxtime.clip"},
{"fti", "application/vnd.anser-web-funds-transfer-initiation"},
{"fvt", "video/vnd.fvt"},
{"fxp", "application/vnd.adobe.fxp"},
{"fxpl", "application/vnd.adobe.fxp"},
{"fzs", "application/vnd.fuzzysheet"},
{"g2w", "application/vnd.geoplan"},
{"g3", "image/g3fax"},
{"g3w", "application/vnd.geospace"},
{"gac", "application/vnd.groove-account"},
{"gam", "application/x-tads"},
{"gbr", "application/rpki-ghostbusters"},
{"gca", "application/x-gca-compressed"},
{"gdl", "model/vnd.gdl"},
{"geo", "application/vnd.dynageo"},
{"gex", "application/vnd.geometry-explorer"},
{"ggb", "application/vnd.geogebra.file"},
{"ggt", "application/vnd.geogebra.tool"},
{"ghf", "application/vnd.groove-help"},
{"gif", "image/gif"},
{"gim", "application/vnd.groove-identity-message"},
{"gml", "application/gml+xml"},
{"gmx", "application/vnd.gmx"},
{"gnumeric", "application/x-gnumeric"},
{"gph", "application/vnd.flographit"},
{"gpx", "application/gpx+xml"},
{"gqf", "application/vnd.grafeq"},
{"gqs", "application/vnd.grafeq"},
{"gram", "application/srgs"},
{"gramps", "application/x-gramps-xml"},
{"gre", "application/vnd.geometry-explorer"},
{"grv", "application/vnd.groove-injector"},
{"grxml", "application/srgs+xml"},
{"gsf", "application/x-font-ghostscript"},
{"gtar", "application/x-gtar"},
{"gtm", "application/vnd.groove-tool-message"},
{"gtw", "model/vnd.gtw"},
{"gv", "text/vnd.graphviz"},
{"gxf", "application/gxf"},
{"gxt", "application/vnd.geonext"},
{"h", "text/x-c"},
{"h261", "video/h261"},
{"h263", "video/h263"},
{"h264", "video/h264"},
{"hal", "application/vnd.hal+xml"},
{"hbci", "application/vnd.hbci"},
{"hdf", "application/x-hdf"},
{"hh", "text/x-c"},
{"hlp", "application/winhlp"},
{"hpgl", "application/vnd.hp-hpgl"},
{"hpid", "application/vnd.hp-hpid"},
{"hps", "application/vnd.hp-hps"},
{"hqx", "application/mac-binhex40"},
{"htke", "application/vnd.kenameaapp"},
{"htm", "text/html"},
{"html", "text/html"},
{"hvd", "application/vnd.yamaha.hv-dic"},
{"hvp", "application/vnd.yamaha.hv-voice"},
{"hvs", "application/vnd.yamaha.hv-script"},
{"i2g", "application/vnd.intergeo"},
{"icc", "application/vnd.iccprofile"},
{"ice", "x-conference/x-cooltalk"},
{"icm", "application/vnd.iccprofile"},
{"ico", "image/x-icon"},
{"ics", "text/calendar"},
{"ief", "image/ief"},
{"ifb", "text/calendar"},
{"ifm", "application/vnd.shana.informed.formdata"},
{"iges", "model/iges"},
{"igl", "application/vnd.igloader"},
{"igm", "application/vnd.insors.igm"},
{"igs", "model/iges"},
{"igx", "application/vnd.micrografx.igx"},
{"iif", "application/vnd.shana.informed.interchange"},
{"imp", "application/vnd.accpac.simply.imp"},
{"ims", "application/vnd.ms-ims"},
{"in", "text/plain"},
{"ink", "application/inkml+xml"},
{"inkml", "application/inkml+xml"},
{"install", "application/x-install-instructions"},
{"iota", "application/vnd.astraea-software.iota"},
{"ipfix", "application/ipfix"},
{"ipk", "application/vnd.shana.informed.package"},
{"irm", "application/vnd.ibm.rights-management"},
{"irp", "application/vnd.irepository.package+xml"},
{"iso", "application/x-iso9660-image"},
{"itp", "application/vnd.shana.informed.formtemplate"},
{"ivp", "application/vnd.immervision-ivp"},
{"ivu", "application/vnd.immervision-ivu"},
{"jad", "text/vnd.sun.j2me.app-descriptor"},
{"jam", "application/vnd.jam"},
{"jar", "application/java-archive"},
{"java", "text/x-java-source"},
{"jisp", "application/vnd.jisp"},
{"jlt", "application/vnd.hp-jlyt"},
{"jnlp", "application/x-java-jnlp-file"},
{"joda", "application/vnd.joost.joda-archive"},
{"jpe", "image/jpeg"},
{"jpeg", "image/jpeg"},
{"jpg", "image/jpeg"},
{"jpgm", "video/jpm"},
{"jpgv", "video/jpeg"},
{"jpm", "video/jpm"},
{"js", "application/javascript"},
{"json", "application/json"},
{"jsonml", "application/jsonml+json"},
{"kar", "audio/midi"},
{"karbon", "application/vnd.kde.karbon"},
{"kfo", "application/vnd.kde.kformula"},
{"kia", "application/vnd.kidspiration"},
{"kml", "application/vnd.google-earth.kml+xml"},
{"kmz", "application/vnd.google-earth.kmz"},
{"kne", "application/vnd.kinar"},
{"knp", "application/vnd.kinar"},
{"kon", "application/vnd.kde.kontour"},
{"kpr", "application/vnd.kde.kpresenter"},
{"kpt", "application/vnd.kde.kpresenter"},
{"kpxx", "application/vnd.ds-keypoint"},
{"ksp", "application/vnd.kde.kspread"},
{"ktr", "application/vnd.kahootz"},
{"ktx", "image/ktx"},
{"ktz", "application/vnd.kahootz"},
{"kwd", "application/vnd.kde.kword"},
{"kwt", "application/vnd.kde.kword"},
{"lasxml", "application/vnd.las.las+xml"},
{"latex", "application/x-latex"},
{"lbd", "application/vnd.llamagraphics.life-balance.desktop"},
{"lbe", "application/vnd.llamagraphics.life-balance.exchange+xml"},
{"les", "application/vnd.hhe.lesson-player"},
{"lha", "application/x-lzh-compressed"},
{"link66", "application/vnd.route66.link66+xml"},
{"list", "text/plain"},
{"list3820", "application/vnd.ibm.modcap"},
{"listafp", "application/vnd.ibm.modcap"},
{"lnk", "application/x-ms-shortcut"},
{"log", "text/plain"},
{"lostxml", "application/lost+xml"},
{"lrf", "application/octet-stream"},
{"lrm", "application/vnd.ms-lrm"},
{"ltf", "application/vnd.frogans.ltf"},
{"lvp", "audio/vnd.lucent.voice"},
{"lwp", "application/vnd.lotus-wordpro"},
{"lzh", "application/x-lzh-compressed"},
{"m13", "application/x-msmediaview"},
{"m14", "application/x-msmediaview"},
{"m1v", "video/mpeg"},
{"m21", "application/mp21"},
{"m2a", "audio/mpeg"},
{"m2v", "video/mpeg"},
{"m3a", "audio/mpeg"},
{"m3u", "audio/x-mpegurl"},
{"m3u8", "application/vnd.apple.mpegurl"},
{"m4a", "audio/mp4"},
{"m4u", "video/vnd.mpegurl"},
{"m4v", "video/x-m4v"},
{"ma", "application/mathematica"},
{"mads", "application/mads+xml"},
{"mag", "application/vnd.ecowin.chart"},
{"maker", "application/vnd.framemaker"},
{"man", "text/troff"},
{"mar", "application/octet-stream"},
{"mathml", "application/mathml+xml"},
{"mb", "application/mathematica"},
{"mbk", "application/vnd.mobius.mbk"},
{"mbox", "application/mbox"},
{"mc1", "application/vnd.medcalcdata"},
{"mcd", "application/vnd.mcd"},
{"mcurl", "text/vnd.curl.mcurl"},
{"mdb", "application/x-msaccess"},
{"mdi", "image/vnd.ms-modi"},
{"me", "text/troff"},
{"mesh", "model/mesh"},
{"meta4", "application/metalink4+xml"},
{"metalink", "application/metalink+xml"},
{"mets", "application/mets+xml"},
{"mfm", "application/vnd.mfmp"},
{"mft", "application/rpki-manifest"},
{"mgp", "application/vnd.osgeo.mapguide.package"},
{"mgz", "application/vnd.proteus.magazine"},
{"mid", "audio/midi"},
{"midi", "audio/midi"},
{"mie", "application/x-mie"},
{"mif", "application/vnd.mif"},
{"mime", "message/rfc822"},
{"mj2", "video/mj2"},
{"mjp2", "video/mj2"},
{"mk3d", "video/x-matroska"},
{"mka", "audio/x-matroska"},
{"mks", "video/x-matroska"},
{"mkv", "video/x-matroska"},
{"mlp", "application/vnd.dolby.mlp"},
{"mmd", "application/vnd.chipnuts.karaoke-mmd"},
{"mmf", "application/vnd.smaf"},
{"mmr", "image/vnd.fujixerox.edmics-mmr"},
{"mng", "video/x-mng"},
{"mny", "application/x-msmoney"},
{"mobi", "application/x-mobipocket-ebook"},
{"mods", "application/mods+xml"},
{"mov", "video/quicktime"},
{"movie", "video/x-sgi-movie"},
{"mp2", "audio/mpeg"},
{"mp21", "application/mp21"},
{"mp2a", "audio/mpeg"},
{"mp3", "audio/mpeg"},
{"mp4", "video/mp4"},
{"mp4a", "audio/mp4"},
{"mp4s", "application/mp4"},
{"mp4v", "video/mp4"},
{"mpc", "application/vnd.mophun.certificate"},
{"mpe", "video/mpeg"},
{"mpeg", "video/mpeg"},
{"mpg", "video/mpeg"},
{"mpg4", "video/mp4"},
{"mpga", "audio/mpeg"},
{"mpkg", "application/vnd.apple.installer+xml"},
{"mpm", "application/vnd.blueice.multipass"},
{"mpn", "application/vnd.mophun.application"},
{"mpp", "application/vnd.ms-project"},
{"mpt", "application/vnd.ms-project"},
{"mpy", "application/vnd.ibm.minipay"},
{"mqy", "application/vnd.mobius.mqy"},
{"mrc", "application/marc"},
{"mrcx", "application/marcxml+xml"},
{"ms", "text/troff"},
{"mscml", "application/mediaservercontrol+xml"},
{"mseed", "application/vnd.fdsn.mseed"},
{"mseq", "application/vnd.mseq"},
{"msf", "application/vnd.epson.msf"},
{"msh", "model/mesh"},
{"msi", "application/x-msdownload"},
{"msl", "application/vnd.mobius.msl"},
{"msty", "application/vnd.muvee.style"},
{"mts", "model/vnd.mts"},
{"mus", "application/vnd.musician"},
{"musicxml", "application/vnd.recordare.musicxml+xml"},
{"mvb", "application/x-msmediaview"},
{"mwf", "application/vnd.mfer"},
{"mxf", "application/mxf"},
{"mxl", "application/vnd.recordare.musicxml"},
{"mxml", "application/xv+xml"},
{"mxs", "application/vnd.triscape.mxs"},
{"mxu", "video/vnd.mpegurl"},
{"n-gage", "application/vnd.nokia.n-gage.symbian.install"},
{"n3", "text/n3"},
{"nb", "application/mathematica"},
{"nbp", "application/vnd.wolfram.player"},
{"nc", "application/x-netcdf"},
{"ncx", "application/x-dtbncx+xml"},
{"nfo", "text/x-nfo"},
{"ngdat", "application/vnd.nokia.n-gage.data"},
{"nitf", "application/vnd.nitf"},
{"nlu", "application/vnd.neurolanguage.nlu"},
{"nml", "application/vnd.enliven"},
{"nnd", "application/vnd.noblenet-directory"},
{"nns", "application/vnd.noblenet-sealer"},
{"nnw", "application/vnd.noblenet-web"},
{"npx", "image/vnd.net-fpx"},
{"nsc", "application/x-conference"},
{"nsf", "application/vnd.lotus-notes"},
{"ntf", "application/vnd.nitf"},
{"nzb", "application/x-nzb"},
{"oa2", "application/vnd.fujitsu.oasys2"},
{"oa3", "application/vnd.fujitsu.oasys3"},
{"oas", "application/vnd.fujitsu.oasys"},
{"obd", "application/x-msbinder"},
{"obj", "application/x-tgif"},
{"oda", "application/oda"},
{"odb", "application/vnd.oasis.opendocument.database"},
{"odc", "application/vnd.oasis.opendocument.chart"},
{"odf", "application/vnd.oasis.opendocument.formula"},
{"odft", "application/vnd.oasis.opendocument.formula-template"},
{"odg", "application/vnd.oasis.opendocument.graphics"},
{"odi", "application/vnd.oasis.opendocument.image"},
{"odm", "application/vnd.oasis.opendocument.text-master"},
{"odp", "application/vnd.oasis.opendocument.presentation"},
{"ods", "application/vnd.oasis.opendocument.spreadsheet"},
{"odt", "application/vnd.oasis.opendocument.text"},
{"oga", "audio/ogg"},
{"ogg", "audio/ogg"},
{"ogv", "video/ogg"},
{"ogx", "application/ogg"},
{"omdoc", "application/omdoc+xml"},
{"onepkg", "application/onenote"},
{"onetmp", "application/onenote"},
{"onetoc", "application/onenote"},
{"onetoc2", "application/onenote"},
{"opf", "application/oebps-package+xml"},
{"opml", "text/x-opml"},
{"oprc", "application/vnd.palm"},
{"org", "application/vnd.lotus-organizer"},
{"osf", "application/vnd.yamaha.openscoreformat"},
{"osfpvg", "application/vnd.yamaha.openscoreformat.osfpvg+xml"},
{"otc", "application/vnd.oasis.opendocument.chart-template"},
{"otf", "application/x-font-otf"},
{"otg", "application/vnd.oasis.opendocument.graphics-template"},
{"oth", "application/vnd.oasis.opendocument.text-web"},
{"oti", "application/vnd.oasis.opendocument.image-template"},
{"otp", "application/vnd.oasis.opendocument.presentation-template"},
{"ots", "application/vnd.oasis.opendocument.spreadsheet-template"},
{"ott", "application/vnd.oasis.opendocument.text-template"},
{"oxps", "application/oxps"},
{"oxt", "application/vnd.openofficeorg.extension"},
{"p", "text/x-pascal"},
{"p10", "application/pkcs10"},
{"p12", "application/x-pkcs12"},
{"p7b", "application/x-pkcs7-certificates"},
{"p7c", "application/pkcs7-mime"},
{"p7m", "application/pkcs7-mime"},
{"p7r", "application/x-pkcs7-certreqresp"},
{"p7s", "application/pkcs7-signature"},
{"p8", "application/pkcs8"},
{"pas", "text/x-pascal"},
{"paw", "application/vnd.pawaafile"},
{"pbd", "application/vnd.powerbuilder6"},
{"pbm", "image/x-portable-bitmap"},
{"pcap", "application/vnd.tcpdump.pcap"},
{"pcf", "application/x-font-pcf"},
{"pcl", "application/vnd.hp-pcl"},
{"pclxl", "application/vnd.hp-pclxl"},
{"pct", "image/x-pict"},
{"pcurl", "application/vnd.curl.pcurl"},
{"pcx", "image/x-pcx"},
{"pdb", "application/vnd.palm"},
{"pdf", "application/pdf"},
{"pfa", "application/x-font-type1"},
{"pfb", "application/x-font-type1"},
{"pfm", "application/x-font-type1"},
{"pfr", "application/font-tdpfr"},
{"pfx", "application/x-pkcs12"},
{"pgm", "image/x-portable-graymap"},
{"pgn", "application/x-chess-pgn"},
{"pgp", "application/pgp-encrypted"},
{"pic", "image/x-pict"},
{"pkg", "application/octet-stream"},
{"pki", "application/pkixcmp"},
{"pkipath", "application/pkix-pkipath"},
{"plb", "application/vnd.3gpp.pic-bw-large"},
{"plc", "application/vnd.mobius.plc"},
{"plf", "application/vnd.pocketlearn"},
{"pls", "application/pls+xml"},
{"pml", "application/vnd.ctc-posml"},
{"png", "image/png"},
{"pnm", "image/x-portable-anymap"},
{"portpkg", "application/vnd.macports.portpkg"},
{"pot", "application/vnd.ms-powerpoint"},
{"potm", "application/vnd.ms-powerpoint.template.macroenabled.12"},
{"potx", "application/"
"vnd.openxmlformats-officedocument.presentationml.template"},
{"ppam", "application/vnd.ms-powerpoint.addin.macroenabled.12"},
{"ppd", "application/vnd.cups-ppd"},
{"ppm", "image/x-portable-pixmap"},
{"pps", "application/vnd.ms-powerpoint"},
{"ppsm", "application/vnd.ms-powerpoint.slideshow.macroenabled.12"},
{"ppsx", "application/"
"vnd.openxmlformats-officedocument.presentationml.slideshow"},
{"ppt", "application/vnd.ms-powerpoint"},
{"pptm", "application/vnd.ms-powerpoint.presentation.macroenabled.12"},
{"pqa", "application/vnd.palm"},
{"prc", "application/x-mobipocket-ebook"},
{"pre", "application/vnd.lotus-freelance"},
{"prf", "application/pics-rules"},
{"ps", "application/postscript"},
{"psb", "application/vnd.3gpp.pic-bw-small"},
{"psd", "image/vnd.adobe.photoshop"},
{"psf", "application/x-font-linux-psf"},
{"pskcxml", "application/pskc+xml"},
{"ptid", "application/vnd.pvi.ptid1"},
{"pub", "application/x-mspublisher"},
{"pvb", "application/vnd.3gpp.pic-bw-var"},
{"pwn", "application/vnd.3m.post-it-notes"},
{"pya", "audio/vnd.ms-playready.media.pya"},
{"pyv", "video/vnd.ms-playready.media.pyv"},
{"qam", "application/vnd.epson.quickanime"},
{"qbo", "application/vnd.intu.qbo"},
{"qfx", "application/vnd.intu.qfx"},
{"qps", "application/vnd.publishare-delta-tree"},
{"qt", "video/quicktime"},
{"qwd", "application/vnd.quark.quarkxpress"},
{"qwt", "application/vnd.quark.quarkxpress"},
{"qxb", "application/vnd.quark.quarkxpress"},
{"qxd", "application/vnd.quark.quarkxpress"},
{"qxl", "application/vnd.quark.quarkxpress"},
{"qxt", "application/vnd.quark.quarkxpress"},
{"ra", "audio/x-pn-realaudio"},
{"ram", "audio/x-pn-realaudio"},
{"rar", "application/x-rar-compressed"},
{"ras", "image/x-cmu-raster"},
{"rcprofile", "application/vnd.ipunplugged.rcprofile"},
{"rdf", "application/rdf+xml"},
{"rdz", "application/vnd.data-vision.rdz"},
{"rep", "application/vnd.businessobjects"},
{"res", "application/x-dtbresource+xml"},
{"rgb", "image/x-rgb"},
{"rif", "application/reginfo+xml"},
{"rip", "audio/vnd.rip"},
{"ris", "application/x-research-info-systems"},
{"rl", "application/resource-lists+xml"},
{"rlc", "image/vnd.fujixerox.edmics-rlc"},
{"rld", "application/resource-lists-diff+xml"},
{"rm", "application/vnd.rn-realmedia"},
{"rmi", "audio/midi"},
{"rmp", "audio/x-pn-realaudio-plugin"},
{"rms", "application/vnd.jcp.javame.midlet-rms"},
{"rmvb", "application/vnd.rn-realmedia-vbr"},
{"rnc", "application/relax-ng-compact-syntax"},
{"roa", "application/rpki-roa"},
{"roff", "text/troff"},
{"rp9", "application/vnd.cloanto.rp9"},
{"rpss", "application/vnd.nokia.radio-presets"},
{"rpst", "application/vnd.nokia.radio-preset"},
{"rq", "application/sparql-query"},
{"rs", "application/rls-services+xml"},
{"rsd", "application/rsd+xml"},
{"rss", "application/rss+xml"},
{"rtf", "application/rtf"},
{"rtx", "text/richtext"},
{"s", "text/x-asm"},
{"s3m", "audio/s3m"},
{"saf", "application/vnd.yamaha.smaf-audio"},
{"sbml", "application/sbml+xml"},
{"sc", "application/vnd.ibm.secure-container"},
{"scd", "application/x-msschedule"},
{"scm", "application/vnd.lotus-screencam"},
{"scq", "application/scvp-cv-request"},
{"scs", "application/scvp-cv-response"},
{"scurl", "text/vnd.curl.scurl"},
{"sda", "application/vnd.stardivision.draw"},
{"sdc", "application/vnd.stardivision.calc"},
{"sdd", "application/vnd.stardivision.impress"},
{"sdkd", "application/vnd.solent.sdkm+xml"},
{"sdkm", "application/vnd.solent.sdkm+xml"},
{"sdp", "application/sdp"},
{"sdw", "application/vnd.stardivision.writer"},
{"see", "application/vnd.seemail"},
{"seed", "application/vnd.fdsn.seed"},
{"sema", "application/vnd.sema"},
{"semd", "application/vnd.semd"},
{"semf", "application/vnd.semf"},
{"ser", "application/java-serialized-object"},
{"setpay", "application/set-payment-initiation"},
{"setreg", "application/set-registration-initiation"},
{"sfd-hdstx", "application/vnd.hydrostatix.sof-data"},
{"sfs", "application/vnd.spotfire.sfs"},
{"sfv", "text/x-sfv"},
{"sgi", "image/sgi"},
{"sgl", "application/vnd.stardivision.writer-global"},
{"sgm", "text/sgml"},
{"sgml", "text/sgml"},
{"sh", "application/x-sh"},
{"shar", "application/x-shar"},
{"shf", "application/shf+xml"},
{"sid", "image/x-mrsid-image"},
{"sig", "application/pgp-signature"},
{"sil", "audio/silk"},
{"silo", "model/mesh"},
{"sis", "application/vnd.symbian.install"},
{"sisx", "application/vnd.symbian.install"},
{"sit", "application/x-stuffit"},
{"sitx", "application/x-stuffitx"},
{"skd", "application/vnd.koan"},
{"skm", "application/vnd.koan"},
{"skp", "application/vnd.koan"},
{"skt", "application/vnd.koan"},
{"sldm", "application/vnd.ms-powerpoint.slide.macroenabled.12"},
{"sldx",
"application/vnd.openxmlformats-officedocument.presentationml.slide"},
{"slt", "application/vnd.epson.salt"},
{"sm", "application/vnd.stepmania.stepchart"},
{"smf", "application/vnd.stardivision.math"},
{"smi", "application/smil+xml"},
{"smil", "application/smil+xml"},
{"smv", "video/x-smv"},
{"smzip", "application/vnd.stepmania.package"},
{"snd", "audio/basic"},
{"snf", "application/x-font-snf"},
{"so", "application/octet-stream"},
{"spc", "application/x-pkcs7-certificates"},
{"spf", "application/vnd.yamaha.smaf-phrase"},
{"spl", "application/x-futuresplash"},
{"spot", "text/vnd.in3d.spot"},
{"spp", "application/scvp-vp-response"},
{"spq", "application/scvp-vp-request"},
{"spx", "audio/ogg"},
{"sql", "application/x-sql"},
{"src", "application/x-wais-source"},
{"srt", "application/x-subrip"},
{"sru", "application/sru+xml"},
{"srx", "application/sparql-results+xml"},
{"ssdl", "application/ssdl+xml"},
{"sse", "application/vnd.kodak-descriptor"},
{"ssf", "application/vnd.epson.ssf"},
{"ssml", "application/ssml+xml"},
{"st", "application/vnd.sailingtracker.track"},
{"stc", "application/vnd.sun.xml.calc.template"},
{"std", "application/vnd.sun.xml.draw.template"},
{"stf", "application/vnd.wt.stf"},
{"sti", "application/vnd.sun.xml.impress.template"},
{"stk", "application/hyperstudio"},
{"stl", "application/vnd.ms-pki.stl"},
{"str", "application/vnd.pg.format"},
{"stw", "application/vnd.sun.xml.writer.template"},
{"sub", "image/vnd.dvb.subtitle"},
{"sub", "text/vnd.dvb.subtitle"},
{"sus", "application/vnd.sus-calendar"},
{"susp", "application/vnd.sus-calendar"},
{"sv4cpio", "application/x-sv4cpio"},
{"sv4crc", "application/x-sv4crc"},
{"svc", "application/vnd.dvb.service"},
{"svd", "application/vnd.svd"},
{"svg", "image/svg+xml"},
{"svgz", "image/svg+xml"},
{"swa", "application/x-director"},
{"swf", "application/x-shockwave-flash"},
{"swi", "application/vnd.aristanetworks.swi"},
{"sxc", "application/vnd.sun.xml.calc"},
{"sxd", "application/vnd.sun.xml.draw"},
{"sxg", "application/vnd.sun.xml.writer.global"},
{"sxi", "application/vnd.sun.xml.impress"},
{"sxm", "application/vnd.sun.xml.math"},
{"sxw", "application/vnd.sun.xml.writer"},
{"t", "text/troff"},
{"t3", "application/x-t3vm-image"},
{"taglet", "application/vnd.mynfc"},
{"tao", "application/vnd.tao.intent-module-archive"},
{"tar", "application/x-tar"},
{"tcap", "application/vnd.3gpp2.tcap"},
{"tcl", "application/x-tcl"},
{"teacher", "application/vnd.smart.teacher"},
{"tei", "application/tei+xml"},
{"teicorpus", "application/tei+xml"},
{"tex", "application/x-tex"},
{"texi", "application/x-texinfo"},
{"texinfo", "application/x-texinfo"},
{"text", "text/plain"},
{"tfi", "application/thraud+xml"},
{"tfm", "application/x-tex-tfm"},
{"tga", "image/x-tga"},
{"thmx", "application/vnd.ms-officetheme"},
{"tif", "image/tiff"},
{"tiff", "image/tiff"},
{"tmo", "application/vnd.tmobile-livetv"},
{"torrent", "application/x-bittorrent"},
{"tpl", "application/vnd.groove-tool-template"},
{"tpt", "application/vnd.trid.tpt"},
{"tr", "text/troff"},
{"tra", "application/vnd.trueapp"},
{"trm", "application/x-msterminal"},
{"tsd", "application/timestamped-data"},
{"tsv", "text/tab-separated-values"},
{"ttc", "application/x-font-ttf"},
{"ttf", "application/x-font-ttf"},
{"ttl", "text/turtle"},
{"twd", "application/vnd.simtech-mindmapper"},
{"twds", "application/vnd.simtech-mindmapper"},
{"txd", "application/vnd.genomatix.tuxedo"},
{"txf", "application/vnd.mobius.txf"},
{"txt", "text/plain"},
{"u32", "application/x-authorware-bin"},
{"udeb", "application/x-debian-package"},
{"ufd", "application/vnd.ufdl"},
{"ufdl", "application/vnd.ufdl"},
{"ulx", "application/x-glulx"},
{"umj", "application/vnd.umajin"},
{"unityweb", "application/vnd.unity"},
{"uoml", "application/vnd.uoml+xml"},
{"uri", "text/uri-list"},
{"uris", "text/uri-list"},
{"urls", "text/uri-list"},
{"ustar", "application/x-ustar"},
{"utz", "application/vnd.uiq.theme"},
{"uu", "text/x-uuencode"},
{"uva", "audio/vnd.dece.audio"},
{"uvd", "application/vnd.dece.data"},
{"uvf", "application/vnd.dece.data"},
{"uvg", "image/vnd.dece.graphic"},
{"uvh", "video/vnd.dece.hd"},
{"uvi", "image/vnd.dece.graphic"},
{"uvm", "video/vnd.dece.mobile"},
{"uvp", "video/vnd.dece.pd"},
{"uvs", "video/vnd.dece.sd"},
{"uvt", "application/vnd.dece.ttml+xml"},
{"uvu", "video/vnd.uvvu.mp4"},
{"uvv", "video/vnd.dece.video"},
{"uvva", "audio/vnd.dece.audio"},
{"uvvd", "application/vnd.dece.data"},
{"uvvf", "application/vnd.dece.data"},
{"uvvg", "image/vnd.dece.graphic"},
{"uvvh", "video/vnd.dece.hd"},
{"uvvi", "image/vnd.dece.graphic"},
{"uvvm", "video/vnd.dece.mobile"},
{"uvvp", "video/vnd.dece.pd"},
{"uvvs", "video/vnd.dece.sd"},
{"uvvt", "application/vnd.dece.ttml+xml"},
{"uvvu", "video/vnd.uvvu.mp4"},
{"uvvv", "video/vnd.dece.video"},
{"uvvx", "application/vnd.dece.unspecified"},
{"uvvz", "application/vnd.dece.zip"},
{"uvx", "application/vnd.dece.unspecified"},
{"uvz", "application/vnd.dece.zip"},
{"vcard", "text/vcard"},
{"vcd", "application/x-cdlink"},
{"vcf", "text/x-vcard"},
{"vcg", "application/vnd.groove-vcard"},
{"vcs", "text/x-vcalendar"},
{"vcx", "application/vnd.vcx"},
{"vis", "application/vnd.visionary"},
{"viv", "video/vnd.vivo"},
{"vob", "video/x-ms-vob"},
{"vor", "application/vnd.stardivision.writer"},
{"vox", "application/x-authorware-bin"},
{"vrml", "model/vrml"},
{"vsd", "application/vnd.visio"},
{"vsf", "application/vnd.vsf"},
{"vss", "application/vnd.visio"},
{"vst", "application/vnd.visio"},
{"vsw", "application/vnd.visio"},
{"vtu", "model/vnd.vtu"},
{"vxml", "application/voicexml+xml"},
{"w3d", "application/x-director"},
{"wad", "application/x-doom"},
{"wav", "audio/x-wav"},
{"wax", "audio/x-ms-wax"},
{"wbmp", "image/vnd.wap.wbmp"},
{"wbs", "application/vnd.criticaltools.wbs+xml"},
{"wbxml", "application/vnd.wap.wbxml"},
{"wcm", "application/vnd.ms-works"},
{"wdb", "application/vnd.ms-works"},
{"wdp", "image/vnd.ms-photo"},
{"weba", "audio/webm"},
{"webm", "video/webm"},
{"webp", "image/webp"},
{"wg", "application/vnd.pmi.widget"},
{"wgt", "application/widget"},
{"wks", "application/vnd.ms-works"},
{"wm", "video/x-ms-wm"},
{"wma", "audio/x-ms-wma"},
{"wmd", "application/x-ms-wmd"},
{"wmf", "application/x-msmetafile"},
{"wml", "text/vnd.wap.wml"},
{"wmlc", "application/vnd.wap.wmlc"},
{"wmls", "text/vnd.wap.wmlscript"},
{"wmlsc", "application/vnd.wap.wmlscriptc"},
{"wmv", "video/x-ms-wmv"},
{"wmx", "video/x-ms-wmx"},
{"wmz", "application/x-ms-wmz"},
{"wmz", "application/x-msmetafile"},
{"woff", "application/font-woff"},
{"wpd", "application/vnd.wordperfect"},
{"wpl", "application/vnd.ms-wpl"},
{"wps", "application/vnd.ms-works"},
{"wqd", "application/vnd.wqd"},
{"wri", "application/x-mswrite"},
{"wrl", "model/vrml"},
{"wsdl", "application/wsdl+xml"},
{"wspolicy", "application/wspolicy+xml"},
{"wtb", "application/vnd.webturbo"},
{"wvx", "video/x-ms-wvx"},
{"x32", "application/x-authorware-bin"},
{"x3d", "model/x3d+xml"},
{"x3db", "model/x3d+binary"},
{"x3dbz", "model/x3d+binary"},
{"x3dv", "model/x3d+vrml"},
{"x3dvz", "model/x3d+vrml"},
{"x3dz", "model/x3d+xml"},
{"xaml", "application/xaml+xml"},
{"xap", "application/x-silverlight-app"},
{"xar", "application/vnd.xara"},
{"xbap", "application/x-ms-xbap"},
{"xbd", "application/vnd.fujixerox.docuworks.binder"},
{"xbm", "image/x-xbitmap"},
{"xdf", "application/xcap-diff+xml"},
{"xdm", "application/vnd.syncml.dm+xml"},
{"xdp", "application/vnd.adobe.xdp+xml"},
{"xdssc", "application/dssc+xml"},
{"xdw", "application/vnd.fujixerox.docuworks"},
{"xenc", "application/xenc+xml"},
{"xer", "application/patch-ops-error+xml"},
{"xfdf", "application/vnd.adobe.xfdf"},
{"xfdl", "application/vnd.xfdl"},
{"xht", "application/xhtml+xml"},
{"xhtml", "application/xhtml+xml"},
{"xhvml", "application/xv+xml"},
{"xif", "image/vnd.xiff"},
{"xla", "application/vnd.ms-excel"},
{"xlam", "application/vnd.ms-excel.addin.macroenabled.12"},
{"xlc", "application/vnd.ms-excel"},
{"xlf", "application/x-xliff+xml"},
{"xlm", "application/vnd.ms-excel"},
{"xls", "application/vnd.ms-excel"},
{"xlsb", "application/vnd.ms-excel.sheet.binary.macroenabled.12"},
{"xlsm", "application/vnd.ms-excel.sheet.macroenabled.12"},
{"xlsx",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
{"xlt", "application/vnd.ms-excel"},
{"xltm", "application/vnd.ms-excel.template.macroenabled.12"},
{"xltx", "application/"
"vnd.openxmlformats-officedocument.spreadsheetml.template"},
{"xlw", "application/vnd.ms-excel"},
{"xm", "audio/xm"},
{"xml", "application/xml"},
{"xo", "application/vnd.olpc-sugar"},
{"xop", "application/xop+xml"},
{"xpi", "application/x-xpinstall"},
{"xpl", "application/xproc+xml"},
{"xpm", "image/x-xpixmap"},
{"xpr", "application/vnd.is-xpr"},
{"xps", "application/vnd.ms-xpsdocument"},
{"xpw", "application/vnd.intercon.formnet"},
{"xpx", "application/vnd.intercon.formnet"},
{"xsl", "application/xml"},
{"xslt", "application/xslt+xml"},
{"xsm", "application/vnd.syncml+xml"},
{"xspf", "application/xspf+xml"},
{"xul", "application/vnd.mozilla.xul+xml"},
{"xvm", "application/xv+xml"},
{"xvml", "application/xv+xml"},
{"xwd", "image/x-xwindowdump"},
{"xyz", "chemical/x-xyz"},
{"xz", "application/x-xz"},
{"yang", "application/yang"},
{"yin", "application/yin+xml"},
{"z1", "application/x-zmachine"},
{"z2", "application/x-zmachine"},
{"z3", "application/x-zmachine"},
{"z4", "application/x-zmachine"},
{"z5", "application/x-zmachine"},
{"z6", "application/x-zmachine"},
{"z7", "application/x-zmachine"},
{"z8", "application/x-zmachine"},
{"zaz", "application/vnd.zzazz.deck+xml"},
{"zip", "application/zip"},
{"zir", "application/vnd.zul"},
{"zirz", "application/vnd.zul"},
{"zmm", "application/vnd.handheld-entertainment+xml"},
{{0}, NULL},
};
// Copy 8 bytes of the requested extension.
// (8 byte comparison in enough to avoid collisions)
uint64_t ext8byte = 0;
char *extlow = (void *)(&ext8byte);
// change the copy to lowercase
size_t pos = 0;
while (ext[pos] && pos < 8) {
extlow[pos] =
(ext[pos] >= 'A' && ext[pos] <= 'Z') ? (ext[pos] | 32) : ext[pos];
++pos;
}
// optimize starting position
uint8_t start = (uint8_t)extlow[0];
// skip unnecessary reviews
if (start >= 'u')
pos = 800;
else if (start >= 'r')
pos = 640;
else if (start >= 'n')
pos = 499;
else if (start >= 'm')
pos = 400;
else if (start >= 'i')
pos = 300;
else if (start >= 'e')
pos = 190;
else
pos = 0;
// check for 8 byte comparison - should be fast on 64 bit systems.
uint64_t *lstext;
while (List[pos].ext[0]) {
lstext = (uint64_t *)List[pos].ext;
if (ext8byte == *lstext)
return List[pos].mime;
pos++;
}
return NULL;
}