diff --git a/V2RayX/AppDelegate.m b/V2RayX/AppDelegate.m index 1a33cd7..ad7d03c 100644 --- a/V2RayX/AppDelegate.m +++ b/V2RayX/AppDelegate.m @@ -566,7 +566,7 @@ - (IBAction)updateSubscriptions:(id)sender { _subsOutbounds = [[NSMutableArray alloc] init]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ for (NSString* link in self.subscriptions) { - NSDictionary* r = [ConfigImporter importFromSubscriptionOfV2RayN:link]; + NSDictionary* r = [ConfigImporter importFromHTTPSubscription:link]; if (r) { for (ServerProfile* p in r[@"vmess"]) { [self.subsOutbounds addObject:[p outboundProfile]]; diff --git a/V2RayX/ConfigImporter.h b/V2RayX/ConfigImporter.h index df9cde7..fa6910e 100644 --- a/V2RayX/ConfigImporter.h +++ b/V2RayX/ConfigImporter.h @@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN + (NSMutableDictionary*)ssOutboundFromSSLink:(NSString*)link; + (NSMutableDictionary*)ssOutboundFromSSConfig:(NSDictionary*)jsonObject; + (ServerProfile*)importFromVmessOfV2RayN:(NSString*)vmessStr; -+ (NSMutableDictionary*)importFromSubscriptionOfV2RayN:(NSString*)httpLink; ++ (NSMutableDictionary*)importFromHTTPSubscription:(NSString*)httpLink; + (NSMutableDictionary*)importFromStandardConfigFiles:(NSArray*)files; + (NSMutableDictionary*)validateRuleSet:(NSMutableDictionary*)set; + (NSMutableDictionary* _Nonnull)importFromSubscriptionOfSSD: (NSString* _Nonnull)ssdLink; diff --git a/V2RayX/ConfigImporter.m b/V2RayX/ConfigImporter.m index 91b2de3..1cf7115 100644 --- a/V2RayX/ConfigImporter.m +++ b/V2RayX/ConfigImporter.m @@ -9,39 +9,62 @@ @implementation ConfigImporter -+ (NSString*)decodeBase64String:(NSString*)encoded { ++ (NSString* _Nonnull)decodeBase64String:(NSString*)encoded { + if (!encoded || ![encoded isKindOfClass:[NSString class]] || encoded.length == 0) { + return @""; + } NSMutableString* fixed = [encoded mutableCopy]; NSInteger numAdd = encoded.length % 4; for (int i = 0; i < numAdd; i += 1) { [fixed appendString:@"="]; } - NSData* decodedData = [[NSData alloc] initWithBase64EncodedString:fixed options:NSDataBase64DecodingIgnoreUnknownCharacters]; - return [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding]; + @try { + NSData* decodedData = [[NSData alloc] initWithBase64EncodedString:fixed options:NSDataBase64DecodingIgnoreUnknownCharacters]; + NSString* decodedString = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding]; + assert(decodedString != nil); + return decodedString; + } @catch (NSException *exception) { + return @""; + } } + (NSDictionary*)parseLegacySSLink:(NSString*)link { //http://shadowsocks.org/en/config/quick-guide.html @try { NSString* encoded = [[link stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] substringFromIndex:5]; - NSString* encodedRemoveTag = [encoded componentsSeparatedByString:@"#"][0]; + NSArray* hashTagSeperatedParts = [encoded componentsSeparatedByString:@"#"]; + NSString* encodedRemoveTag = hashTagSeperatedParts[0]; NSData* decodedData = [[NSData alloc] initWithBase64EncodedString:encodedRemoveTag options:0]; NSString* decoded = [[NSString alloc] initWithData: decodedData encoding:NSUTF8StringEncoding]; NSArray* parts = [decoded componentsSeparatedByString:@"@"]; - NSArray* server_port = [parts[1] componentsSeparatedByString:@":"]; - NSMutableArray* method_password = [[parts[0] componentsSeparatedByString:@":"] mutableCopy]; - NSString* method = method_password[0]; - [method_password removeObjectAtIndex:0]; - return @{ - @"server":server_port[0], - @"server_port":server_port[1], - @"password": [method_password componentsJoinedByString:@":"], - @"method":method}; + NSArray* addressAndPort = [parts[1] componentsSeparatedByString:@":"]; + NSMutableArray* methodAndPassword = [[parts[0] componentsSeparatedByString:@":"] mutableCopy]; + NSString* method = methodAndPassword[0]; + [methodAndPassword removeObjectAtIndex:0]; + + NSNumberFormatter* f = [[NSNumberFormatter alloc] init]; + f.numberStyle = NSNumberFormatterDecimalStyle; + NSNumber *port = [f numberFromString:addressAndPort[1]]; + + if (hashTagSeperatedParts.count == 1) { + return @{ + @"server":addressAndPort[0], + @"server_port":port, + @"password": [methodAndPassword componentsJoinedByString:@":"], + @"method":method}; + } else { + return @{ + @"server":addressAndPort[0], + @"server_port":port, + @"password": [methodAndPassword componentsJoinedByString:@":"], + @"method":method, + @"tag":hashTagSeperatedParts[1] + }; + } } @catch (NSException *exception) { return nil; - } @finally { - ; } } @@ -70,7 +93,6 @@ + (NSDictionary*)parseStandardSSLink:(NSString*)link { if (!port) { return nil; } - return @{ @"server":hostInfo[0], @"server_port":port, @@ -80,12 +102,10 @@ + (NSDictionary*)parseStandardSSLink:(NSString*)link { }; } @catch (NSException *exception) { return nil; - } @finally { - ; } } -+ (NSMutableDictionary*)ssOutboundFromSSLink:(NSString*)link { ++ (NSMutableDictionary* )ssOutboundFromSSLink:(NSString*)link { NSDictionary* parsed = [ConfigImporter parseStandardSSLink:link]; if (parsed) { return [ConfigImporter ssOutboundFromSSConfig:parsed]; @@ -257,7 +277,7 @@ + (NSMutableDictionary*)importFromStandardConfigFiles:(NSArray*)files { return result; } -+ (NSMutableDictionary*)importFromSubscriptionOfV2RayN: (NSString*)httpLink { ++ (NSMutableDictionary*)importFromHTTPSubscription: (NSString*)httpLink { // https://blog.csdn.net/yi_zz32/article/details/48769487 NSMutableDictionary* result = [@{@"vmess": @[], @"other": @[]} mutableDeepCopy]; if (![@"http" isEqualToString:[httpLink substringToIndex:4]]) { @@ -286,6 +306,8 @@ + (NSMutableDictionary*)importFromSubscriptionOfV2RayN: (NSString*)httpLink { [result[@"other"] addObject:outbound]; continue; } + NSMutableDictionary* ssdResults = [ConfigImporter importFromSubscriptionOfSSD:linkStr]; + [result[@"other"] addObjectsFromArray:ssdResults[@"other"]]; } } return result; diff --git a/V2RayX/ConfigWindowController.m b/V2RayX/ConfigWindowController.m index b9fa030..0057771 100644 --- a/V2RayX/ConfigWindowController.m +++ b/V2RayX/ConfigWindowController.m @@ -336,23 +336,27 @@ - (void)presentImportResultOfVmessCount:(NSInteger)vmessCount otherCount:(NSInte } - (IBAction)importFromMiscLinks:(id)sender { - [self askInputWithPrompt:@"V2RayX will try importing vmess:// and http(s):// links from v2rayN ." handler:^(NSString *inputStr) { + [self askInputWithPrompt:@"V2RayX will try importing ssd://, vmess:// and http(s):// links from v2rayN and SSD." handler:^(NSString *inputStr) { if ([inputStr length] != 0) { - ServerProfile* p = [ConfigImporter importFromVmessOfV2RayN:inputStr]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + ServerProfile* p = [ConfigImporter importFromVmessOfV2RayN:inputStr]; NSInteger vmessCount = 0; NSInteger otherCount = 0; if (p) { [self.profiles addObject:p]; vmessCount = 1; - } else { - NSMutableDictionary* p2 = [ConfigImporter importFromSubscriptionOfV2RayN:inputStr]; - if (p2) { - [self.profiles addObjectsFromArray:p2[@"vmess"]]; - [self.outbounds addObjectsFromArray:p2[@"other"]]; - vmessCount = [p2[@"vmess"] count]; - otherCount = [p2[@"other"] count]; - } + } + NSDictionary* ssdResult = [ConfigImporter importFromSubscriptionOfSSD:inputStr]; + for (NSDictionary* d in ssdResult[@"other"]) { + [self.outbounds addObject:[d mutableDeepCopy]]; + otherCount += 1; + } + NSMutableDictionary* p2 = [ConfigImporter importFromHTTPSubscription:inputStr]; + if (p2) { + [self.profiles addObjectsFromArray:p2[@"vmess"]]; + [self.outbounds addObjectsFromArray:p2[@"other"]]; + vmessCount += [p2[@"vmess"] count]; + otherCount += [p2[@"other"] count]; } [self presentImportResultOfVmessCount:vmessCount otherCount:otherCount ruleSetCount:0]; }); diff --git a/V2RayX/Info.plist b/V2RayX/Info.plist index d1f562f..dd3929d 100644 --- a/V2RayX/Info.plist +++ b/V2RayX/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 1142 + 1235 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) LSUIElement