| /* |
| 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; |
| } |