mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-10-14 08:07:11 +00:00
feat(carddav): fix issue where only personal contacts can be synced on macOS (#1981). When , append the addressbook name as a username suffix to force macOS to treat each book as a distinct account. Usernames must not contain . Install via the provisioning profile. Closes #6033.
This commit is contained in:
1 parent
d16a1a9708
commit
761934af42
13 files changed
+136
-9
No files matched your search
@@ -49,7 +49,7 @@
|
||||
- (BOOL) checkLogin: (NSString *) _login
|
||||
password: (NSString *) _pwd
|
||||
{
|
||||
NSString *domain;
|
||||
NSString *domain, *checkedLogin;
|
||||
SOGoSystemDefaults *sd;
|
||||
SOGoCASSession *session;
|
||||
SOGoPasswordPolicyError perr;
|
||||
@@ -58,9 +58,21 @@
|
||||
|
||||
domain = nil;
|
||||
perr = PolicyNoError;
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
checkedLogin = [_login stringByReplacingString: @"%40" withString: @"@"];
|
||||
|
||||
/* Per-addressbook CardDAV profile: a login of the form <user>!<book> is an
|
||||
alias used to discriminate the URL path; the password check applies to
|
||||
the underlying real user. */
|
||||
if ([sd carddavSingleAddressBookProfile])
|
||||
{
|
||||
NSRange aliasRange = [checkedLogin rangeOfString: @"!"];
|
||||
if (aliasRange.location != NSNotFound)
|
||||
checkedLogin = [checkedLogin substringToIndex: aliasRange.location];
|
||||
}
|
||||
|
||||
rc = ([[SOGoUserManager sharedUserManager]
|
||||
checkLogin: [_login stringByReplacingString: @"%40"
|
||||
withString: @"@"]
|
||||
checkLogin: checkedLogin
|
||||
password: _pwd
|
||||
domain: &domain
|
||||
perr: &perr
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "SOGoMobileProvision.h"
|
||||
#import "SOGoSystemDefaults.h"
|
||||
#import "SOGoUser.h"
|
||||
#import "NSString+Crypto.h"
|
||||
|
||||
@@ -34,11 +35,12 @@
|
||||
NSURL *serverURL;
|
||||
NSDictionary *provisioning;
|
||||
NSError *error;
|
||||
NSString *payloadType, *prefix, *type;
|
||||
NSString *payloadType, *prefix, *type, *username;
|
||||
NSNumber *port;
|
||||
|
||||
activeUser = [context activeUser];
|
||||
serverURL = [context serverURL];
|
||||
username = [activeUser login];
|
||||
|
||||
if (!context || !path) {
|
||||
[self errorWithFormat: @"Invalid provisioning profile - no parameters"];
|
||||
@@ -55,6 +57,32 @@
|
||||
payloadType = @"com.apple.carddav.account";
|
||||
prefix = @"CardDAV";
|
||||
type = @"Contact";
|
||||
|
||||
/* Per-addressbook CardDAV profile: rewrite the principal URL and the
|
||||
CardDAV username from <user> to <user>!<book>. macOS Contacts
|
||||
strips path-only discriminators when it falls back on its own
|
||||
discovery; embedding the alias in the username keeps the !book
|
||||
tag in every subsequent request, both in the URL and in the Basic
|
||||
Auth header. */
|
||||
if ([[SOGoSystemDefaults sharedSystemDefaults] carddavSingleAddressBookProfile])
|
||||
{
|
||||
NSArray *parts;
|
||||
parts = [path componentsSeparatedByString: @"/"];
|
||||
if ([parts count] >= 7
|
||||
&& [[parts objectAtIndex: 4] isEqualToString: @"Contacts"])
|
||||
{
|
||||
NSString *aliasLogin;
|
||||
NSMutableArray *rewritten;
|
||||
aliasLogin = [NSString stringWithFormat: @"%@!%@",
|
||||
[parts objectAtIndex: 3],
|
||||
[parts objectAtIndex: 5]];
|
||||
rewritten = [parts mutableCopy];
|
||||
[rewritten replaceObjectAtIndex: 3 withObject: aliasLogin];
|
||||
path = [rewritten componentsJoinedByString: @"/"];
|
||||
[rewritten release];
|
||||
username = aliasLogin;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -74,7 +102,7 @@
|
||||
port, [NSString stringWithFormat:@"%@%@", prefix, @"Port"],
|
||||
path, [NSString stringWithFormat:@"%@%@", prefix, @"PrincipalURL"],
|
||||
[NSNumber numberWithBool:[[serverURL scheme] isEqualToString:@"https"]], [NSString stringWithFormat:@"%@%@", prefix, @"UseSSL"],
|
||||
[activeUser login], [NSString stringWithFormat:@"%@%@", prefix, @"Username"],
|
||||
username, [NSString stringWithFormat:@"%@%@", prefix, @"Username"],
|
||||
[NSString stringWithFormat: @"SOGo %@ provisioning", prefix], @"PayloadDescription",
|
||||
[NSString stringWithFormat:@"%@ %@", type, name], @"PayloadDisplayName",
|
||||
[NSString stringWithFormat:@"%@.%@.apple.%@", [serverURL host], [name asMD5String], [prefix lowercaseString]], @"PayloadIdentifier",
|
||||
|
||||
@@ -1257,7 +1257,10 @@
|
||||
davCurrentUserPrincipal = nil;
|
||||
else
|
||||
{
|
||||
s = [NSString stringWithFormat: @"/SOGo/dav/%@", login];
|
||||
/* loginAlias preserves the <user>!<book> alias used by the
|
||||
per-addressbook CardDAV profile, so that macOS Contacts keeps
|
||||
using the alias URL instead of falling back on the canonical one. */
|
||||
s = [NSString stringWithFormat: @"/SOGo/dav/%@", [activeUser loginAlias]];
|
||||
userHREF = davElementWithContent (@"href", XMLNS_WEBDAV, s);
|
||||
davCurrentUserPrincipal
|
||||
= [davElementWithContent (@"current-user-principal",
|
||||
|
||||
@@ -62,6 +62,7 @@ static const NSString *kDisableSharingCalendar = @"Calendar";
|
||||
- (BOOL) isCalendarDAVAccessEnabled;
|
||||
- (BOOL) isCalendarJitsiLinkEnabled;
|
||||
- (BOOL) isAddressBookDAVAccessEnabled;
|
||||
- (BOOL) carddavSingleAddressBookProfile;
|
||||
|
||||
- (BOOL) enableEMailAlarms;
|
||||
|
||||
|
||||
@@ -588,6 +588,11 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict,
|
||||
return [self boolForKey: @"SOGoAddressBookDAVAccessEnabled"];
|
||||
}
|
||||
|
||||
- (BOOL) carddavSingleAddressBookProfile
|
||||
{
|
||||
return [self boolForKey: @"SOGoCarddavSingleAddressBookProfile"];
|
||||
}
|
||||
|
||||
- (BOOL) enableEMailAlarms
|
||||
{
|
||||
return [self boolForKey: @"SOGoEnableEMailAlarms"];
|
||||
|
||||
@@ -63,6 +63,10 @@
|
||||
SOGoUserFolder *homeFolder;
|
||||
NSString *currentPassword;
|
||||
NSString *loginInDomain;
|
||||
/* The login as it appeared at lookup time, before any alias stripping
|
||||
(per-addressbook CardDAV profile uses <user>!<book> aliases). Equal
|
||||
to [self login] for regular users. */
|
||||
NSString *loginAlias;
|
||||
//NSString *language;
|
||||
NSArray *allEmails;
|
||||
NSMutableArray *mailAccounts;
|
||||
@@ -88,6 +92,7 @@
|
||||
- (NSString *) currentPassword;
|
||||
|
||||
- (NSString *) loginInDomain;
|
||||
- (NSString *) loginAlias;
|
||||
|
||||
/* properties */
|
||||
- (NSString *) domain;
|
||||
|
||||
@@ -144,11 +144,26 @@
|
||||
realUID = nil;
|
||||
domain = nil;
|
||||
|
||||
ASSIGN (loginAlias, newLogin);
|
||||
|
||||
if ([newLogin isEqualToString: @"anonymous"] || [newLogin isEqualToString: @"freebusy"])
|
||||
realUID = newLogin;
|
||||
else
|
||||
{
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
|
||||
/* Per-addressbook CardDAV profile: strip the alias suffix so that a login
|
||||
of the form <user>!<book> resolves to the real user <user>. The book
|
||||
part is consumed elsewhere (Main/SOGo.m, SOGoContactFolders) to filter
|
||||
the addressbook listing. The original alias is kept in loginAlias so
|
||||
that DAV property hrefs (current-user-principal, etc.) preserve it. */
|
||||
if ([sd carddavSingleAddressBookProfile])
|
||||
{
|
||||
NSRange aliasRange = [newLogin rangeOfString: @"!"];
|
||||
if (aliasRange.location != NSNotFound)
|
||||
newLogin = [newLogin substringToIndex: aliasRange.location];
|
||||
}
|
||||
|
||||
if ([sd enableDomainBasedUID] || [[sd loginDomains] count] > 0)
|
||||
{
|
||||
r = [newLogin rangeOfString: @"@" options: NSBackwardsSearch];
|
||||
@@ -243,9 +258,15 @@
|
||||
[currentPassword release];
|
||||
[cn release];
|
||||
[loginInDomain release];
|
||||
[loginAlias release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSString *) loginAlias
|
||||
{
|
||||
return [loginAlias length] ? loginAlias : self->login;
|
||||
}
|
||||
|
||||
- (void) setPrimaryRoles: (NSArray *) newRoles
|
||||
{
|
||||
ASSIGN (roles, newRoles);
|
||||
|
||||
@@ -44,6 +44,8 @@
|
||||
@class SOGoContactFolders;
|
||||
|
||||
@interface SOGoUserFolder : SOGoFolder
|
||||
{
|
||||
}
|
||||
|
||||
/* ownership */
|
||||
|
||||
|
||||
@@ -102,11 +102,14 @@
|
||||
ownerUser = [SOGoUser userWithLogin: nameInContainer roles: nil];
|
||||
login = [ownerUser login];
|
||||
[self setOwner: login];
|
||||
if (![nameInContainer isEqualToString: login])
|
||||
if (![nameInContainer isEqualToString: login]
|
||||
&& [nameInContainer rangeOfString: @"!"].location == NSNotFound)
|
||||
// In case the user domain is specified in the URL but not in the user
|
||||
// login name, we remove it (user@domain => user).
|
||||
// This happens when SOGoLoginDomains is defined but
|
||||
// SOGoEnableDomainBasedUID is disabled.
|
||||
// The !book alias used by the per-addressbook CardDAV profile must be
|
||||
// preserved here so generated hrefs keep the discriminator.
|
||||
ASSIGN(nameInContainer, login);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user