//
//  OSSUtil.m
//  oss_ios_sdk
//
//  Created by zhouzhuo on 8/16/15.
//  Copyright (c) 2015 aliyun.com. All rights reserved.
//

#import "OSSUtil.h"
#import <mach/mach.h>
#import "CommonCrypto/CommonDigest.h"
#import "CommonCrypto/CommonHMAC.h"
#import "OSSModel.h"
#import "OSSLog.h"
#import "OSSHttpdns.h"
#import "OSSDefine.h"
#import "OSSIPv6Adapter.h"
#import "OSSReachability.h"
#import <CoreTelephony/CTCarrier.h>
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import "aos_crc64.h"

NSString * const ALIYUN_HOST_SUFFIX = @".aliyuncs.com";
NSString * const ALIYUN_OSS_TEST_ENDPOINT = @".aliyun-inc.com";
int32_t const CHUNK_SIZE = 8 * 1024;

@implementation OSSUtil

+ (BOOL)isIncludeCnameExcludeList:(NSArray *)cnameExcludeList host:(NSString *)host {
    for (NSString *cnameExclude in cnameExcludeList) {
        if ([host hasSuffix:cnameExclude]) {
            return YES;
        }
    }
    return NO;
}

+ (NSString *)calBase64Sha1WithData:(NSString *)data withSecret:(NSString *)key {
    NSData *secretData = [key dataUsingEncoding:NSUTF8StringEncoding];
    NSData *clearTextData = [data dataUsingEncoding:NSUTF8StringEncoding];
    uint8_t input[20];
    CCHmac(kCCHmacAlgSHA1, [secretData bytes], [secretData length], [clearTextData bytes], [clearTextData length], input);

    return [self calBase64WithData:input];
}

+ (NSString*)calBase64WithData:(uint8_t *)data {
    static char b[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    NSInteger a = 20;
    NSMutableData* c = [NSMutableData dataWithLength:((a + 2) / 3) * 4];
    uint8_t* d = (uint8_t*)c.mutableBytes;
    NSInteger i;
    for (i=0; i < a; i += 3) {
        NSInteger e = 0;
        NSInteger j;
        for (j = i; j < (i + 3); j++) {
            e <<= 8;
            if (j < a) {
                e |= (0xFF & data[j]);
            }
        }
        NSInteger index = (i / 3) * 4;
        d[index + 0] = b[(e >> 18) & 0x3F];
        d[index + 1] = b[(e >> 12) & 0x3F];
        if ((i + 1) < a) {
            d[index + 2] = b[(e >> 6) & 0x3F];
        } else {
            d[index + 2] = '=';
        }
        if ((i + 2) < a) {
            d[index + 3] = b[(e >> 0) & 0x3F];
        } else {
            d[index + 3] = '=';
        }
    }
    NSString *result = [[NSString alloc] initWithData:c encoding:NSASCIIStringEncoding];
    return result;
}

+ (NSString *)encodeURL:(NSString *)url {
    //保持和android处理方式一致，添加+ -> %20，* -> %2A，%7E -> ~, "%2F" -> /
    NSMutableString *output = [NSMutableString string];
    const unsigned char *source = (const unsigned char *)[url UTF8String];
    NSUInteger sourceLen = strlen((const char *)source);
    for (int i = 0; i < sourceLen; ++i) {
        const unsigned char thisChar = source[i];
        if (thisChar == ' ') {
            [output appendString:@"%20"];
        } else if (thisChar == '*') {
            [output appendString:@"%2A"];
        } else if (thisChar == '.' || thisChar == '-' || thisChar == '_' || thisChar == '~' ||
                   (thisChar >= 'a' && thisChar <= 'z') ||
                   (thisChar >= 'A' && thisChar <= 'Z') ||
                   (thisChar >= '0' && thisChar <= '9')) {
            [output appendFormat:@"%c", thisChar];
        } else {
            [output appendFormat:@"%%%02X", thisChar];
        }
    }
    NSString *encodeUrl = [output stringByReplacingOccurrencesOfString:@"%2F" withString:@"/"];
    encodeUrl = [encodeUrl stringByReplacingOccurrencesOfString:@"%7E" withString:@"~"];
    return encodeUrl;

    
//  不要用系统urlencode 的方式，很多特殊字符都没有转化；
//  详见：https://stackoverflow.com/questions/8088473/how-do-i-url-encode-a-string

}

+ (NSString *)encodeResourcePath:(NSString *)resourcePath {
    //保持和android处理方式一致，添加+ -> %20，* -> %2A，%7E -> ~, "%2F" -> /
    NSMutableString *output = [NSMutableString string];
    const unsigned char *source = (const unsigned char *)[resourcePath UTF8String];
    NSUInteger sourceLen = strlen((const char *)source);
    for (int i = 0; i < sourceLen; ++i) {
        const unsigned char thisChar = source[i];
        if (thisChar == ' ') {
            [output appendString:@"%20"];
        } else if (thisChar == '*') {
            [output appendString:@"%2A"];
        } else if (thisChar == '.' || thisChar == '-' || thisChar == '_' || thisChar == '~' || thisChar == '/' ||
                   (thisChar >= 'a' && thisChar <= 'z') ||
                   (thisChar >= 'A' && thisChar <= 'Z') ||
                   (thisChar >= '0' && thisChar <= '9')) {
            [output appendFormat:@"%c", thisChar];
        } else {
            [output appendFormat:@"%%%02X", thisChar];
        }
    }
    NSString *encodeUrl = [output stringByReplacingOccurrencesOfString:@"%2F" withString:@"/"];
    encodeUrl = [encodeUrl stringByReplacingOccurrencesOfString:@"%7E" withString:@"~"];
    return encodeUrl;

    
//  不要用系统urlencode 的方式，很多特殊字符都没有转化；
//  详见：https://stackoverflow.com/questions/8088473/how-do-i-url-encode-a-string

}

+ (NSString *)encodeQuery:(NSString *)query {
    NSMutableString *output = [NSMutableString string];
    const unsigned char *source = (const unsigned char *)[query UTF8String];
    NSUInteger sourceLen = strlen((const char *)source);
    for (int i = 0; i < sourceLen; ++i) {
        const unsigned char thisChar = source[i];
        if (thisChar == ' ') {
            [output appendString:@"%20"];
        } else if (thisChar == '*') {
            [output appendString:@"%2A"];
        } else if (thisChar == '.' || thisChar == '-' || thisChar == '_' || thisChar == '~' ||
                   (thisChar >= 'a' && thisChar <= 'z') ||
                   (thisChar >= 'A' && thisChar <= 'Z') ||
                   (thisChar >= '0' && thisChar <= '9')) {
            [output appendFormat:@"%c", thisChar];
        } else {
            [output appendFormat:@"%%%02X", thisChar];
        }
    }
    NSString *encodeUrl = [output stringByReplacingOccurrencesOfString:@"%7E" withString:@"~"];
    return encodeUrl;
}

+ (NSData *)constructHttpBodyFromPartInfos:(NSArray *)partInfos {
    NSMutableString * body = [NSMutableString stringWithString:@"<CompleteMultipartUpload>\n"];
    [partInfos enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        if ([obj isKindOfClass:[OSSPartInfo class]]) {
            OSSPartInfo * thePart = obj;
            [body appendFormat:@"<Part>\n<PartNumber>%d</PartNumber>\n<ETag>%@</ETag>\n</Part>\n", thePart.partNum, thePart.eTag];
        }
    }];
    [body appendString:@"</CompleteMultipartUpload>\n"];
    OSSLogVerbose(@"constucted complete multipart upload body:\n%@", body);
    return [body dataUsingEncoding:NSUTF8StringEncoding];
}

+ (NSData *)constructHttpBodyForDeleteMultipleObjects:(NSArray<NSString *> *)keys quiet:(BOOL)quiet {
    NSMutableString * body = [NSMutableString stringWithString:@"<Delete>\n"];
    [body appendFormat:@"<Quiet>%@</Quiet>\n",quiet?@"true":@"false"];
    [keys enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop) {
        [body appendFormat:@"<Object>\n<Key>%@</Key>\n</Object>\n", key];
    }];
    [body appendString:@"</Delete>\n"];
    OSSLogVerbose(@"constucted delete multiple objects body:\n%@", body);
    return [body dataUsingEncoding:NSUTF8StringEncoding];
}

+ (NSData *)constructHttpBodyForCreateBucketWithLocation:(NSString *)location {
    NSString * body = [NSString stringWithFormat:@"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
                       @"<CreateBucketConfiguration>\n"
                       @"<LocationConstraint>%@</LocationConstraint>\n"
                       @"</CreateBucketConfiguration>\n",
                       location];
    OSSLogVerbose(@"constucted create bucket body:\n%@", body);
    return [body dataUsingEncoding:NSUTF8StringEncoding];
}

+ (BOOL)validateBucketName:(NSString *)bucketName {
    if (bucketName == nil) {
        return false;
    }

    static NSRegularExpression *regEx;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        regEx = [[NSRegularExpression alloc] initWithPattern:@"^[a-z0-9][a-z0-9\\-]{1,61}[a-z0-9]$" options:NSRegularExpressionCaseInsensitive error:nil];
    });
    NSUInteger regExMatches = [regEx numberOfMatchesInString:bucketName options:0 range:NSMakeRange(0, [bucketName length])];
    return regExMatches != 0;
}

+ (BOOL)validateObjectKey:(NSString *)objectKey {
    if (objectKey == nil) {
        return false;
    }

    if (objectKey.length <= 0 || objectKey.length > 1023) {
        return false;
    }

    if (![objectKey canBeConvertedToEncoding:NSUTF8StringEncoding]) {
        return false;
    }

    unichar firstChar = [objectKey characterAtIndex:0];
    if (firstChar == '/' || firstChar == '\\') {
        return false;
    }

    return true;
}

+ (NSString *)getIpByHost:(NSString *)host {
    if ([self isNetworkDelegateState]) {
        OSSLogDebug(@"current network is delegate state");
        return host;
    }
    NSString * ip = [[OSSHttpdns sharedInstance] asynGetIpByHost:host];
    OSSLogDebug(@"resolved host %@ and get ip: %@", host, ip);

    return ip ? [[OSSIPv6Adapter getInstance] handleIpv4Address:ip] : host;
}

+ (BOOL)isNetworkDelegateState {
    NSURL* URL = [[NSURL alloc] initWithString:@"https://m.aliyun.com"];
    NSDictionary *proxySettings = CFBridgingRelease(CFNetworkCopySystemProxySettings());
    NSArray *proxies = nil;
    proxies = CFBridgingRelease(CFNetworkCopyProxiesForURL((__bridge CFURLRef)URL,
                                                           (__bridge CFDictionaryRef)proxySettings));
    if (proxies.count) {
        NSDictionary *settings = [proxies objectAtIndex:0];
        NSString* host = [settings objectForKey:(NSString *)kCFProxyHostNameKey];
        NSNumber* port = [settings objectForKey:(NSString *)kCFProxyPortNumberKey];
        if (host && port) {
            return YES;
        }
    }
    return NO;
}

+ (BOOL)isOssOriginBucketHost:(NSString *)host {
    return [[host lowercaseString] hasSuffix:ALIYUN_HOST_SUFFIX] || [[host lowercaseString] hasSuffix:ALIYUN_OSS_TEST_ENDPOINT];
}

+ (NSString *)base64Md5ForData:(NSData *)data {
    uint8_t * bytes = (uint8_t *)[[self dataMD5:data] bytes];
    return [self base64ForData:bytes length:CC_MD5_DIGEST_LENGTH];
}

+ (NSString *)base64Md5ForFilePath:(NSString *)filePath {
    uint8_t * bytes = (uint8_t *)[[self fileMD5:filePath] bytes];
    return [self base64ForData:bytes length:CC_MD5_DIGEST_LENGTH];
}

+ (NSString *)base64Md5ForFileURL:(NSURL *)fileURL {
    return [self base64Md5ForFilePath:[fileURL path]];
}

+ (NSData *)dataMD5:(NSData *)data {
    if(data == nil) {
        return nil;
    }
    CC_MD5_CTX md5;
    CC_MD5_Init(&md5);
    for (int i = 0; i < data.length; i += CHUNK_SIZE) {
        NSData *subdata = nil;
        if (i <= ((long)data.length - CHUNK_SIZE)) {
            subdata = [data subdataWithRange:NSMakeRange(i, CHUNK_SIZE)];
            CC_MD5_Update(&md5, [subdata bytes], (CC_LONG)[subdata length]);
        } else {
            subdata = [data subdataWithRange:NSMakeRange(i, data.length - i)];
            CC_MD5_Update(&md5, [subdata bytes], (CC_LONG)[subdata length]);
        }
    }
    unsigned char digestResult[CC_MD5_DIGEST_LENGTH * sizeof(unsigned char)];
    CC_MD5_Final(digestResult, &md5);
    return [NSData dataWithBytes:(const void *)digestResult length:CC_MD5_DIGEST_LENGTH * sizeof(unsigned char)];
}

+ (NSData *)fileMD5:(NSString*)path {
    NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:path];
    if(handle == nil) {
        return nil;
    }
    CC_MD5_CTX md5;
    CC_MD5_Init(&md5);
    BOOL done = NO;
    while(!done) {
        @autoreleasepool{
            NSData* fileData = [handle readDataOfLength: CHUNK_SIZE];
            CC_MD5_Update(&md5, [fileData bytes], (CC_LONG)[fileData length]);
            if([fileData length] == 0) {
                done = YES;
            }
        }
    }
    if (@available(iOS 13.0, *)) {
        [handle closeAndReturnError:nil];
    } else {
        [handle closeFile];
    }
    unsigned char digestResult[CC_MD5_DIGEST_LENGTH * sizeof(unsigned char)];
    CC_MD5_Final(digestResult, &md5);
    return [NSData dataWithBytes:(const void *)digestResult length:CC_MD5_DIGEST_LENGTH * sizeof(unsigned char)];
}

+ (NSData *)fileMD5:(NSString*)path error:(NSError **)error {
    NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:path];
    if(handle == nil) {
        return nil;
    }
    CC_MD5_CTX md5;
    CC_MD5_Init(&md5);
    BOOL done = NO;
    while(!done) {
        @autoreleasepool{
            NSData *fileData;
            if (@available(iOS 13.0, *)) {
                fileData = [handle readDataUpToLength:CHUNK_SIZE error:error];
            } else {
                @try {
                    fileData = [handle readDataOfLength: CHUNK_SIZE];
                } @catch(NSException *exception) {
                    *error = [NSError errorWithDomain:OSSClientErrorDomain
                                                 code:OSSClientErrorCodeFileCantRead
                                             userInfo:@{OSSErrorMessageTOKEN: [exception description]}];
                }
            }
            CC_MD5_Update(&md5, [fileData bytes], (CC_LONG)[fileData length]);
            if([fileData length] == 0) {
                done = YES;
            }
        }
    }
    if (@available(iOS 13.0, *)) {
        [handle closeAndReturnError:error];
    } else {
        [handle closeFile];
    }
    unsigned char digestResult[CC_MD5_DIGEST_LENGTH * sizeof(unsigned char)];
    CC_MD5_Final(digestResult, &md5);
    return [NSData dataWithBytes:(const void *)digestResult length:CC_MD5_DIGEST_LENGTH * sizeof(unsigned char)];
}

+ (NSString *)convertMd5Bytes2String:(unsigned char *)md5Bytes {
    return [NSString stringWithFormat:
            @"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
            md5Bytes[0], md5Bytes[1], md5Bytes[2], md5Bytes[3],
            md5Bytes[4], md5Bytes[5], md5Bytes[6], md5Bytes[7],
            md5Bytes[8], md5Bytes[9], md5Bytes[10], md5Bytes[11],
            md5Bytes[12], md5Bytes[13], md5Bytes[14], md5Bytes[15]
            ];
}

+ (NSString *)convertMd5Bytes2StringWithData:(NSData *)data error:(NSError **)error {
    if (!data || [data length] == 0) {
        *error = [NSError errorWithDomain:OSSClientErrorDomain
                                     code:OSSClientErrorCodeInvalidArgument
                                 userInfo:@{OSSErrorMessageTOKEN: @"data is null or data length is 0"}];
        return nil;
    }
    NSMutableString *string = [[NSMutableString alloc] initWithCapacity:[data length]];
    
    [data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) {
        unsigned char *dataBytes = (unsigned char*)bytes;
        for (NSInteger i = 0; i < byteRange.length; i++) {
            NSString *hexStr = [NSString stringWithFormat:@"%02X", dataBytes[i]];
            [string appendString:hexStr];
        }
    }];
    
    return string;
}

+ (NSString *)dataMD5String:(NSData *)data error:(NSError **)error {
    data = [self dataMD5:data];
    return [self convertMd5Bytes2StringWithData:data error:error];
}

+ (NSString *)fileMD5String:(NSString *)path error:(NSError **)error {
    BOOL isDirectory = NO;
    BOOL isExist = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDirectory];
    if (isDirectory || !isExist) {
        OSSLogWarn(@"a file doesn't exists at a specified path(%@)", path);
        return nil;
    }

    NSData *data = [self fileMD5:path error:error];
    return [self convertMd5Bytes2StringWithData:data error:error];
}

+ (NSString *)dataMD5String:(NSData *)data {
    unsigned char * md5Bytes = (unsigned char *)[[self dataMD5:data] bytes];
    return [self convertMd5Bytes2String:md5Bytes];
}

+ (NSString *)fileMD5String:(NSString *)path {
    BOOL isDirectory = NO;
    BOOL isExist = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDirectory];
    if (isDirectory || !isExist) {
        OSSLogWarn(@"a file doesn't exists at a specified path(%@)", path);
        return nil;
    }

    unsigned char * md5Bytes = (unsigned char *)[[self fileMD5:path] bytes];
    return [self convertMd5Bytes2String:md5Bytes];
}

+ (NSString*)base64ForData:(uint8_t *)input length:(int32_t)length {
    if (input == nil) {
        return nil;
    }
    NSData * data = [NSData dataWithBytes:input length:length];
    return [data base64EncodedStringWithOptions: NSDataBase64Encoding64CharacterLineLength];
}

+ (BOOL)isSubresource:(NSString *)param {
    /****************************************************************
    * define a constant array to contain all specified subresource */
    static NSArray * OSSSubResourceARRAY = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        OSSSubResourceARRAY = @[
            @"acl", @"uploads", @"location", @"cors", @"logging", @"website", @"referer", @"lifecycle", @"delete", @"append",
            @"tagging", @"objectMeta", @"uploadId", @"partNumber", @"security-token", @"position", @"img", @"style",
            @"styleName", @"replication", @"replicationProgress", @"replicationLocation", @"cname", @"bucketInfo", @"comp",
            @"qos", @"live", @"status", @"vod", @"startTime", @"endTime", @"symlink", @"x-oss-process", @"response-content-type",
            @"response-content-language", @"response-expires", @"response-cache-control", @"response-content-disposition", @"response-content-encoding",@"restore", @"sequential"
            ];
    });
    /****************************************************************/

    return [OSSSubResourceARRAY containsObject:param];
}

+ (NSString *)populateSubresourceStringFromParameter:(NSDictionary *)parameters {
    NSMutableArray * subresource = [NSMutableArray new];
    [parameters enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        NSString * keyStr = [key oss_trim];
        NSString * valueStr = [obj oss_trim];
        if (![OSSUtil isSubresource:keyStr]) {
            return;
        }
        if ([valueStr length] == 0) {
            [subresource addObject:keyStr];
        } else {
            [subresource addObject:[NSString stringWithFormat:@"%@=%@", keyStr, valueStr]];
        }
    }];
    NSArray * sortedSubResource = [subresource sortedArrayUsingSelector:@selector(compare:)]; // 升序
    return [sortedSubResource componentsJoinedByString:@"&"];
}

+ (NSString *)populateQueryStringFromParameter:(NSDictionary *)parameters {
    NSMutableArray * subresource = [NSMutableArray new];
    [parameters enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        NSString * keyStr = [OSSUtil encodeURL:[key oss_trim]];
        NSString * valueStr = [OSSUtil encodeURL:[obj oss_trim]];
        if ([valueStr length] == 0) {
            [subresource addObject:keyStr];
        } else {
            [subresource addObject:[NSString stringWithFormat:@"%@=%@", keyStr, valueStr]];
        }
    }];
    return [subresource componentsJoinedByString:@"&"];
}

+ (NSString *)sign:(NSString *)content withToken:(OSSFederationToken *)token {
    NSString * sign = [OSSUtil calBase64Sha1WithData:content withSecret:token.tSecretKey];
    return [NSString stringWithFormat:@"OSS %@:%@", token.tAccessKey, sign];
}

+ (NSString *)getRelativePath:(NSString *)fullPath {
    NSString * rootPath = NSHomeDirectory();
    return [fullPath stringByReplacingOccurrencesOfString:rootPath withString:@""];
}

+ (NSString *)detemineMimeTypeForFilePath:(NSString *)filePath uploadName:(NSString *)uploadName {

    static dispatch_once_t onceToken;
    static NSDictionary * mimeMap;
    dispatch_once(&onceToken, ^{
        mimeMap = @{
            @"aw": @"application/applixware",
            @"atom": @"application/atom+xml",
            @"atomcat": @"application/atomcat+xml",
            @"atomsvc": @"application/atomsvc+xml",
            @"ccxml": @"application/ccxml+xml",
            @"cdmia": @"application/cdmi-capability",
            @"cdmic": @"application/cdmi-container",
            @"cdmid": @"application/cdmi-domain",
            @"cdmio": @"application/cdmi-object",
            @"cdmiq": @"application/cdmi-queue",
            @"cu": @"application/cu-seeme",
            @"davmount": @"application/davmount+xml",
            @"dssc": @"application/dssc+der",
            @"xdssc": @"application/dssc+xml",
            @"es": @"application/ecmascript",
            @"emma": @"application/emma+xml",
            @"epub": @"application/epub+zip",
            @"exi": @"application/exi",
            @"pfr": @"application/font-tdpfr",
            @"stk": @"application/hyperstudio",
            @"ipfix": @"application/ipfix",
            @"jar": @"application/java-archive",
            @"ser": @"application/java-serialized-object",
            @"class": @"application/java-vm",
            @"js": @"application/javascript",
            @"json": @"application/json",
            @"hqx": @"application/mac-binhex40",
            @"cpt": @"application/mac-compactpro",
            @"mads": @"application/mads+xml",
            @"mrc": @"application/marc",
            @"mrcx": @"application/marcxml+xml",
            @"ma": @"application/mathematica",
            @"mathml": @"application/mathml+xml",
            @"mbox": @"application/mbox",
            @"mscml": @"application/mediaservercontrol+xml",
            @"meta4": @"application/metalink4+xml",
            @"mets": @"application/mets+xml",
            @"mods": @"application/mods+xml",
            @"m21": @"application/mp21",
            @"mp4": @"video/mp4",
            @"doc": @"application/msword",
            @"mxf": @"application/mxf",
            @"bin": @"application/octet-stream",
            @"oda": @"application/oda",
            @"opf": @"application/oebps-package+xml",
            @"ogx": @"application/ogg",
            @"onetoc": @"application/onenote",
            @"xer": @"application/patch-ops-error+xml",
            @"pdf": @"application/pdf",
            @"pgp": @"application/pgp-signature",
            @"prf": @"application/pics-rules",
            @"p10": @"application/pkcs10",
            @"p7m": @"application/pkcs7-mime",
            @"p7s": @"application/pkcs7-signature",
            @"p8": @"application/pkcs8",
            @"ac": @"application/pkix-attr-cert",
            @"cer": @"application/pkix-cert",
            @"crl": @"application/pkix-crl",
            @"pkipath": @"application/pkix-pkipath",
            @"pki": @"application/pkixcmp",
            @"pls": @"application/pls+xml",
            @"ai": @"application/postscript",
            @"cww": @"application/prs.cww",
            @"pskcxml": @"application/pskc+xml",
            @"rdf": @"application/rdf+xml",
            @"rif": @"application/reginfo+xml",
            @"rnc": @"application/relax-ng-compact-syntax",
            @"rl": @"application/resource-lists+xml",
            @"rld": @"application/resource-lists-diff+xml",
            @"rs": @"application/rls-services+xml",
            @"rsd": @"application/rsd+xml",
            @"rss": @"application/rss+xml",
            @"rtf": @"application/rtf",
            @"sbml": @"application/sbml+xml",
            @"scq": @"application/scvp-cv-request",
            @"scs": @"application/scvp-cv-response",
            @"spq": @"application/scvp-vp-request",
            @"spp": @"application/scvp-vp-response",
            @"sdp": @"application/sdp",
            @"setpay": @"application/set-payment-initiation",
            @"setreg": @"application/set-registration-initiation",
            @"shf": @"application/shf+xml",
            @"smi": @"application/smil+xml",
            @"rq": @"application/sparql-query",
            @"srx": @"application/sparql-results+xml",
            @"gram": @"application/srgs",
            @"grxml": @"application/srgs+xml",
            @"sru": @"application/sru+xml",
            @"ssml": @"application/ssml+xml",
            @"tei": @"application/tei+xml",
            @"tfi": @"application/thraud+xml",
            @"tsd": @"application/timestamped-data",
            @"plb": @"application/vnd.3gpp.pic-bw-large",
            @"psb": @"application/vnd.3gpp.pic-bw-small",
            @"pvb": @"application/vnd.3gpp.pic-bw-var",
            @"tcap": @"application/vnd.3gpp2.tcap",
            @"pwn": @"application/vnd.3m.post-it-notes",
            @"aso": @"application/vnd.accpac.simply.aso",
            @"imp": @"application/vnd.accpac.simply.imp",
            @"acu": @"application/vnd.acucobol",
            @"atc": @"application/vnd.acucorp",
            @"air": @"application/vnd.adobe.air-application-installer-package+zip",
            @"fxp": @"application/vnd.adobe.fxp",
            @"xdp": @"application/vnd.adobe.xdp+xml",
            @"xfdf": @"application/vnd.adobe.xfdf",
            @"ahead": @"application/vnd.ahead.space",
            @"azf": @"application/vnd.airzip.filesecure.azf",
            @"azs": @"application/vnd.airzip.filesecure.azs",
            @"azw": @"application/vnd.amazon.ebook",
            @"acc": @"application/vnd.americandynamics.acc",
            @"ami": @"application/vnd.amiga.ami",
            @"apk": @"application/vnd.android.package-archive",
            @"cii": @"application/vnd.anser-web-certificate-issue-initiation",
            @"fti": @"application/vnd.anser-web-funds-transfer-initiation",
            @"atx": @"application/vnd.antix.game-component",
            @"mpkg": @"application/vnd.apple.installer+xml",
            @"m3u8": @"application/vnd.apple.mpegurl",
            @"swi": @"application/vnd.aristanetworks.swi",
            @"aep": @"application/vnd.audiograph",
            @"mpm": @"application/vnd.blueice.multipass",
            @"bmi": @"application/vnd.bmi",
            @"rep": @"application/vnd.businessobjects",
            @"cdxml": @"application/vnd.chemdraw+xml",
            @"mmd": @"application/vnd.chipnuts.karaoke-mmd",
            @"cdy": @"application/vnd.cinderella",
            @"cla": @"application/vnd.claymore",
            @"rp9": @"application/vnd.cloanto.rp9",
            @"c4g": @"application/vnd.clonk.c4group",
            @"c11amc": @"application/vnd.cluetrust.cartomobile-config",
            @"c11amz": @"application/vnd.cluetrust.cartomobile-config-pkg",
            @"csp": @"application/vnd.commonspace",
            @"cdbcmsg": @"application/vnd.contact.cmsg",
            @"cmc": @"application/vnd.cosmocaller",
            @"clkx": @"application/vnd.crick.clicker",
            @"clkk": @"application/vnd.crick.clicker.keyboard",
            @"clkp": @"application/vnd.crick.clicker.palette",
            @"clkt": @"application/vnd.crick.clicker.template",
            @"clkw": @"application/vnd.crick.clicker.wordbank",
            @"wbs": @"application/vnd.criticaltools.wbs+xml",
            @"pml": @"application/vnd.ctc-posml",
            @"ppd": @"application/vnd.cups-ppd",
            @"car": @"application/vnd.curl.car",
            @"pcurl": @"application/vnd.curl.pcurl",
            @"rdz": @"application/vnd.data-vision.rdz",
            @"fe_launch": @"application/vnd.denovo.fcselayout-link",
            @"dna": @"application/vnd.dna",
            @"mlp": @"application/vnd.dolby.mlp",
            @"dpg": @"application/vnd.dpgraph",
            @"dfac": @"application/vnd.dreamfactory",
            @"ait": @"application/vnd.dvb.ait",
            @"svc": @"application/vnd.dvb.service",
            @"geo": @"application/vnd.dynageo",
            @"mag": @"application/vnd.ecowin.chart",
            @"nml": @"application/vnd.enliven",
            @"esf": @"application/vnd.epson.esf",
            @"msf": @"application/vnd.epson.msf",
            @"qam": @"application/vnd.epson.quickanime",
            @"slt": @"application/vnd.epson.salt",
            @"ssf": @"application/vnd.epson.ssf",
            @"es3": @"application/vnd.eszigno3+xml",
            @"ez2": @"application/vnd.ezpix-album",
            @"ez3": @"application/vnd.ezpix-package",
            @"fdf": @"application/vnd.fdf",
            @"seed": @"application/vnd.fdsn.seed",
            @"gph": @"application/vnd.flographit",
            @"ftc": @"application/vnd.fluxtime.clip",
            @"fm": @"application/vnd.framemaker",
            @"fnc": @"application/vnd.frogans.fnc",
            @"ltf": @"application/vnd.frogans.ltf",
            @"fsc": @"application/vnd.fsc.weblaunch",
            @"oas": @"application/vnd.fujitsu.oasys",
            @"oa2": @"application/vnd.fujitsu.oasys2",
            @"oa3": @"application/vnd.fujitsu.oasys3",
            @"fg5": @"application/vnd.fujitsu.oasysgp",
            @"bh2": @"application/vnd.fujitsu.oasysprs",
            @"ddd": @"application/vnd.fujixerox.ddd",
            @"xdw": @"application/vnd.fujixerox.docuworks",
            @"xbd": @"application/vnd.fujixerox.docuworks.binder",
            @"fzs": @"application/vnd.fuzzysheet",
            @"txd": @"application/vnd.genomatix.tuxedo",
            @"ggb": @"application/vnd.geogebra.file",
            @"ggt": @"application/vnd.geogebra.tool",
            @"gex": @"application/vnd.geometry-explorer",
            @"gxt": @"application/vnd.geonext",
            @"g2w": @"application/vnd.geoplan",
            @"g3w": @"application/vnd.geospace",
            @"gmx": @"application/vnd.gmx",
            @"kml": @"application/vnd.google-earth.kml+xml",
            @"kmz": @"application/vnd.google-earth.kmz",
            @"gqf": @"application/vnd.grafeq",
            @"gac": @"application/vnd.groove-account",
            @"ghf": @"application/vnd.groove-help",
            @"gim": @"application/vnd.groove-identity-message",
            @"grv": @"application/vnd.groove-injector",
            @"gtm": @"application/vnd.groove-tool-message",
            @"tpl": @"application/vnd.groove-tool-template",
            @"vcg": @"application/vnd.groove-vcard",
            @"hal": @"application/vnd.hal+xml",
            @"zmm": @"application/vnd.handheld-entertainment+xml",
            @"hbci": @"application/vnd.hbci",
            @"les": @"application/vnd.hhe.lesson-player",
            @"hpgl": @"application/vnd.hp-hpgl",
            @"hpid": @"application/vnd.hp-hpid",
            @"hps": @"application/vnd.hp-hps",
            @"jlt": @"application/vnd.hp-jlyt",
            @"pcl": @"application/vnd.hp-pcl",
            @"pclxl": @"application/vnd.hp-pclxl",
            @"sfd-hdstx": @"application/vnd.hydrostatix.sof-data",
            @"x3d": @"application/vnd.hzn-3d-crossword",
            @"mpy": @"application/vnd.ibm.minipay",
            @"afp": @"application/vnd.ibm.modcap",
            @"irm": @"application/vnd.ibm.rights-management",
            @"sc": @"application/vnd.ibm.secure-container",
            @"icc": @"application/vnd.iccprofile",
            @"igl": @"application/vnd.igloader",
            @"ivp": @"application/vnd.immervision-ivp",
            @"ivu": @"application/vnd.immervision-ivu",
            @"igm": @"application/vnd.insors.igm",
            @"xpw": @"application/vnd.intercon.formnet",
            @"i2g": @"application/vnd.intergeo",
            @"qbo": @"application/vnd.intu.qbo",
            @"qfx": @"application/vnd.intu.qfx",
            @"rcprofile": @"application/vnd.ipunplugged.rcprofile",
            @"irp": @"application/vnd.irepository.package+xml",
            @"xpr": @"application/vnd.is-xpr",
            @"fcs": @"application/vnd.isac.fcs",
            @"jam": @"application/vnd.jam",
            @"rms": @"application/vnd.jcp.javame.midlet-rms",
            @"jisp": @"application/vnd.jisp",
            @"joda": @"application/vnd.joost.joda-archive",
            @"ktz": @"application/vnd.kahootz",
            @"karbon": @"application/vnd.kde.karbon",
            @"chrt": @"application/vnd.kde.kchart",
            @"kfo": @"application/vnd.kde.kformula",
            @"flw": @"application/vnd.kde.kivio",
            @"kon": @"application/vnd.kde.kontour",
            @"kpr": @"application/vnd.kde.kpresenter",
            @"ksp": @"application/vnd.kde.kspread",
            @"kwd": @"application/vnd.kde.kword",
            @"htke": @"application/vnd.kenameaapp",
            @"kia": @"application/vnd.kidspiration",
            @"kne": @"application/vnd.kinar",
            @"skp": @"application/vnd.koan",
            @"sse": @"application/vnd.kodak-descriptor",
            @"lasxml": @"application/vnd.las.las+xml",
            @"lbd": @"application/vnd.llamagraphics.life-balance.desktop",
            @"lbe": @"application/vnd.llamagraphics.life-balance.exchange+xml",
            @"123": @"application/vnd.lotus-1-2-3",
            @"apr": @"application/vnd.lotus-approach",
            @"pre": @"application/vnd.lotus-freelance",
            @"nsf": @"application/vnd.lotus-notes",
            @"org": @"application/vnd.lotus-organizer",
            @"scm": @"application/vnd.lotus-screencam",
            @"lwp": @"application/vnd.lotus-wordpro",
            @"portpkg": @"application/vnd.macports.portpkg",
            @"mcd": @"application/vnd.mcd",
            @"mc1": @"application/vnd.medcalcdata",
            @"cdkey": @"application/vnd.mediastation.cdkey",
            @"mwf": @"application/vnd.mfer",
            @"mfm": @"application/vnd.mfmp",
            @"flo": @"application/vnd.micrografx.flo",
            @"igx": @"application/vnd.micrografx.igx",
            @"mif": @"application/vnd.mif",
            @"daf": @"application/vnd.mobius.daf",
            @"dis": @"application/vnd.mobius.dis",
            @"mbk": @"application/vnd.mobius.mbk",
            @"mqy": @"application/vnd.mobius.mqy",
            @"msl": @"application/vnd.mobius.msl",
            @"plc": @"application/vnd.mobius.plc",
            @"txf": @"application/vnd.mobius.txf",
            @"mpn": @"application/vnd.mophun.application",
            @"mpc": @"application/vnd.mophun.certificate",
            @"xul": @"application/vnd.mozilla.xul+xml",
            @"cil": @"application/vnd.ms-artgalry",
            @"cab": @"application/vnd.ms-cab-compressed",
            @"xls": @"application/vnd.ms-excel",
            @"xlam": @"application/vnd.ms-excel.addin.macroenabled.12",
            @"xlsb": @"application/vnd.ms-excel.sheet.binary.macroenabled.12",
            @"xlsm": @"application/vnd.ms-excel.sheet.macroenabled.12",
            @"xltm": @"application/vnd.ms-excel.template.macroenabled.12",
            @"eot": @"application/vnd.ms-fontobject",
            @"chm": @"application/vnd.ms-htmlhelp",
            @"ims": @"application/vnd.ms-ims",
            @"lrm": @"application/vnd.ms-lrm",
            @"thmx": @"application/vnd.ms-officetheme",
            @"cat": @"application/vnd.ms-pki.seccat",
            @"stl": @"application/vnd.ms-pki.stl",
            @"ppt": @"application/vnd.ms-powerpoint",
            @"ppam": @"application/vnd.ms-powerpoint.addin.macroenabled.12",
            @"pptm": @"application/vnd.ms-powerpoint.presentation.macroenabled.12",
            @"sldm": @"application/vnd.ms-powerpoint.slide.macroenabled.12",
            @"ppsm": @"application/vnd.ms-powerpoint.slideshow.macroenabled.12",
            @"potm": @"application/vnd.ms-powerpoint.template.macroenabled.12",
            @"mpp": @"application/vnd.ms-project",
            @"docm": @"application/vnd.ms-word.document.macroenabled.12",
            @"dotm": @"application/vnd.ms-word.template.macroenabled.12",
            @"wps": @"application/vnd.ms-works",
            @"wpl": @"application/vnd.ms-wpl",
            @"xps": @"application/vnd.ms-xpsdocument",
            @"mseq": @"application/vnd.mseq",
            @"mus": @"application/vnd.musician",
            @"msty": @"application/vnd.muvee.style",
            @"nlu": @"application/vnd.neurolanguage.nlu",
            @"nnd": @"application/vnd.noblenet-directory",
            @"nns": @"application/vnd.noblenet-sealer",
            @"nnw": @"application/vnd.noblenet-web",
            @"ngdat": @"application/vnd.nokia.n-gage.data",
            @"n-gage": @"application/vnd.nokia.n-gage.symbian.install",
            @"rpst": @"application/vnd.nokia.radio-preset",
            @"rpss": @"application/vnd.nokia.radio-presets",
            @"edm": @"application/vnd.novadigm.edm",
            @"edx": @"application/vnd.novadigm.edx",
            @"ext": @"application/vnd.novadigm.ext",
            @"odc": @"application/vnd.oasis.opendocument.chart",
            @"otc": @"application/vnd.oasis.opendocument.chart-template",
            @"odb": @"application/vnd.oasis.opendocument.database",
            @"odf": @"application/vnd.oasis.opendocument.formula",
            @"odft": @"application/vnd.oasis.opendocument.formula-template",
            @"odg": @"application/vnd.oasis.opendocument.graphics",
            @"otg": @"application/vnd.oasis.opendocument.graphics-template",
            @"odi": @"application/vnd.oasis.opendocument.image",
            @"oti": @"application/vnd.oasis.opendocument.image-template",
            @"odp": @"application/vnd.oasis.opendocument.presentation",
            @"otp": @"application/vnd.oasis.opendocument.presentation-template",
            @"ods": @"application/vnd.oasis.opendocument.spreadsheet",
            @"ots": @"application/vnd.oasis.opendocument.spreadsheet-template",
            @"odt": @"application/vnd.oasis.opendocument.text",
            @"odm": @"application/vnd.oasis.opendocument.text-master",
            @"ott": @"application/vnd.oasis.opendocument.text-template",
            @"oth": @"application/vnd.oasis.opendocument.text-web",
            @"xo": @"application/vnd.olpc-sugar",
            @"dd2": @"application/vnd.oma.dd2+xml",
            @"oxt": @"application/vnd.openofficeorg.extension",
            @"pptx": @"application/vnd.openxmlformats-officedocument.presentationml.presentation",
            @"sldx": @"application/vnd.openxmlformats-officedocument.presentationml.slide",
            @"ppsx": @"application/vnd.openxmlformats-officedocument.presentationml.slideshow",
            @"potx": @"application/vnd.openxmlformats-officedocument.presentationml.template",
            @"xlsx": @"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            @"xltx": @"application/vnd.openxmlformats-officedocument.spreadsheetml.template",
            @"docx": @"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
            @"dotx": @"application/vnd.openxmlformats-officedocument.wordprocessingml.template",
            @"mgp": @"application/vnd.osgeo.mapguide.package",
            @"dp": @"application/vnd.osgi.dp",
            @"pdb": @"application/vnd.palm",
            @"paw": @"application/vnd.pawaafile",
            @"str": @"application/vnd.pg.format",
            @"ei6": @"application/vnd.pg.osasli",
            @"efif": @"application/vnd.picsel",
            @"wg": @"application/vnd.pmi.widget",
            @"plf": @"application/vnd.pocketlearn",
            @"pbd": @"application/vnd.powerbuilder6",
            @"box": @"application/vnd.previewsystems.box",
            @"mgz": @"application/vnd.proteus.magazine",
            @"qps": @"application/vnd.publishare-delta-tree",
            @"ptid": @"application/vnd.pvi.ptid1",
            @"qxd": @"application/vnd.quark.quarkxpress",
            @"bed": @"application/vnd.realvnc.bed",
            @"mxl": @"application/vnd.recordare.musicxml",
            @"musicxml": @"application/vnd.recordare.musicxml+xml",
            @"cryptonote": @"application/vnd.rig.cryptonote",
            @"cod": @"application/vnd.rim.cod",
            @"rm": @"application/vnd.rn-realmedia",
            @"link66": @"application/vnd.route66.link66+xml",
            @"st": @"application/vnd.sailingtracker.track",
            @"see": @"application/vnd.seemail",
            @"sema": @"application/vnd.sema",
            @"semd": @"application/vnd.semd",
            @"semf": @"application/vnd.semf",
            @"ifm": @"application/vnd.shana.informed.formdata",
            @"itp": @"application/vnd.shana.informed.formtemplate",
            @"iif": @"application/vnd.shana.informed.interchange",
            @"ipk": @"application/vnd.shana.informed.package",
            @"twd": @"application/vnd.simtech-mindmapper",
            @"mmf": @"application/vnd.smaf",
            @"teacher": @"application/vnd.smart.teacher",
            @"sdkm": @"application/vnd.solent.sdkm+xml",
            @"dxp": @"application/vnd.spotfire.dxp",
            @"sfs": @"application/vnd.spotfire.sfs",
            @"sdc": @"application/vnd.stardivision.calc",
            @"sda": @"application/vnd.stardivision.draw",
            @"sdd": @"application/vnd.stardivision.impress",
            @"smf": @"application/vnd.stardivision.math",
            @"sdw": @"application/vnd.stardivision.writer",
            @"sgl": @"application/vnd.stardivision.writer-global",
            @"sm": @"application/vnd.stepmania.stepchart",
            @"sxc": @"application/vnd.sun.xml.calc",
            @"stc": @"application/vnd.sun.xml.calc.template",
            @"sxd": @"application/vnd.sun.xml.draw",
            @"std": @"application/vnd.sun.xml.draw.template",
            @"sxi": @"application/vnd.sun.xml.impress",
            @"sti": @"application/vnd.sun.xml.impress.template",
            @"sxm": @"application/vnd.sun.xml.math",
            @"sxw": @"application/vnd.sun.xml.writer",
            @"sxg": @"application/vnd.sun.xml.writer.global",
            @"stw": @"application/vnd.sun.xml.writer.template",
            @"sus": @"application/vnd.sus-calendar",
            @"svd": @"application/vnd.svd",
            @"sis": @"application/vnd.symbian.install",
            @"xsm": @"application/vnd.syncml+xml",
            @"bdm": @"application/vnd.syncml.dm+wbxml",
            @"xdm": @"application/vnd.syncml.dm+xml",
            @"tao": @"application/vnd.tao.intent-module-archive",
            @"tmo": @"application/vnd.tmobile-livetv",
            @"tpt": @"application/vnd.trid.tpt",
            @"mxs": @"application/vnd.triscape.mxs",
            @"tra": @"application/vnd.trueapp",
            @"ufd": @"application/vnd.ufdl",
            @"utz": @"application/vnd.uiq.theme",
            @"umj": @"application/vnd.umajin",
            @"unityweb": @"application/vnd.unity",
            @"uoml": @"application/vnd.uoml+xml",
            @"vcx": @"application/vnd.vcx",
            @"vsd": @"application/vnd.visio",
            @"vis": @"application/vnd.visionary",
            @"vsf": @"application/vnd.vsf",
            @"wbxml": @"application/vnd.wap.wbxml",
            @"wmlc": @"application/vnd.wap.wmlc",
            @"wmlsc": @"application/vnd.wap.wmlscriptc",
            @"wtb": @"application/vnd.webturbo",
            @"nbp": @"application/vnd.wolfram.player",
            @"wpd": @"application/vnd.wordperfect",
            @"wqd": @"application/vnd.wqd",
            @"stf": @"application/vnd.wt.stf",
            @"xar": @"application/vnd.xara",
            @"xfdl": @"application/vnd.xfdl",
            @"hvd": @"application/vnd.yamaha.hv-dic",
            @"hvs": @"application/vnd.yamaha.hv-script",
            @"hvp": @"application/vnd.yamaha.hv-voice",
            @"osf": @"application/vnd.yamaha.openscoreformat",
            @"osfpvg": @"application/vnd.yamaha.openscoreformat.osfpvg+xml",
            @"saf": @"application/vnd.yamaha.smaf-audio",
            @"spf": @"application/vnd.yamaha.smaf-phrase",
            @"cmp": @"application/vnd.yellowriver-custom-menu",
            @"zir": @"application/vnd.zul",
            @"zaz": @"application/vnd.zzazz.deck+xml",
            @"vxml": @"application/voicexml+xml",
            @"wgt": @"application/widget",
            @"hlp": @"application/winhlp",
            @"wsdl": @"application/wsdl+xml",
            @"wspolicy": @"application/wspolicy+xml",
            @"7z": @"application/x-7z-compressed",
            @"abw": @"application/x-abiword",
            @"ace": @"application/x-ace-compressed",
            @"aab": @"application/x-authorware-bin",
            @"aam": @"application/x-authorware-map",
            @"aas": @"application/x-authorware-seg",
            @"bcpio": @"application/x-bcpio",
            @"torrent": @"application/x-bittorrent",
            @"bz": @"application/x-bzip",
            @"bz2": @"application/x-bzip2",
            @"vcd": @"application/x-cdlink",
            @"chat": @"application/x-chat",
            @"pgn": @"application/x-chess-pgn",
            @"cpio": @"application/x-cpio",
            @"csh": @"application/x-csh",
            @"deb": @"application/x-debian-package",
            @"dir": @"application/x-director",
            @"wad": @"application/x-doom",
            @"ncx": @"application/x-dtbncx+xml",
            @"dtb": @"application/x-dtbook+xml",
            @"res": @"application/x-dtbresource+xml",
            @"dvi": @"application/x-dvi",
            @"bdf": @"application/x-font-bdf",
            @"gsf": @"application/x-font-ghostscript",
            @"psf": @"application/x-font-linux-psf",
            @"otf": @"application/x-font-otf",
            @"pcf": @"application/x-font-pcf",
            @"snf": @"application/x-font-snf",
            @"ttf": @"application/x-font-ttf",
            @"pfa": @"application/x-font-type1",
            @"woff": @"application/x-font-woff",
            @"spl": @"application/x-futuresplash",
            @"gnumeric": @"application/x-gnumeric",
            @"gtar": @"application/x-gtar",
            @"hdf": @"application/x-hdf",
            @"jnlp": @"application/x-java-jnlp-file",
            @"latex": @"application/x-latex",
            @"prc": @"application/x-mobipocket-ebook",
            @"application": @"application/x-ms-application",
            @"wmd": @"application/x-ms-wmd",
            @"wmz": @"application/x-ms-wmz",
            @"xbap": @"application/x-ms-xbap",
            @"mdb": @"application/x-msaccess",
            @"obd": @"application/x-msbinder",
            @"crd": @"application/x-mscardfile",
            @"clp": @"application/x-msclip",
            @"exe": @"application/x-msdownload",
            @"mvb": @"application/x-msmediaview",
            @"wmf": @"application/x-msmetafile",
            @"mny": @"application/x-msmoney",
            @"pub": @"application/x-mspublisher",
            @"scd": @"application/x-msschedule",
            @"trm": @"application/x-msterminal",
            @"wri": @"application/x-mswrite",
            @"nc": @"application/x-netcdf",
            @"p12": @"application/x-pkcs12",
            @"p7b": @"application/x-pkcs7-certificates",
            @"p7r": @"application/x-pkcs7-certreqresp",
            @"rar": @"application/x-rar-compressed",
            @"sh": @"application/x-sh",
            @"shar": @"application/x-shar",
            @"swf": @"application/x-shockwave-flash",
            @"xap": @"application/x-silverlight-app",
            @"sit": @"application/x-stuffit",
            @"sitx": @"application/x-stuffitx",
            @"sv4cpio": @"application/x-sv4cpio",
            @"sv4crc": @"application/x-sv4crc",
            @"tar": @"application/x-tar",
            @"tcl": @"application/x-tcl",
            @"tex": @"application/x-tex",
            @"tfm": @"application/x-tex-tfm",
            @"texinfo": @"application/x-texinfo",
            @"ustar": @"application/x-ustar",
            @"src": @"application/x-wais-source",
            @"der": @"application/x-x509-ca-cert",
            @"fig": @"application/x-xfig",
            @"xpi": @"application/x-xpinstall",
            @"xdf": @"application/xcap-diff+xml",
            @"xenc": @"application/xenc+xml",
            @"xhtml": @"application/xhtml+xml",
            @"xml": @"application/xml",
            @"dtd": @"application/xml-dtd",
            @"xop": @"application/xop+xml",
            @"xslt": @"application/xslt+xml",
            @"xspf": @"application/xspf+xml",
            @"mxml": @"application/xv+xml",
            @"yang": @"application/yang",
            @"yin": @"application/yin+xml",
            @"zip": @"application/zip",
            @"adp": @"audio/adpcm",
            @"au": @"audio/basic",
            @"mid": @"audio/midi",
            @"mp4a": @"audio/mp4",
            @"mpga": @"audio/mpeg",
            @"oga": @"audio/ogg",
            @"uva": @"audio/vnd.dece.audio",
            @"eol": @"audio/vnd.digital-winds",
            @"dra": @"audio/vnd.dra",
            @"dts": @"audio/vnd.dts",
            @"dtshd": @"audio/vnd.dts.hd",
            @"lvp": @"audio/vnd.lucent.voice",
            @"pya": @"audio/vnd.ms-playready.media.pya",
            @"ecelp4800": @"audio/vnd.nuera.ecelp4800",
            @"ecelp7470": @"audio/vnd.nuera.ecelp7470",
            @"ecelp9600": @"audio/vnd.nuera.ecelp9600",
            @"rip": @"audio/vnd.rip",
            @"weba": @"audio/webm",
            @"aac": @"audio/x-aac",
            @"aif": @"audio/x-aiff",
            @"m3u": @"audio/x-mpegurl",
            @"wax": @"audio/x-ms-wax",
            @"wma": @"audio/x-ms-wma",
            @"ram": @"audio/x-pn-realaudio",
            @"rmp": @"audio/x-pn-realaudio-plugin",
            @"wav": @"audio/x-wav",
            @"cdx": @"chemical/x-cdx",
            @"cif": @"chemical/x-cif",
            @"cmdf": @"chemical/x-cmdf",
            @"cml": @"chemical/x-cml",
            @"csml": @"chemical/x-csml",
            @"xyz": @"chemical/x-xyz",
            @"bmp": @"image/bmp",
            @"cgm": @"image/cgm",
            @"g3": @"image/g3fax",
            @"gif": @"image/gif",
            @"ief": @"image/ief",
            @"jpeg": @"image/jpeg",
            @"jpg" : @"image/jpeg",
            @"ktx": @"image/ktx",
            @"png": @"image/png",
            @"btif": @"image/prs.btif",
            @"svg": @"image/svg+xml",
            @"tiff": @"image/tiff",
            @"psd": @"image/vnd.adobe.photoshop",
            @"uvi": @"image/vnd.dece.graphic",
            @"sub": @"image/vnd.dvb.subtitle",
            @"djvu": @"image/vnd.djvu",
            @"dwg": @"image/vnd.dwg",
            @"dxf": @"image/vnd.dxf",
            @"fbs": @"image/vnd.fastbidsheet",
            @"fpx": @"image/vnd.fpx",
            @"fst": @"image/vnd.fst",
            @"mmr": @"image/vnd.fujixerox.edmics-mmr",
            @"rlc": @"image/vnd.fujixerox.edmics-rlc",
            @"mdi": @"image/vnd.ms-modi",
            @"npx": @"image/vnd.net-fpx",
            @"wbmp": @"image/vnd.wap.wbmp",
            @"xif": @"image/vnd.xiff",
            @"webp": @"image/webp",
            @"ras": @"image/x-cmu-raster",
            @"cmx": @"image/x-cmx",
            @"fh": @"image/x-freehand",
            @"ico": @"image/x-icon",
            @"pcx": @"image/x-pcx",
            @"pic": @"image/x-pict",
            @"pnm": @"image/x-portable-anymap",
            @"pbm": @"image/x-portable-bitmap",
            @"pgm": @"image/x-portable-graymap",
            @"ppm": @"image/x-portable-pixmap",
            @"rgb": @"image/x-rgb",
            @"xbm": @"image/x-xbitmap",
            @"xpm": @"image/x-xpixmap",
            @"xwd": @"image/x-xwindowdump",
            @"eml": @"message/rfc822",
            @"igs": @"model/iges",
            @"msh": @"model/mesh",
            @"dae": @"model/vnd.collada+xml",
            @"dwf": @"model/vnd.dwf",
            @"gdl": @"model/vnd.gdl",
            @"gtw": @"model/vnd.gtw",
            @"mts": @"model/vnd.mts",
            @"vtu": @"model/vnd.vtu",
            @"wrl": @"model/vrml",
            @"ics": @"text/calendar",
            @"css": @"text/css",
            @"csv": @"text/csv",
            @"html": @"text/html",
            @"n3": @"text/n3",
            @"txt": @"text/plain",
            @"dsc": @"text/prs.lines.tag",
            @"rtx": @"text/richtext",
            @"sgml": @"text/sgml",
            @"tsv": @"text/tab-separated-values",
            @"t": @"text/troff",
            @"ttl": @"text/turtle",
            @"uri": @"text/uri-list",
            @"curl": @"text/vnd.curl",
            @"dcurl": @"text/vnd.curl.dcurl",
            @"scurl": @"text/vnd.curl.scurl",
            @"mcurl": @"text/vnd.curl.mcurl",
            @"fly": @"text/vnd.fly",
            @"flx": @"text/vnd.fmi.flexstor",
            @"gv": @"text/vnd.graphviz",
            @"3dml": @"text/vnd.in3d.3dml",
            @"spot": @"text/vnd.in3d.spot",
            @"jad": @"text/vnd.sun.j2me.app-descriptor",
            @"wml": @"text/vnd.wap.wml",
            @"wmls": @"text/vnd.wap.wmlscript",
            @"s": @"text/x-asm",
            @"c": @"text/x-c",
            @"f": @"text/x-fortran",
            @"p": @"text/x-pascal",
            @"java": @"text/x-java-source",
            @"etx": @"text/x-setext",
            @"uu": @"text/x-uuencode",
            @"vcs": @"text/x-vcalendar",
            @"vcf": @"text/x-vcard",
            @"3gp": @"video/3gpp",
            @"3g2": @"video/3gpp2",
            @"h261": @"video/h261",
            @"h263": @"video/h263",
            @"h264": @"video/h264",
            @"jpgv": @"video/jpeg",
            @"jpm": @"video/jpm",
            @"mj2": @"video/mj2",
            @"mp4": @"video/mp4",
            @"mpeg": @"video/mpeg",
            @"ogv": @"video/ogg",
            @"qt": @"video/quicktime",
            @"uvh": @"video/vnd.dece.hd",
            @"uvm": @"video/vnd.dece.mobile",
            @"uvp": @"video/vnd.dece.pd",
            @"uvs": @"video/vnd.dece.sd",
            @"uvv": @"video/vnd.dece.video",
            @"fvt": @"video/vnd.fvt",
            @"mxu": @"video/vnd.mpegurl",
            @"pyv": @"video/vnd.ms-playready.media.pyv",
            @"uvu": @"video/vnd.uvvu.mp4",
            @"viv": @"video/vnd.vivo",
            @"webm": @"video/webm",
            @"f4v": @"video/x-f4v",
            @"fli": @"video/x-fli",
            @"flv": @"video/x-flv",
            @"m4v": @"video/x-m4v",
            @"asf": @"video/x-ms-asf",
            @"wm": @"video/x-ms-wm",
            @"wmv": @"video/x-ms-wmv",
            @"wmx": @"video/x-ms-wmx",
            @"wvx": @"video/x-ms-wvx",
            @"avi": @"video/x-msvideo",
            @"movie": @"video/x-sgi-movie",
            @"mov": @"video/quicktime",
            @"ice": @"x-conference/x-cooltalk",
            @"par ": @"text/plain-bas",
            @"yaml": @"text/yaml"
            };
    });

    NSString * extention = nil;

    if (filePath) {
        extention = [filePath pathExtension];
    }

    if (![extention oss_isNotEmpty] && uploadName) {
        extention = [uploadName pathExtension];
    }

    if (![extention oss_isNotEmpty]) {
        return @"application/octet-stream";
    }

    NSString * mimeType = [mimeMap objectForKey:extention.lowercaseString];
    return mimeType ? mimeType : @"application/octet-stream";
}

+ (BOOL)hasPhoneFreeSpace{
    NSError *error;
    NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:&error];
    if(error) return NO;
    long long space = [[attrs objectForKey:NSFileSystemFreeSize] longLongValue];
    if(space < 0) return NO;
    if(space < osskDDDefaultLogMaxFileSize) return NO;
    return YES;
}

+ (uint64_t)crc64ecma:(uint64_t)crc1 buffer:(void *)buffer length:(size_t)len
{
    return aos_crc64(crc1, buffer, len);
}

+ (uint64_t)crc64ForCombineCRC1:(uint64_t)crc1 CRC2:(uint64_t)crc2 length:(size_t)len2
{
    return aos_crc64_combine(crc1, crc2, len2);
}

+ (NSString *)sha1WithString:(NSString *)string
{
    NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
    return [self sha1WithData:data];
}

+ (NSString *)sha1WithData:(NSData *)data
{
    unsigned char *digest = NULL;
    
    // Malloc a buffer to hold hash.
    digest = malloc(CC_SHA1_DIGEST_LENGTH * sizeof(unsigned char));
    memset(digest, 0x0, CC_SHA1_DIGEST_LENGTH);
    CC_SHA1(data.bytes, (CC_LONG)data.length, digest);
    
    NSString *result = [self sha1WithDigest:digest];
    if (digest) {
        free(digest);
    }
    
    return result;
}

+ (NSString *)sha1WithDigest:(const unsigned char *)digest
{
    if (!digest) {
        return nil;
    }
    NSMutableString *result = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * sizeof(unsigned char)];
    for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) {
        [result appendFormat:@"%02x",digest[i]];
    }
    
    return result;
}

+ (NSString *)sha1WithFilePath:(NSString *)filePath
{
    NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:filePath];
    if(!handle) {
        return nil;
    }
    CC_SHA1_CTX sha1;
    CC_SHA1_Init(&sha1);
    BOOL done = NO;
    unsigned char *digest = NULL;
    
    while(!done) {
        @autoreleasepool{
            NSData* fileData = [handle readDataOfLength: CHUNK_SIZE];
            if(fileData.length == 0) {
                break;
            }

            CC_SHA1_Update(&sha1, fileData.bytes, (CC_LONG)[fileData length]);
        }
    }
    if (@available(iOS 13.0, *)) {
        [handle closeAndReturnError:nil];
    } else {
        [handle closeFile];
    }
    
    // Malloc a buffer to hold hash.
    digest = malloc(CC_SHA1_DIGEST_LENGTH * sizeof(unsigned char));
    memset(digest, 0x0, CC_SHA1_DIGEST_LENGTH);
    CC_SHA1_Final(digest, &sha1);
    
    NSString *result = [self sha1WithDigest:digest];
    if (digest) {
        free(digest);
    }
    
    return result;
}

+ (NSData *)constructHttpBodyForTriggerCallback:(NSString *)callbackParams callbackVaribles:(NSString *)callbackVaribles
{
    NSMutableString *bodyString = [NSMutableString string];
    
    [bodyString appendString:@"x-oss-process=trigger/callback,callback_"];
    if ([callbackParams oss_isNotEmpty])
    {
        [bodyString appendString:callbackParams];
    }
    
    [bodyString appendString:@",callback-var_"];
    if ([callbackVaribles oss_isNotEmpty])
    {
        [bodyString appendString:callbackVaribles];
    }
    
    return [bodyString dataUsingEncoding:NSUTF8StringEncoding];
}

+ (NSData *)constructHttpBodyForImagePersist:(NSString *)action toBucket:(NSString *)toBucket toObjectKey:(NSString *)toObjectKey
{
    /*
     * parameter has checked before
     */
    NSMutableString *bodyString = [NSMutableString string];
    [bodyString appendString:@"x-oss-process="];
    if ([action rangeOfString:@"image/"].location == NSNotFound)
    {
        [bodyString appendString:@"image/"];
        
    }
    [bodyString appendString:action];
    [bodyString appendString:@"|sys/"];
    
    
    NSString * bucket_base64 = [[toBucket dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0];
    
    NSString * objectkey_base64 = [[toObjectKey dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0];
    
    [bodyString appendString:@"saveas,o_"];
    [bodyString appendString:objectkey_base64];
    [bodyString appendString:@",b_"];
    [bodyString appendString:bucket_base64];

    return [bodyString dataUsingEncoding:NSUTF8StringEncoding];
}


@end

@implementation NSString (OSS)

- (NSString *)oss_trim {
    return [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}

- (BOOL)oss_isNotEmpty
{
    return ![[self oss_trim] isEqualToString:@""];
}

- (NSString *)oss_stringByAppendingPathComponentForURL:(NSString *)aString
{
    if ([self hasSuffix:@"/"]) {
        return [NSString stringWithFormat:@"%@%@", self, aString];
    } else {
        return [NSString stringWithFormat:@"%@/%@", self, aString];
    }
}

+ (NSString *)oss_documentDirectory
{
    static NSString *documentDirectory = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        documentDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    });
    return documentDirectory;
}

- (NSString *)oss_urlEncodedString {
    static NSCharacterSet *allowCharacterSet = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        allowCharacterSet = [[NSCharacterSet characterSetWithCharactersInString:@"!*'();:@&=+$,/?#[]"] invertedSet];
    });
    
    return [self stringByAddingPercentEncodingWithAllowedCharacters:allowCharacterSet];
}

@end
