Revision baaf1397 pithos-macos/PithosAccount.m

b/pithos-macos/PithosAccount.m
44 44
#import "PithosUtilities.h"
45 45
#import "pithos_macosAppDelegate.h"
46 46

  
47
@interface PithosAccount (Internal)
48
- (BOOL)urlIsValid:(NSString *)urlString;
47
static NSString *defaultAuthURLString = @"https://accounts.okeanos.grnet.gr/identity/v2.0";
48
static NSString *defaultManualURLString = @"https://pithos.okeanos.grnet.gr";
49

  
50
@interface NSString(Additions)
51
- (BOOL)isValidURL;
52
- (NSString *)stringByRemovingTrailingSlashes;
53
@end
54

  
55
@implementation NSString(Additions)
56
- (BOOL)isValidURL {
57
    NSURL *URL = [NSURL URLWithString:self];
58
    return (URL && URL.scheme && URL.host);
59
}
60

  
61
- (NSString *)stringByRemovingTrailingSlashes {
62
    NSString *stringWithRemovedTrailingSlashes = [self copy];
63
    while ([stringWithRemovedTrailingSlashes hasSuffix:@"/"]) {
64
        stringWithRemovedTrailingSlashes = [stringWithRemovedTrailingSlashes substringToIndex:(stringWithRemovedTrailingSlashes.length - 1)];
65
    }
66
    return stringWithRemovedTrailingSlashes;
67
}
49 68
@end
50 69

  
51 70
@implementation PithosAccount
52 71
@synthesize uniqueName, active, name, clientVersion;
53 72
@synthesize syncActive, syncDirectoryPath, syncAccountsDictionary, syncSkipHidden, syncLastCompleted, syncDaemon;
54
@synthesize serverURL, versionResource, loginResource, publicResource, userCatalogResource;
55
@synthesize authUser, authToken, storageURLPrefix, authURL, loginURLPrefix, publicURLPrefix, userCatalogURL, userCatalog;
73
@synthesize authURLString, pithosObjectStoreURLString, astakosAccountURLString, astakosWebloginURLString, manual;
74
@synthesize authToken, authUser, userCatalog;
75
@synthesize ignoreSSLErrors;
56 76
@synthesize pithos, accountNode, sharingAccountsNode;
77
@synthesize tokensURL, storageURLPrefix, loginURL, userCatalogURL, publicURLPrefix;
57 78

  
58 79
#pragma mark -
59 80
#pragma mark Object Lifecycle
......
63 84
    pithosAccount.uniqueName = [NSString stringWithFormat:@"pithosAccount-%f", [NSDate timeIntervalSinceReferenceDate]];
64 85
    pithosAccount.clientVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
65 86
    pithosAccount.syncSkipHidden = YES;
87
    pithosAccount.authURLString = defaultAuthURLString;
66 88
    return pithosAccount;
67 89
}
68 90

  
69

  
70 91
- (NSString *)description {
71
    return [NSString stringWithFormat:@"uniqueName: %@, active: %d, name: %@, clientVersion: %@, syncActive: %d, syncDirectoryPath: %@, syncAccountsDictionary: %@, syncSkipHidden: %d, syncLastCompleted: %@, serverURL: %@, versionResource: %@, loginResource: %@, publicResource: %@, userCatalogResource: %@, authUser: %@, authToken: %@, storageURLPrefix: %@, authURL: %@, loginURLPrefix: %@, publicURLPrefix: %@",
72
            uniqueName, active, name, clientVersion, syncActive, syncDirectoryPath, syncAccountsDictionary, syncSkipHidden, syncLastCompleted, serverURL, versionResource, loginResource, publicResource, userCatalogResource, authUser, authToken, storageURLPrefix, authURL, loginURLPrefix, publicURLPrefix];
73
}
74

  
75
#pragma mark -
76
#pragma mark Internal
77

  
78
- (BOOL)urlIsValid:(NSString *)urlString {
79
    if (urlString) {
80
        NSURL *url = [NSURL URLWithString:urlString];
81
        if (url && url.scheme && url.host)
82
            return YES;
83
    }
84
    return NO;
92
    return [NSString stringWithFormat:@"uniqueName: %@, active: %d, name: %@, clientVersion: %@, syncActive: %d, syncDirectoryPath: %@, syncAccountsDictionary: %@, syncSkipHidden: %d, syncLastCompleted: %@, authURLString: %@, pithosObjectStoreURLString:%@ , astakosAccountURLString: %@, astakosWebloginURLString: %@, manual: %d, authToken: %@, authUser: %@",
93
            uniqueName, active, name, clientVersion, syncActive, syncDirectoryPath, syncAccountsDictionary, syncSkipHidden, syncLastCompleted, authURLString, pithosObjectStoreURLString, astakosAccountURLString, astakosWebloginURLString, manual, authToken, authUser];
85 94
}
86 95

  
87 96
#pragma mark -
......
200 209
    return syncDaemon;
201 210
}
202 211

  
203
- (NSString *)serverURL {
204
    if (![self urlIsValid:serverURL]) {
205
        serverURL = @"https://pithos.okeanos.grnet.gr";
212
- (void)setAuthURLString:(NSString *)anAuthURLString {
213
    NSString *tmpURLString = [anAuthURLString stringByRemovingTrailingSlashes];
214
    if (![authURLString isEqualToString:tmpURLString]) {
215
        authURLString = tmpURLString;
216

  
217
        @synchronized(self) {
218
            updatePithos = YES;
219
            resetSyncDaemonLocalState = YES;
220
            syncLastCompleted = nil;
221
        }
206 222
    }
207
    return serverURL;
208 223
}
209 224

  
210
- (void)setServerURL:(NSString *)aServerURL {
211
    if (![self.serverURL isEqualToString:aServerURL] && [self urlIsValid:aServerURL]) {
212
        serverURL = aServerURL;
213
        storageURLPrefix = nil;
214
        authURL = nil;
215
        publicURLPrefix = nil;
216
        loginURLPrefix = nil;
217
        userCatalogURL = nil;
225
- (void)setPithosObjectStoreURLString:(NSString *)aPithosObjectStoreURLString {
226
    NSString *tmpURLString = [aPithosObjectStoreURLString stringByRemovingTrailingSlashes];
227
    if (![pithosObjectStoreURLString isEqualToString:tmpURLString]) {
228
        pithosObjectStoreURLString = tmpURLString;
218 229

  
219 230
        @synchronized(self) {
220 231
            updatePithos = YES;
221
            resetSyncDaemonLocalState = YES;
222
            syncLastCompleted = nil;
223 232
        }
224 233
    }
225 234
}
226 235

  
227
- (NSString *)versionResource {
228
    if (!versionResource) {
229
        versionResource = @"v1";
236
- (void)setAstakosAccountURLString:(NSString *)anAstakosAccountURLString {
237
    NSString *tmpURLString = [anAstakosAccountURLString stringByRemovingTrailingSlashes];
238
    if (![astakosAccountURLString isEqualToString:tmpURLString]) {
239
        astakosAccountURLString = tmpURLString;
240
        
241
        @synchronized(self) {
242
            updatePithos = YES;
243
        }
230 244
    }
231
    return versionResource;
232 245
}
233 246

  
234
- (NSString *)loginResource {
235
    if (!loginResource) {
236
        loginResource = @"login";
247
- (void)setAstakosWebloginURLString:(NSString *)anAstakosWebloginURLString {
248
    NSString *tmpURLString = [anAstakosWebloginURLString stringByRemovingTrailingSlashes];
249
    if (![astakosWebloginURLString isEqualToString:tmpURLString]) {
250
        astakosWebloginURLString = tmpURLString;
237 251
    }
238
    return loginResource;
239 252
}
240 253

  
241
- (NSString *)userCatalogResource {
242
    if (!userCatalogResource) {
243
        userCatalogResource = @"user_catalogs";
254
- (void)setAuthToken:(NSString *)anAuthToken {
255
    if (anAuthToken.length && ![anAuthToken isEqualToString:authToken]) {
256
        authToken = anAuthToken;
257
        
258
        @synchronized(self) {
259
            updatePithos = YES;
260
        }
244 261
    }
245
    return userCatalogResource;
246 262
}
247 263

  
248 264
- (void)setAuthUser:(NSString *)anAuthUser {
249
    if ([anAuthUser length] && ![anAuthUser isEqualToString:authUser]) {
265
    if (anAuthUser.length && ![anAuthUser isEqualToString:authUser]) {
250 266
        authUser = anAuthUser;
251 267
        
252 268
        @synchronized(self) {
......
258 274
    }
259 275
}
260 276

  
261
- (void)setAuthToken:(NSString *)anAuthToken {
262
    if ([anAuthToken length] && ![anAuthToken isEqualToString:authToken]) {
263
        authToken = anAuthToken;
277
- (void)setIgnoreSSLErrors:(BOOL)anIgnoreSSLErrors {
278
    if (anIgnoreSSLErrors != ignoreSSLErrors) {
279
        ignoreSSLErrors = anIgnoreSSLErrors;
264 280
        
265 281
        @synchronized(self) {
266 282
            updatePithos = YES;
......
268 284
    }
269 285
}
270 286

  
271
- (NSString *)storageURLPrefix {
272
    if (![self urlIsValid:storageURLPrefix]) {
273
        storageURLPrefix = [self.serverURL stringByAppendingFormat:@"/%@", self.versionResource];
274
    }
275
    return storageURLPrefix;
276
}
277

  
278
- (void)setStorageURLPrefix:(NSString *)aStorageURLPrefix {
279
    if (![self.storageURLPrefix isEqualToString:aStorageURLPrefix] && [self urlIsValid:aStorageURLPrefix]) {
280
        storageURLPrefix = aStorageURLPrefix;
281
    }
282
}
283

  
284
- (NSString *)authURL {
285
    if (![self urlIsValid:authURL]) {
286
        authURL = [self.serverURL stringByAppendingFormat:@"/%@", self.versionResource];
287
    }
288
    return authURL;
289
}
290

  
291
- (void)setAuthURL:(NSString *)anAuthURL {
292
    if (![self.authURL isEqualToString:anAuthURL] && [self urlIsValid:anAuthURL]) {
293
        authURL = anAuthURL;
294
    }
295
}
296

  
297
- (NSString *)publicURLPrefix {
298
    if (![self urlIsValid:publicURLPrefix]) {
299
        if (publicResource)
300
            publicURLPrefix = [self.serverURL stringByAppendingFormat:@"/%@", publicResource];
301
        else
302
            publicURLPrefix = [self.serverURL copy];
303
    }
304
    return publicURLPrefix;
305
}
306

  
307
- (void)setPublicURLPrefix:(NSString *)aPublicURLPrefix {
308
    if (![self.publicURLPrefix isEqualToString:aPublicURLPrefix] && [self urlIsValid:aPublicURLPrefix]) {
309
        publicURLPrefix = aPublicURLPrefix;
310
    }
311
}
312

  
313
- (NSString *)loginURLPrefix {
314
    if (![self urlIsValid:loginURLPrefix]) {
315
        loginURLPrefix = [self.serverURL stringByAppendingFormat:@"/%@", self.loginResource];
316
    }
317
    return loginURLPrefix;
318
}
319

  
320
- (void)setLoginURLPrefix:(NSString *)aLoginURLPrefix {
321
    if (![self.loginURLPrefix isEqualToString:aLoginURLPrefix] && [self urlIsValid:aLoginURLPrefix]) {
322
        loginURLPrefix = aLoginURLPrefix;
323
    }
324
}
325

  
326
- (NSString *)userCatalogURL {
327
    if (![self urlIsValid:userCatalogURL]) {
328
        userCatalogURL = [self.serverURL stringByAppendingFormat:@"/%@", self.userCatalogResource];
329
    }
330
    return userCatalogURL;
331
}
332

  
333
- (void)setUserCatalogURL:(NSString *)aUserCatalogURL {
334
    if (![self.userCatalogURL isEqualToString:aUserCatalogURL] && [self urlIsValid:aUserCatalogURL]) {
335
        userCatalogURL = aUserCatalogURL;
336
    }
337
}
338

  
339 287
- (NSMutableDictionary *)userCatalog {
340 288
    if (!userCatalog) {
341 289
        userCatalog = [NSMutableDictionary dictionary];
......
347 295
    @synchronized(self) {
348 296
        if (!pithos || updatePithos) {
349 297
            pithos = [ASIPithos pithos];
350
            pithos.authUser = authUser;
351 298
            pithos.authToken = authToken;
299
            pithos.authUser = authUser;
300
            
301
            pithos.ignoreSSLErrors = ignoreSSLErrors;
302
            
303
            pithos.tokensURL = self.tokensURL;
352 304
            pithos.storageURLPrefix = self.storageURLPrefix;
353
            pithos.authURL = self.authURL;
354
            pithos.publicURLPrefix = self.publicURLPrefix;
355 305
            pithos.userCatalogURL = self.userCatalogURL;
306
            pithos.publicURLPrefix = self.publicURLPrefix;
356 307
            updatePithos = NO;
357 308
        }
358 309
    }
......
377 328
    return sharingAccountsNode;
378 329
}
379 330

  
331
- (NSString *)tokensURL {
332
    return [authURLString stringByAppendingString:@"/tokens"];
333
}
334

  
335
- (NSString *)storageURLPrefix {
336
    return [pithosObjectStoreURLString copy];
337
}
338

  
339
- (NSString *)loginURL {
340
    return [astakosWebloginURLString stringByAppendingString:@"/login"];
341
}
342

  
343
- (NSString *)publicURLPrefix {
344
    return [pithosObjectStoreURLString copy];
345
}
346

  
347
- (NSString *)userCatalogURL {
348
    return [astakosAccountURLString stringByAppendingString:@"/user_catalogs"];
349
}
350

  
380 351
#pragma mark -
381 352
#pragma mark Actions
382 353

  
383
- (void)authenticateWithServerURL:(NSString *)aServerURL authUser:(NSString *)anAuthUser authToken:(NSString *)anAuthToken {
384
    self.serverURL = aServerURL;
385
    self.authUser = anAuthUser;
354
- (void)updateWithAuthURLString:(NSString *)anAuthURLString
355
     pithosObjectStoreURLString:(NSString *)aPithosObjectStoreURLString
356
        astakosAccountURLString:(NSString *)anAstakosAccountURLString
357
       astakosWebloginURLString:(NSString *)anAstakosWebloginURLString
358
                         manual:(BOOL)aManual
359
                      authToken:(NSString *)anAuthToken
360
                      authUser:(NSString *)anAuthUser
361
                ignoreSSLErrors:(BOOL)anIgnoreSSLErrors {
362
    self.authURLString = anAuthURLString;
363
    self.pithosObjectStoreURLString = aPithosObjectStoreURLString;
364
    self.astakosAccountURLString = anAstakosAccountURLString;
365
    self.astakosWebloginURLString = anAstakosWebloginURLString;
366
    self.manual = aManual;
386 367
    self.authToken = anAuthToken;
387
    DLog(@"Account: %@\nauthentication", self);
388
    if (![authUser length] || ![authToken length]) {
368
    self.authUser = anAuthUser;
369
    self.ignoreSSLErrors = anIgnoreSSLErrors;
370
    DLog(@"Account updated: %@", self);
371
    if (!authToken.length || !authUser.length) {
389 372
        self.active = NO;
390 373
        self.syncActive = NO;
391
        // XXX Show preferences with self as the selected account?
392 374
    } else  {
393
        [self updateUserCatalogForForDisplaynames:nil UUIDs:[NSArray arrayWithObject:authUser]];
394
        
375
        [self updateUserCatalogForDisplaynames:nil UUIDs:[NSArray arrayWithObject:authUser]];
376

  
395 377
        self.active = YES;
396 378
        if (syncDaemon) {
397 379
            self.syncDaemon.pithos = self.pithos;
......
412 394
    }
413 395
}
414 396

  
415
- (void)loginWithServerURL:(NSString *)aServerURL {
416
    self.serverURL = aServerURL;
417
    NSProcessInfo *processInfo = [NSProcessInfo processInfo];
418
    NSString *loginURL = [NSString stringWithFormat:@"%@?next=pithos://%d/%@&force=", 
419
                          self.loginURLPrefix, [processInfo processIdentifier], [ASIPithosRequest encodeToPercentEscape:self.name]];
420
    DLog(@"Account: %@\nloginURL: %@", self, loginURL);
421
    [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:loginURL]];
422
}
423

  
424 397
- (void)updateSyncWithSyncActive:(BOOL)aSyncActive 
425 398
               syncDirectoryPath:(NSString *)aSyncDirectoryPath 
426 399
          syncAccountsDictionary:(NSMutableDictionary *)aSyncAccountsDictionary 
......
438 411
    }    
439 412
}
440 413

  
441
- (ASIPithosRequest *)updateUserCatalogForForDisplaynames:(NSArray *)displaynames UUIDs:(NSArray *)UUIDs {
414
- (NSMutableDictionary *)servicesFromServiceCatalogRequest:(ASIPithosRequest *)serviceCatalogRequest {
415
    NSMutableDictionary *services = [NSMutableDictionary dictionary];
416
    if (serviceCatalogRequest.responseStatusCode == 200) {
417
        BOOL pithosObjectStoreFound = NO;
418
        BOOL astakosAccountFound = NO;
419
        BOOL astakosWebloginFound = NO;
420
        NSArray *serviceCatalog = [serviceCatalogRequest serviceCatalog];
421
        for (NSDictionary *service in serviceCatalog) {
422
            NSString *serviceName = [service objectForKey:@"name"];
423
            if (!pithosObjectStoreFound && [serviceName isEqualToString:@"pithos_object-store"]) {
424
                [services setObject:[[[service objectForKey:@"endpoints"] objectAtIndex:0] objectForKey:@"publicURL"]
425
                             forKey:@"PithosObjectStoreURLString"];
426
                pithosObjectStoreFound = YES;
427
            } else if (!astakosAccountFound && [serviceName isEqualToString:@"astakos_account"]) {
428
                [services setObject:[[[service objectForKey:@"endpoints"] objectAtIndex:0] objectForKey:@"publicURL"]
429
                             forKey:@"AstakosAccountURLString"];
430
                astakosAccountFound = YES;
431
            } else if (!astakosWebloginFound && [serviceName isEqualToString:@"astakos_weblogin"]) {
432
                [services setObject:[[[service objectForKey:@"endpoints"] objectAtIndex:0] objectForKey:@"SNF:webloginURL"]
433
                             forKey:@"AstakosWebloginURLString"];
434
                astakosWebloginFound = YES;
435
            }
436
            if (pithosObjectStoreFound && astakosAccountFound && astakosWebloginFound)
437
                break;
438
        }
439
    }
440
    return services;
441
}
442

  
443
- (void)updateServicesFromServiceCatalogRequest:(ASIPithosRequest *)serviceCatalogRequest {
444
    if (serviceCatalogRequest.responseStatusCode == 200) {
445
        NSMutableDictionary *services = [self servicesFromServiceCatalogRequest:serviceCatalogRequest];
446
        self.pithosObjectStoreURLString = [services objectForKey:@"PithosObjectStoreURLString"];
447
        self.astakosAccountURLString = [services objectForKey:@"AstakosAccountURLString"];
448
        self.astakosWebloginURLString = [services objectForKey:@"AstakosWebloginURLString"];
449
        self.manual = NO;
450
        
451
        if (authToken.length) {
452
            NSDictionary *token = [serviceCatalogRequest token];
453
            self.authToken = [token objectForKey:@"id"];
454
            self.authUser = [[token objectForKey:@"tenant"] objectForKey:@"id"];
455
        }
456
    } else if (serviceCatalogRequest.responseStatusCode == 404) {
457
        self.pithosObjectStoreURLString = [[[NSURL URLWithString:authURLString] URLByAppendingPathComponent:@"v1"] description];
458
        self.astakosAccountURLString = [self.authURLString copy];
459
        self.astakosWebloginURLString = [self.authURLString copy];
460
        self.manual = YES;
461
    }
462
}
463

  
464
- (ASIPithosRequest *)updateUserCatalogForDisplaynames:(NSArray *)displaynames UUIDs:(NSArray *)UUIDs {
442 465
    ASIPithosRequest *userCatalogRequest = [ASIPithosRequest userCatalogRequestWithPithos:self.pithos
443 466
                                                                             displaynames:displaynames
444 467
                                                                                    UUIDs:UUIDs];
......
496 519
        self.syncSkipHidden = [decoder decodeBoolForKey:@"syncSkipHidden"];
497 520
        self.syncLastCompleted = [decoder decodeObjectForKey:@"syncLastCompleted"];
498 521
        
499
        self.serverURL = [decoder decodeObjectForKey:@"serverURL"];
500
        self.versionResource = [decoder decodeObjectForKey:@"versionResource"];
501
        self.loginResource = [decoder decodeObjectForKey:@"loginResource"];
502
        self.publicResource = [decoder decodeObjectForKey:@"publicResource"];
503
        self.userCatalogResource = [decoder decodeObjectForKey:@"userCatalogResource"];
522
        self.authURLString = [decoder decodeObjectForKey:@"authURLString"];
523
        self.pithosObjectStoreURLString = [decoder decodeObjectForKey:@"pithosObjectStoreURLString"];
524
        self.astakosAccountURLString = [decoder decodeObjectForKey:@"astakosAccountURLString"];
525
        self.astakosWebloginURLString = [decoder decodeObjectForKey:@"astakosWebloginURLString"];
526
        self.manual = [decoder decodeBoolForKey:@"manual"];
527
        
528
        // Support for older versions.
529
        if (!authURLString && !pithosObjectStoreURLString && !astakosAccountURLString && !astakosWebloginURLString) {
530
            NSString *tmpURLString = [decoder decodeObjectForKey:@"serverURL"];
531
            if (!tmpURLString ||
532
                [[[NSURL URLWithString:tmpURLString] URLByAppendingPathComponent:@""] isEqual:[[NSURL URLWithString:defaultManualURLString] URLByAppendingPathComponent:@""]]) {
533
                self.authURLString = defaultAuthURLString;
534
                self.manual = NO;
535
            } else {
536
                self.pithosObjectStoreURLString = [tmpURLString stringByAppendingString:@"/v1"];
537
                self.astakosAccountURLString = tmpURLString;
538
                self.astakosWebloginURLString = tmpURLString;
539
                self.manual = YES;
540
            }
541
        }
504 542
        
505
        self.authUser = [decoder decodeObjectForKey:@"authUser"];
506 543
        self.authToken = [decoder decodeObjectForKey:@"authToken"];
507
        self.storageURLPrefix = [decoder decodeObjectForKey:@"storageURLPrefix"];
508
        self.authURL = [decoder decodeObjectForKey:@"authURL"];
509
        self.publicURLPrefix = [decoder decodeObjectForKey:@"publicURLPrefix"];
510
        self.loginURLPrefix = [decoder decodeObjectForKey:@"loginURLPrefix"];
511
        self.userCatalogURL = [decoder decodeObjectForKey:@"userCatalogURL"];
544
        self.authUser = [decoder decodeObjectForKey:@"authUser"];
512 545
        self.userCatalog = [decoder decodeObjectForKey:@"userCatalog"];
513 546
        
514
        if (![authUser length] || ![authToken length] || ![self.storageURLPrefix length])
547
        if (![authUser length] || ![authToken length])
515 548
            self.active = NO;
516 549
        
517 550
        NSString *currentVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
......
537 570
    [encoder encodeBool:syncSkipHidden forKey:@"syncSkipHidden"];
538 571
    [encoder encodeObject:self.syncLastCompleted forKey:@"syncLastCompleted"];
539 572

  
540
    [encoder encodeObject:serverURL forKey:@"serverURL"];
541
    [encoder encodeObject:versionResource forKey:@"versionResource"];
542
    [encoder encodeObject:publicResource forKey:@"publicResource"];
543
    [encoder encodeObject:loginResource forKey:@"loginResource"];
544
    [encoder encodeObject:userCatalogResource forKey:@"userCatalogResource"];
573
    [encoder encodeObject:authURLString forKey:@"authURLString"];
574
    [encoder encodeObject:pithosObjectStoreURLString forKey:@"pithosObjectStoreURLString"];
575
    [encoder encodeObject:astakosAccountURLString forKey:@"astakosAccountURLString"];
576
    [encoder encodeObject:astakosWebloginURLString forKey:@"astakosWebloginURLString"];
577
    [encoder encodeBool:manual forKey:@"manual"];
545 578
    
546
    [encoder encodeObject:authUser forKey:@"authUser"];
547 579
    [encoder encodeObject:authToken forKey:@"authToken"];
548
    [encoder encodeObject:storageURLPrefix forKey:@"storageURLPrefix"];
549
    [encoder encodeObject:authURL forKey:@"authURL"];
550
    [encoder encodeObject:publicURLPrefix forKey:@"publicURLPrefix"];
551
    [encoder encodeObject:loginURLPrefix forKey:@"loginURLPrefix"];
552
    [encoder encodeObject:userCatalogURL forKey:@"userCatalogURL"];
580
    [encoder encodeObject:authUser forKey:@"authUser"];
553 581
    [encoder encodeObject:userCatalog forKey:@"userCatalog"];
554 582
}
555 583

  

Also available in: Unified diff