blob: 45c560013b9df7ef52d43dbd29bc583adc95a767 [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
// clang-format off
#include "http_response_http1.h"
// clang-format on
#include "base64.h"
#include "http.h"
#include "siphash.h"
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
/* *****************************************************************************
Helpers
***************************************************************************** */
#define forward_func(response, func, ...) \
if ((response)->metadata.version == 1) { \
return h1p_##func(__VA_ARGS__); \
} else \
return -1;
#define perform_func(response, func, ...) \
if ((response)->metadata.version == 1) { \
h1p_##func(__VA_ARGS__); \
}
// static char* MONTH_NAMES[] = {"Jan ", "Feb ", "Mar ", "Apr ", "May ", "Jun ",
// "Jul ", "Aug ", "Sep ", "Oct ", "Nov ", "Dec
// "};
//
#define is_num(c) ((c) >= '0' && (c) <= '9')
#define num_val(c) ((c)-48)
/* *****************************************************************************
API implementation
***************************************************************************** */
/**
Initializes a response object with the request object. This function assumes the
response object memory is garbage and might have been stack-allocated.
Notice that the `http_request_s` pointer must point at a valid request object
and that the request object must remain valid until the response had been
completed.
Hangs on failuer (waits for available resources).
*/
http_response_s http_response_init(http_request_s *request) {
protocol_s *http = request->metadata.owner;
time_t date = server_last_tick();
return (http_response_s){
.metadata.request = request,
.metadata.fd = request->metadata.fd,
.metadata.packet = sock_checkout_packet(),
.status = 200,
.date = date,
.last_modified = date,
.metadata.version = (http->service == HTTP1 ? 1 : 0),
.metadata.should_close =
(request && request->connection &&
request->connection_len ==
5), // don't check header value, length is unique enough.
};
}
/**
Releases any resources held by the response object (doesn't release the response
object itself, which might have been allocated on the stack).
This function assumes the response object might have been stack-allocated.
*/
void http_response_destroy(http_response_s *response) {
if (response->metadata.packet) {
sock_free_packet(response->metadata.packet);
response->metadata.packet = NULL;
}
}
/**
Writes a header to the response. This function writes only the requested
number of bytes from the header name and the requested number of bytes from
the header value. It can be used even when the header name and value don't
contain NULL terminating bytes by passing the `.name_len` or `.value_len` data
in the `http_headers_s` structure.
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_write_header
int http_response_write_header(http_response_s *response,
http_headers_s header) {
// check if the header is a reserved header
if ((header.name_length == 4 || header.name_length == 0) &&
strncasecmp(header.name, "date", 4) == 0)
response->metadata.date_written = 1;
else if ((header.name_length == 10 || header.name_length == 0) &&
strncasecmp(header.name, "connection", 10) == 0) {
response->metadata.connection_written = 1;
if (header.value && header.value[0] == 'c')
response->metadata.should_close = 1;
} else if ((header.name_length == 14 || header.name_length == 0) &&
strncasecmp(header.name, "content-length", 14) == 0)
response->metadata.content_length_written = 1;
// write the header to the protocol
forward_func(response, response_write_header, response, header);
}
#define http_response_write_header(response, ...) \
http_response_write_header(response, (http_headers_s){__VA_ARGS__})
/**
Set / Delete a cookie using this helper function.
To set a cookie, use (in this example, a session cookie):
http_response_set_cookie(response,
.name = "my_cookie",
.value = "data");
To delete a cookie, use:
http_response_set_cookie(response,
.name = "my_cookie",
.value = NULL);
This function writes a cookie header to the response. Only the requested
number of bytes from the cookie value and name are written (if none are
provided, a terminating NULL byte is assumed).
Both the name and the value of the cookie are checked for validity (legal
characters), but other properties aren't reviewed (domain/path) - please make
sure to use only valid data, as HTTP imposes restrictions on these things.
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) {
forward_func(response, response_set_cookie, response, cookie);
};
/**
Indicates that any pending data (i.e. unsent headers) should be sent and that no
more use of the response object will be made.
*/
void http_response_finish(http_response_s *response) {
perform_func(response, response_finish, response);
if (response->metadata.logged)
http_response_log_finish(response);
http_response_destroy(response);
}
/**
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) {
forward_func(response, response_write_body, response, body, length);
}
/**
Sends the headers (if they weren't previously sent) and writes the data to the
underlying socket.
The server's outgoing buffer will take ownership of the file and close it
using `fclose` once the data was sent.
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) {
forward_func(response, response_sendfile, 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).
If a `file_path_unsafe` is provided, it will be appended to the `file_path_safe`
(if any) and URL decoded before attempting to locate and open the file. Any
insecure path manipulations in the `file_path_unsafe` (i.e. `..` or `//`) will
cause the function to fail.
`file_path_unsafe` MUST begine with a `/`, or it will be appended to
`file_path_safe` as part of the last folder's name. if `file_path_safe` ends
with a `/`, it will be trancated.
If the `log` flag is set, response logging will be performed.
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;
http_response_s tmp_response;
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;
struct stat file_data = {};
// fprintf(stderr, "\n\noriginal request path: %s\n", req->path);
char *fname = malloc(path_safe_len + path_unsafe_len + 1 + 11);
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_url(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)
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) {
response = &tmp_response;
tmp_response = http_response_init(request);
if (log)
http_response_log_start(response);
}
// we have a file, time to handle response details.
int file = open(fname, O_RDONLY);
if (file == -1) {
goto no_file;
}
// free the allocated fname memory
free(fname);
fname = NULL;
// 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_length = 12, .value = mime);
}
}
/* add ETag */
uint64_t sip = file_data.st_size;
sip ^= 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_length = 4,
.value = buffer, .value_length = 12);
response->last_modified = file_data.st_mtime;
http_response_write_header(response, .name = "Cache-Control",
.name_length = 13, .value = "max-age=3600",
.value_length = 12);
/* check etag */
if ((ext = http_request_find_header(request, "if-none-match", 13)) &&
memcmp(ext, buffer, 12) == 0) {
/* send back 304 */
response->status = 304;
close(file);
perform_func(response, response_finish, response);
if (log)
http_response_log_finish(response);
return 0;
}
// Range handling
if ((ext = http_request_find_header(request, "range", 5)) &&
(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 (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 (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_length = 13, .value = buffer,
.value_length = pos - buffer);
response->status = 206;
http_response_write_header(response, .name = "Accept-Ranges",
.name_length = 13, .value = "bytes",
.value_length = 5);
if (*((uint32_t *)request->method) == *((uint32_t *)HEAD)) {
response->content_length = 0;
close(file);
perform_func(response, response_finish, response);
if (log)
http_response_log_finish(response);
return 0;
}
http_response_sendfile(response, file, start, finish - start + 1);
if (log)
http_response_log_finish(response);
return 0;
}
invalid_range:
http_response_write_header(response, .name = "Accept-Ranges",
.name_length = 13, .value = "none",
.value_length = 4);
if (*((uint32_t *)request->method) == *((uint32_t *)HEAD)) {
response->content_length = 0;
close(file);
perform_func(response, response_finish, response);
if (log)
http_response_log_finish(response);
return 0;
}
http_response_sendfile(response, file, 0, file_data.st_size);
if (log)
http_response_log_finish(response);
return 0;
no_file:
free(fname);
return -1;
}
#ifdef RUSAGE_SELF
const static 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->metadata.clock_start = get_clock_mili();
response->metadata.logged = 1;
}
/**
prints out the log to stderr.
*/
void http_response_log_finish(http_response_s *response) {
http_request_s *request = response->metadata.request;
uintptr_t bytes_sent = (uintptr_t)response->metadata.headers_pos;
size_t mili = response->metadata.logged
? ((get_clock_mili() - response->metadata.clock_start) /
CLOCK_RESOLUTION)
: 0;
struct tm tm;
struct sockaddr_in addrinfo;
socklen_t addrlen = sizeof(addrinfo);
time_t last_tick = server_last_tick();
http_gmtime(&last_tick, &tm);
// TODO Guess IP address from headers (forwarded) where possible
int got_add = getpeername(sock_uuid2fd(request->metadata.fd),
(struct sockaddr *)&addrinfo, &addrlen);
#define HTTP_REQUEST_LOG_LIMIT 128
char buffer[HTTP_REQUEST_LOG_LIMIT];
char *tmp;
size_t pos;
if (got_add == 0) {
tmp = inet_ntoa(addrinfo.sin_addr);
pos = strlen(tmp);
memcpy(buffer, tmp, pos);
} else {
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++] = ' ';
pos += http_ul2a(buffer + pos, bytes_sent);
if (response->metadata.logged) {
buffer[pos++] = ' ';
pos += http_ul2a(buffer + pos, mili);
buffer[pos++] = 'm';
buffer[pos++] = 's';
}
buffer[pos++] = '\n';
response->metadata.logged = 0;
fwrite(buffer, 1, pos, stderr);
}
/* *****************************************************************************
Hardcded lists / matching
*****************************************************************************
*/
/** 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;
}