Browse Source

plex: fix and rewrite the authentication methods

Pierre SAGASPE 6 years ago
parent
commit
6ed9aa813e

+ 2 - 1
SharedSources/ServerBrowsing/Plex/VLCPlexParser.h

@@ -2,7 +2,7 @@
  * VLCPlexParser.h
  * VLC for iOS
  *****************************************************************************
- * Copyright (c) 2014-2017 VideoLAN. All rights reserved.
+ * Copyright (c) 2014-2019 VideoLAN. All rights reserved.
  *
  * Authors: Pierre Sagaspe <pierre.sagaspe # me.com>
  *
@@ -14,5 +14,6 @@
 
 - (NSArray *)PlexMediaServerParser:(NSString *)adress port:(NSNumber *)port navigationPath:(NSString *)navPath authentification:(NSString *)auth error:(NSError *__autoreleasing *)error;
 - (NSArray *)PlexExtractDeviceInfo:(NSData *)data;
+- (NSString *)PlexExtractToken:(NSData *)data;
 
 @end

+ 25 - 2
SharedSources/ServerBrowsing/Plex/VLCPlexParser.m

@@ -2,7 +2,7 @@
  * VLCPlexParser.m
  * VLC for iOS
  *****************************************************************************
- * Copyright (c) 2014-2017 VideoLAN. All rights reserved.
+ * Copyright (c) 2014-2019 VideoLAN. All rights reserved.
  *
  * Authors: Pierre Sagaspe <pierre.sagaspe # me.com>
  *
@@ -144,6 +144,23 @@ static NSString *const kPlexVLCDeviceName = @"VLC for iOS";
     return [NSArray arrayWithArray:_containerInfo];
 }
 
+- (NSString *)PlexExtractToken:(NSData *)data
+{
+    NSString *token = @"";
+    _containerInfo = [[NSMutableArray alloc] init];
+    _dicoInfo = [[NSMutableDictionary alloc] init];
+    NSXMLParser *xmlparser = [[NSXMLParser alloc] initWithData:data];
+    [xmlparser setDelegate:self];
+
+    if (![xmlparser parse]) {
+        NSLog(@"PlexParser data Errors : %@", data);
+    } else {
+        token = [[NSArray arrayWithArray:_containerInfo] firstObject][@"token"];
+    }
+
+    return token;
+}
+
 #pragma mark - Parser
 
 - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
@@ -216,6 +233,12 @@ static NSString *const kPlexVLCDeviceName = @"VLC for iOS";
             [_dicoInfo setObject:[attributeDict objectForKey:@"clientIdentifier"] forKey:@"clientIdentifier"];
         if ([attributeDict objectForKey:@"id"])
             [_dicoInfo setObject:[attributeDict objectForKey:@"id"] forKey:@"id"];
+
+    } else if ([elementName isEqualToString:@"user"]) {
+        if ([attributeDict objectForKey:@"authToken"])
+            [_dicoInfo setObject:[attributeDict objectForKey:@"authToken"] forKey:@"token"];
+        if ([attributeDict objectForKey:@"authenticationToken"])
+            [_dicoInfo setObject:[attributeDict objectForKey:@"authenticationToken"] forKey:@"token"];
     }
 
     if ([attributeDict objectForKey:@"thumb"] && ([elementName isEqualToString:@"Video"] || [elementName isEqualToString:@"Directory"] || [elementName isEqualToString:@"Part"] || [elementName isEqualToString:@"Track"]))
@@ -225,7 +248,7 @@ static NSString *const kPlexVLCDeviceName = @"VLC for iOS";
 
 - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
 {
-    if (([elementName isEqualToString:@"Video"] || [elementName isEqualToString:@"Track"] || [elementName isEqualToString:@"Directory"] || [elementName isEqualToString:@"MediaContainer"] || [elementName isEqualToString:@"Device"]) && [_dicoInfo count] > 0) {
+    if (([elementName isEqualToString:@"Video"] || [elementName isEqualToString:@"Track"] || [elementName isEqualToString:@"Directory"] || [elementName isEqualToString:@"MediaContainer"] || [elementName isEqualToString:@"Device"] || [elementName isEqualToString:@"user"]) && [_dicoInfo count] > 0) {
         [_containerInfo addObject:_dicoInfo];
         _dicoInfo = [[NSMutableDictionary alloc] init];
     }

+ 2 - 6
SharedSources/ServerBrowsing/Plex/VLCPlexWebAPI.h

@@ -2,7 +2,7 @@
  * VLCPlexWebAPI.h
  * VLC for iOS
  *****************************************************************************
- * Copyright (c) 2014-2017 VideoLAN. All rights reserved.
+ * Copyright (c) 2014-2019 VideoLAN. All rights reserved.
  *
  * Authors: Pierre Sagaspe <pierre.sagaspe # me.com>
  *
@@ -12,17 +12,13 @@
 #import <UIKit/UIKit.h>
 @interface VLCPlexWebAPI : NSObject
 
-- (BOOL)PlexCreateIdentification:(NSString *)username password:(NSString *)password;
-- (NSArray *)PlexBasicAuthentification:(NSString *)username password:(NSString *)password;
+- (NSMutableDictionary *)PlexBasicAuthentification:(NSString *)username password:(NSString *)password;
 - (NSString *)PlexAuthentification:(NSString *)username password:(NSString *)password;
 - (NSString *)urlAuth:(NSString *)url authentification:(NSString *)auth;
-- (NSData *)HttpRequestWithCookie:(NSURL *)url cookies:(NSArray *)authToken HTTPMethod:(NSString *)method;
-- (NSURL *)CreatePlexStreamingURL:(NSString *)address port:(NSString *)port videoKey:(NSString *)key username:(NSString *)username deviceInfo:(NSDictionary *)deviceInfo session:(NSString *)session;
 - (void)stopSession:(NSString *)adress port:(NSString *)port session:(NSString *)session;
 - (NSInteger)MarkWatchedUnwatchedMedia:(NSString *)address port:(NSString *)port videoRatingKey:(NSString *)ratingKey state:(NSString *)state authentification:(NSString *)auth;
 - (NSString *)getFileSubtitleFromPlexServer:(NSDictionary *)mediaObject modeStream:(BOOL)modeStream error:(NSError *__autoreleasing*)error;
 - (NSString *)getSession;
-- (NSData *)PlexDeviceInfo:(NSArray *)cookies;
 
 + (NSString *)urlAuth:(NSString *)url authentification:(NSString *)auth;
 

+ 64 - 129
SharedSources/ServerBrowsing/Plex/VLCPlexWebAPI.m

@@ -2,7 +2,7 @@
  * VLCPlexWebAPI.m
  * VLC for iOS
  *****************************************************************************
- * Copyright (c) 2014-2017 VideoLAN. All rights reserved.
+ * Copyright (c) 2014-2019 VideoLAN. All rights reserved.
  *
  * Authors: Pierre Sagaspe <pierre.sagaspe # me.com>
  *
@@ -15,98 +15,86 @@
 #import "sysexits.h"
 
 #define kPlexMediaServerSignIn @"https://plex.tv/users/sign_in.xml"
+//#define kPlexMediaServerSignIn @"https://plex.tv/users/sign_in.json"
 #define kPlexURLdeviceInfo @"https://plex.tv/devices.xml"
 
 @implementation VLCPlexWebAPI
 
 #pragma mark - Authentification
 
-- (NSArray *)PlexBasicAuthentification:(NSString *)username password:(NSString *)password
+- (NSMutableDictionary *)PlexBasicAuthentification:(NSString *)username password:(NSString *)password
 {
-    NSArray *authToken = nil;
-
     NSURL *url = [NSURL URLWithString:kPlexMediaServerSignIn];
 
-    NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:url];
-    if ([cookies count])
-        return cookies;
-
-    NSString *authString = [NSString stringWithFormat:@"%@:%@", username, password];
-    NSData *authData = [authString dataUsingEncoding:NSASCIIStringEncoding];
-    NSString *authBase64 = [NSString stringWithFormat:@"Basic %@", [authData base64EncodedStringWithOptions:0]];
-
-    NSString *timeString = [NSString stringWithFormat:@"%ld", (long)[[NSDate date] timeIntervalSince1970]];
-
-    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:5.0];
-    [request setHTTPMethod:@"POST"];
-    [request setValue:authBase64 forHTTPHeaderField:@"Authorization"];
-    [request setValue:timeString forHTTPHeaderField:@"X-Plex-Access-Time"];
-
-    NSHTTPURLResponse *response = nil;
-    NSError *error = nil;
-    [self sendSynchronousRequest:request returningResponse:&response error:&error];
-
-    authToken = [NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:[NSURL URLWithString:@""]];
-    [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:authToken forURL:url mainDocumentURL:nil];
+    NSString *appVersion = [NSString stringWithFormat:@"%@", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]];
 
-    return authToken;
-}
+    NSMutableDictionary *authDict = [NSMutableDictionary dictionary];
+    NSString *clientIdentifier = [NSString stringWithFormat:@"PlexVLC-%@", [[UIDevice currentDevice] model]];
+    [authDict setObject:clientIdentifier forKey:@"clientIdentifier"];
+    [authDict setObject:@"PlexVLC" forKey:@"product"];
+    [authDict setObject:appVersion forKey:@"productVersion"];
+    [authDict setObject:[[UIDevice currentDevice] model] forKey:@"device"];
+    [authDict setObject:@"" forKey:@"token"];
+    [authDict setObject:@"VLC for iOS" forKey:@"name"];
+    [authDict setObject:username forKey:@"username"];
 
-- (BOOL)PlexCreateIdentification:(NSString *)username password:(NSString *)password
-{
-    NSURL *url = [NSURL URLWithString:kPlexMediaServerSignIn];
+    NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:url];
+    if ([cookies count]) {
+        for (NSHTTPCookie *cookie in cookies) {
+            if ([cookie.name isEqualToString:@"plexToken"]) {
+                [authDict setObject:cookie.value forKey:@"token"];
+                return authDict;
+            }
+        }
+    }
 
     NSString *authString = [NSString stringWithFormat:@"%@:%@", username, password];
     NSData *authData = [authString dataUsingEncoding:NSASCIIStringEncoding];
     NSString *authBase64 = [NSString stringWithFormat:@"Basic %@", [authData base64EncodedStringWithOptions:0]];
 
-    NSString *appVersion = [NSString stringWithFormat:@"%@", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]];
     NSString *timeString = [NSString stringWithFormat:@"%ld", (long)[[NSDate date] timeIntervalSince1970]];
 
     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:5.0];
     [request setHTTPMethod:@"POST"];
     [request setValue:authBase64 forHTTPHeaderField:@"Authorization"];
-    [request setValue:@"iOS" forHTTPHeaderField:@"X-Plex-Platform"];
-    [request setValue:[[UIDevice currentDevice] systemVersion] forHTTPHeaderField:@"X-Plex-Platform-Version"];
-    [request setValue:@"client" forHTTPHeaderField:@"X-Plex-Provides"];
-    [request setValue:[[[UIDevice currentDevice] identifierForVendor] UUIDString] forHTTPHeaderField:@"X-Plex-Client-Identifier"];
-    [request setValue:@"PlexVLC" forHTTPHeaderField:@"X-Plex-Product"];
+    [request setValue:[authDict objectForKey:@"device"] forHTTPHeaderField:@"X-Plex-Device"];
+    [request setValue:[authDict objectForKey:@"name"] forHTTPHeaderField:@"X-Plex-Device-Name"];
+    [request setValue:[authDict objectForKey:@"clientIdentifier"] forHTTPHeaderField:@"X-Plex-Client-Identifier"];
+    [request setValue:[authDict objectForKey:@"product"] forHTTPHeaderField:@"X-Plex-Product"];
     [request setValue:appVersion forHTTPHeaderField:@"X-Plex-Version"];
-    [request setValue:@"VLC for iOS" forHTTPHeaderField:@"X-Plex-Device-Name"];
-    [request setValue:[[UIDevice currentDevice] model] forHTTPHeaderField:@"X-Plex-Device"];
-    [request setValue:timeString forHTTPHeaderField:@"X-Plex-Access-Time"];
-
-    NSHTTPURLResponse *response = nil;
-    NSError *error = nil;
-    [self sendSynchronousRequest:request returningResponse:&response error:&error];
-
-    if ([response statusCode] == 201)
-        return YES;
-    else {
-        APLog(@"Plex Create Identification Error : %@", [response allHeaderFields]);
-        return NO;
-    }
-}
-
-- (NSData *)HttpRequestWithCookie:(NSURL *)url cookies:(NSArray *)authToken HTTPMethod:(NSString *)method
-{
-    NSString *timeString = [NSString stringWithFormat:@"%ld", (long)[[NSDate date] timeIntervalSince1970]];
-
-    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:5.0];
-    NSDictionary *headers = [NSHTTPCookie requestHeaderFieldsWithCookies:authToken];
-    [request setHTTPMethod:method];
-    [request setAllHTTPHeaderFields:headers];
     [request setValue:timeString forHTTPHeaderField:@"X-Plex-Access-Time"];
 
     NSHTTPURLResponse *response = nil;
     NSError *error = nil;
-    NSData *urlData = [self sendSynchronousRequest:request returningResponse:&response error:&error];
-
+    NSData *data = [self sendSynchronousRequest:request returningResponse:&response error:&error];
     // for debug
-    //NSString *debugStr = [[NSString alloc] initWithData:urlData encoding:NSUTF8StringEncoding];
-    //APLog(@"data : %@", debugStr);
+    /*if ([response statusCode] == 201) {
+        NSLog(@"Plex token : %@", [response allHeaderFields]);
+        NSLog(@"Plex token : %@", [NSString stringWithUTF8String:[data bytes]]);
+    } else {
+        NSLog(@"%ld", (long)response.statusCode);
+        NSLog(@"Plex Create Identification Error : %@", [response allHeaderFields]);
+        NSLog(@"Plex token error : %@", [NSString stringWithUTF8String:[data bytes]]);
+    }*/
+
+    VLCPlexParser *plexParser = [[VLCPlexParser alloc] init];
+    NSString *token = [plexParser PlexExtractToken:data];
+    if (![token isEqualToString:@""]) {
+        [authDict setObject:token forKey:@"token"];
+        NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
+        [cookieProperties setObject:@"plexToken" forKey:NSHTTPCookieName];
+        [cookieProperties setObject:[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]] forKey:NSHTTPCookieValue];
+        [cookieProperties setObject:@"plex.tv" forKey:NSHTTPCookieDomain];
+        [cookieProperties setObject:kPlexMediaServerSignIn forKey:NSHTTPCookieOriginURL];
+        [cookieProperties setObject:@"/" forKey:NSHTTPCookiePath];
+        [cookieProperties setObject:@"0" forKey:NSHTTPCookieVersion];
+        [cookieProperties setObject:token forKey:NSHTTPCookieValue];
+
+        NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieProperties];
+        [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
+    }
 
-    return urlData;
+    return authDict;
 }
 
 - (NSString *)PlexAuthentification:(NSString *)username password:(NSString *)password
@@ -114,36 +102,16 @@
     NSString *authentification = @"";
 
     if ((![username isEqualToString:@""]) && (![password isEqualToString:@""])) {
-        NSArray *deviceInfo;
-        NSString *appVersion = [NSString stringWithFormat:@"%@", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]];
-
-        NSArray *authToken = [self PlexBasicAuthentification:username password:password];
-        NSData *data = [self PlexDeviceInfo:authToken];
 
-        VLCPlexParser *plexParser = [[VLCPlexParser alloc] init];
-        deviceInfo = [plexParser PlexExtractDeviceInfo:data];
-        NSDictionary *firstObject = [deviceInfo firstObject];
+        NSDictionary *authDict = [self PlexBasicAuthentification:username password:password];
 
-        if ((deviceInfo.count == 0) ||
-            ((![firstObject[@"productVersion"] isEqualToString:appVersion]) ||
-             (![firstObject[@"platformVersion"] isEqualToString:[[UIDevice currentDevice] systemVersion]]))) {
-
-            [self PlexCreateIdentification:username password:password];
-            data = [self PlexDeviceInfo:authToken];
-            deviceInfo = [plexParser PlexExtractDeviceInfo:data];
-        }
-
-        if (deviceInfo.count != 0) {
-            firstObject = [deviceInfo firstObject];
-            UIDevice *currentDevice = [UIDevice currentDevice];
-            authentification = [[NSString stringWithFormat:@"X-Plex-Product=%@&X-Plex-Version=%@&X-Plex-Client-Identifier=%@&X-Plex-Platform=iOS&X-Plex-Platform-Version=%@&X-Plex-Device=%@&X-Plex-Device-Name=%@&X-Plex-Token=%@&X-Plex-Username=%@",
-                                 firstObject[@"product"],
-                                 firstObject[@"productVersion"],
-                                 firstObject[@"clientIdentifier"],
-                                 [currentDevice systemVersion],
-                                 [currentDevice model],
-                                 firstObject[@"name"],
-                                 firstObject[@"token"], username]
+        if (![[authDict objectForKey:@"token"] isEqualToString:@""]) {
+            authentification = [[NSString stringWithFormat:@"X-Plex-Product=%@&X-Plex-Version=%@&X-Plex-Client-Identifier=%@&X-Plex-Device=%@&X-Plex-Token=%@&X-Plex-Username=%@",
+                                 [authDict objectForKey:@"product"],
+                                 [authDict objectForKey:@"productVersion"],
+                                 [authDict objectForKey:@"clientIdentifier"],
+                                 [authDict objectForKey:@"device"],
+                                 [authDict objectForKey:@"token"], username]
                                 stringByReplacingOccurrencesOfString:@" " withString:@"+"];
         }
     }
@@ -151,7 +119,8 @@
     return authentification;
 }
 
-- (NSString *)urlAuth:(NSString *)url authentification:(NSString *)auth {
+- (NSString *)urlAuth:(NSString *)url authentification:(NSString *)auth 
+{
     return [[self class] urlAuth:url authentification:auth];
 }
 
@@ -159,7 +128,7 @@
 {
     NSString *key = @"";
 
-    if (![auth isEqualToString:@""]) {
+    if ((![auth isEqualToString:@""]) && (auth)) {
         NSRange isRange = [url rangeOfString:@"?" options:NSCaseInsensitiveSearch];
         if(isRange.location != NSNotFound)
             key = @"&";
@@ -240,33 +209,6 @@
     return FileSubtitlePath;
 }
 
-- (NSURL *)CreatePlexStreamingURL:(NSString *)address port:(NSString *)port videoKey:(NSString *)key username:(NSString *)username deviceInfo:(NSDictionary *)deviceInfo session:(NSString *)session
-{
-    /* it starts video transcoding but without sound !!! why ? */
-    UIDevice *currentDevice = [UIDevice currentDevice];
-
-    NSString *authentification = [[NSString stringWithFormat:@"&X-Plex-Product=%@&X-Plex-Version=%@&X-Plex-Client-Identifier=%@&X-Plex-Platform=iOS&X-Plex-Platform-Version=%@&X-Plex-Device=%@&X-Plex-Device-Name=%@&X-Plex-Token=%@&X-Plex-Username=%@",
-                                   deviceInfo[@"product"],
-                                   deviceInfo[@"productVersion"],
-                                   deviceInfo[@"clientIdentifier"],
-                                   [currentDevice systemVersion],
-                                   [currentDevice model],
-                                   deviceInfo[@"name"],
-                                   deviceInfo[@"token"], username]
-                                  stringByReplacingOccurrencesOfString:@" " withString:@"+"];
-
-    NSString *unescaped = [NSString stringWithFormat:@"http://127.0.0.1:32400%@", key];
-    NSString *escapedString = [unescaped stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
-
-    NSURL *url = [NSURL URLWithString:[[NSString stringWithFormat:@"http://%@%@/video/:/transcode/universal/start.m3u8?path=%@&mediaIndex=0&partIndex=0&protocol=hls&offset=0&fastSeek=1&directPlay=0&directStream=1&subtitleSize=100&audioBoost=100&session=%@&subtitles=burn",
-                                        address,
-                                        port,
-                                        escapedString,
-                                        session] stringByAppendingString:authentification]];
-
-    return url;
-}
-
 - (void)stopSession:(NSString *)adress port:(NSString *)port session:(NSString *)session
 {
     NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%@/video/:/transcode/universal/stop?session=%@", adress, port, session]];
@@ -288,13 +230,6 @@
     return session;
 }
 
-- (NSData *)PlexDeviceInfo:(NSArray *)cookies
-{
-    NSURL *url = [NSURL URLWithString:kPlexURLdeviceInfo];
-    NSData *data = [self HttpRequestWithCookie:url cookies:cookies HTTPMethod:@"GET"];
-    return data;
-}
-
 - (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error
 {
     NSError __block *erreur = NULL;