// // QRCodeUtils.m // ShadowsocksX-NG // // Created by 邱宇舟 on 16/6/8. // Copyright © 2016年 qiuyuzhou. All rights reserved. // #import #import #import "Utils.h" void ScanQRCodeOnScreen() { /* displays[] Quartz display ID's */ CGDirectDisplayID *displays = nil; CGError err = CGDisplayNoErr; CGDisplayCount dspCount = 0; /* How many active displays do we have? */ err = CGGetActiveDisplayList(0, NULL, &dspCount); /* If we are getting an error here then their won't be much to display. */ if(err != CGDisplayNoErr) { NSLog(@"Could not get active display count (%d)\n", err); return; } /* Allocate enough memory to hold all the display IDs we have. */ displays = calloc((size_t)dspCount, sizeof(CGDirectDisplayID)); // Get the list of active displays err = CGGetActiveDisplayList(dspCount, displays, &dspCount); /* More error-checking here. */ if(err != CGDisplayNoErr) { NSLog(@"Could not get active display list (%d)\n", err); return; } NSMutableArray* foundSSUrls = [NSMutableArray array]; CIDetector *detector = [CIDetector detectorOfType:@"CIDetectorTypeQRCode" context:[CIContext contextWithOptions:@{kCIContextUseSoftwareRenderer: @true}] options:@{ CIDetectorAccuracy:CIDetectorAccuracyHigh }]; for (unsigned int displaysIndex = 0; displaysIndex < dspCount; displaysIndex++) { /* Make a snapshot image of the current display. */ CGImageRef image = CGDisplayCreateImage(displays[displaysIndex]); NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:image]]; for (CIQRCodeFeature *feature in features) { // NSLog(@"%@", feature.messageString); if ( [feature.messageString hasPrefix:@"ss://"] ) { [foundSSUrls addObject:[NSURL URLWithString:feature.messageString]]; }else if ( [feature.messageString hasPrefix:@"ssr://"] ){ [foundSSUrls addObject:[NSURL URLWithString:feature.messageString]]; } } CGImageRelease(image); } free(displays); // TODO if not find any urls push a notification tells there is no QR on the screen [[NSNotificationCenter defaultCenter] postNotificationName:@"NOTIFY_FOUND_SS_URL" object:nil userInfo: @{ @"urls": foundSSUrls, @"source": @"qrcode" } ]; } NSString* decode64(NSString* str){ str = [str stringByReplacingOccurrencesOfString:@"-" withString:@"+"]; str = [str stringByReplacingOccurrencesOfString:@"_" withString:@"/"]; if(str.length%4){ NSInteger length = (4-str.length%4) + str.length; str = [str stringByPaddingToLength: length withString:@"=" startingAtIndex:0]; } NSData* decodeData = [[NSData alloc] initWithBase64EncodedString:str options:0]; NSString* decodeStr = [[NSString alloc] initWithData:decodeData encoding:NSUTF8StringEncoding]; return decodeStr; } NSString* encode64(NSString* str){ NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding]; NSString *stringBase64 = [data base64EncodedStringWithOptions: NSDataBase64EncodingEndLineWithCarriageReturn]; stringBase64 = [stringBase64 stringByReplacingOccurrencesOfString:@"+" withString:@"-"]; stringBase64 = [stringBase64 stringByReplacingOccurrencesOfString:@"/" withString:@"_"]; stringBase64 = [stringBase64 stringByReplacingOccurrencesOfString:@"=" withString:@""]; return stringBase64; } NSDictionary* ParseAppURLSchemes(NSURL* url){ if(!url.host){ return nil; } NSString *urlString = [url absoluteString]; if ([urlString hasPrefix:@"ss://"]) { return ParseSSURL(url); } if ([urlString hasPrefix:@"ssr://"]) { return ParseSSRURL(url); } return nil; } // 解析SS URL,如果成功则返回一个与ServerProfile类兼容的dict // 或SSR URL,ServerProfile类已经默认添加SSR参数,默认放空,如果URL为SSR://则改变解析方法 // ss:// + base64(method:password@domain:port) static NSDictionary* ParseSSURL(NSURL* url) { if (!url.host) { return nil; } NSString *urlString = [url absoluteString]; int i = 0; NSString *errorReason = nil; if([urlString hasPrefix:@"ss://"]){ while(i < 2) { if (i == 1) { NSString* host = url.host; if ([host length]%4!=0) { int n = 4 - [host length]%4; if (1==n) { host = [host stringByAppendingString:@"="]; } else if (2==n) { host = [host stringByAppendingString:@"=="]; } } NSData *data = [[NSData alloc] initWithBase64EncodedString:host options:0]; NSString *decodedString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; urlString = decodedString; } i++; urlString = [urlString stringByReplacingOccurrencesOfString:@"ss://" withString:@"" options:NSAnchoredSearch range:NSMakeRange(0, urlString.length)]; NSRange firstColonRange = [urlString rangeOfString:@":"]; NSRange lastColonRange = [urlString rangeOfString:@":" options:NSBackwardsSearch]; NSRange lastAtRange = [urlString rangeOfString:@"@" options:NSBackwardsSearch]; if (firstColonRange.length == 0) { errorReason = @"colon not found"; continue; } if (firstColonRange.location == lastColonRange.location) { errorReason = @"only one colon"; continue; } if (lastAtRange.length == 0) { errorReason = @"at not found"; continue; } if (!((firstColonRange.location < lastAtRange.location) && (lastAtRange.location < lastColonRange.location))) { errorReason = @"wrong position"; continue; } NSString *method = [urlString substringWithRange:NSMakeRange(0, firstColonRange.location)]; NSString *password = [urlString substringWithRange:NSMakeRange(firstColonRange.location + 1, lastAtRange.location - firstColonRange.location - 1)]; NSString *IP = [urlString substringWithRange:NSMakeRange(lastAtRange.location + 1, lastColonRange.location - lastAtRange.location - 1)]; NSString *port = [urlString substringWithRange:NSMakeRange(lastColonRange.location + 1, urlString.length - lastColonRange.location - 1)]; return @{@"ServerHost": IP, @"ServerPort": @([port integerValue]), @"Method": method, @"Password": password, }; } } return nil; } static NSDictionary* ParseSSRURL(NSURL* url) { NSString *urlString = [url absoluteString]; NSString *firstParam; NSString *lastParam; //if ([urlString hasPrefix:@"ssr://"]){ // ssr:// + base64(abc.xyz:12345:auth_sha1_v2:rc4-md5:tls1.2_ticket_auth:{base64(password)}/?obfsparam={base64(混淆参数(网址))}&protoparam={base64(混淆协议)}&remarks={base64(节点名称)}&group={base64(分组名)}) urlString = [urlString stringByReplacingOccurrencesOfString:@"ssr://" withString:@"" options:NSAnchoredSearch range:NSMakeRange(0, urlString.length)]; NSString *decodedString = decode64(urlString); NSLog(@"%@", decodedString); if ([decodedString isEqual: @""]) { [[NSNotificationCenter defaultCenter] postNotificationName:@"NOTIFY_INVALIDE_QR" object:nil userInfo: @{ @"urls": @"配置二维码无效!", @"source": @"qrcode" } ]; }else{ NSRange paramSplit = [decodedString rangeOfString:@"?"]; NSLog(@"%lu", (unsigned long)paramSplit.location); if (paramSplit.length == 0){ firstParam = decodedString; } else { firstParam = [decodedString substringToIndex:paramSplit.location-1]; lastParam = [decodedString substringFromIndex:paramSplit.location]; } NSDictionary *parserLastParamDict = parseSSRLastParam(lastParam); //后面已经parser完成,接下来需要解析到profile里面 //abc.xyz:12345:auth_sha1_v2:rc4-md5:tls1.2_ticket_auth:{base64(password)} NSRange range = [firstParam rangeOfString:@":"]; NSString *ip = [firstParam substringToIndex:range.location];//第一个参数是域名 firstParam = [firstParam substringFromIndex:range.location + range.length]; range = [firstParam rangeOfString:@":"]; NSString *port = [firstParam substringToIndex:range.location];//第二个参数是端口 firstParam = [firstParam substringFromIndex:range.location + range.length]; range = [firstParam rangeOfString:@":"]; NSString *ssrProtocol = [firstParam substringToIndex:range.location];//第三个参数是协议 firstParam = [firstParam substringFromIndex:range.location + range.length]; range = [firstParam rangeOfString:@":"]; NSString *encryption = [firstParam substringToIndex:range.location];//第四个参数是加密 firstParam = [firstParam substringFromIndex:range.location + range.length]; range = [firstParam rangeOfString:@":"]; NSString *ssrObfs = [firstParam substringToIndex:range.location];//第五个参数是混淆协议 firstParam = [firstParam substringFromIndex:range.location + range.length]; // range = [firstParam rangeOfString:@":"]; NSString *password = decode64(firstParam);// [[NSString alloc] initWithData:[[NSData alloc] initWithBase64EncodedString:firstParam options:0]encoding:NSUTF8StringEncoding];//第五个参数是base64密码 NSString *ssrObfsParam = @""; NSString *remarks = @""; NSString *ssrProtocolParam = @""; NSString *ssrGroup = @""; for (NSString *key in parserLastParamDict) { // NSLog(@"key: %@ value: %@", key, parserLastParamDict[key]); if ([key isEqual: @"obfsparam"]) { ssrObfsParam = parserLastParamDict[key]; } else if ([key isEqual: @"remarks"]) { remarks = parserLastParamDict[key]; } else if([key isEqual:@"protoparam"]){ ssrProtocolParam = parserLastParamDict[key]; } else if([key isEqual:@"group"]){ ssrGroup = parserLastParamDict[key]; } } return @{@"ServerHost":ip, @"ServerPort": @([port integerValue]), @"Method": encryption, @"Password": password, @"ssrObfs":ssrObfs, @"ssrObfsParam":ssrObfsParam, @"ssrProtocol":ssrProtocol, @"ssrProtocolParam":ssrProtocolParam, @"Remark":remarks, @"ssrGroup":ssrGroup, }; } //} return nil; } static NSDictionary* parseSSRLastParam(NSString* lastParam){ NSMutableDictionary *parserLastParamDict = [[NSMutableDictionary alloc]init]; if(lastParam.length == 0){ return nil; } lastParam = [lastParam substringFromIndex:1]; NSArray *lastParamArray = [lastParam componentsSeparatedByString:@"&"]; for (int i=0; i