Merge remote-tracking branch 'upstream/master' into merge-upstream

Conflicts:
	SoObjects/SOGo/SOGoUserManager.m
	Tools/SOGoToolRestore.m
This commit is contained in:
Julio García
2015-06-26 09:41:58 +02:00
41 changed files with 761 additions and 273 deletions
@@ -3157,6 +3157,8 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
inContainer: self];
[object setIsNew: YES];
content = [NSMutableString stringWithString: @"BEGIN:VCALENDAR\n"];
[content appendFormat: @"PRODID:-//Inverse inc./SOGo %@//EN\n", SOGoVersion];
if (timezone)
[content appendFormat: @"%@\n", [timezone versitString]];
[content appendFormat: @"%@\nEND:VCALENDAR", [event versitString]];
+21 -2
View File
@@ -1820,9 +1820,28 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
[self warnWithFormat: @"Invalid event: no end date; setting duration to %@", [event duration]];
}
if ([event organizer] && ![[[event organizer] cn] length])
if ([event organizer])
{
[[event organizer] setCn: [[event organizer] rfc822Email]];
NSString *uid;
if (![[[event organizer] cn] length])
{
[[event organizer] setCn: [[event organizer] rfc822Email]];
}
// We now make sure that the organizer, if managed by SOGo, is using
// its default email when creating events and inviting attendees.
uid = [[SOGoUserManager sharedUserManager] getUIDForEmail: [[event organizer] rfc822Email]];
if (uid)
{
NSDictionary *defaultIdentity;
SOGoUser *organizer;
organizer = [SOGoUser userWithLogin: uid];
defaultIdentity = [organizer defaultIdentity];
[[event organizer] setCn: [defaultIdentity objectForKey: @"fullName"]];
[[event organizer] setEmail: [defaultIdentity objectForKey: @"email"]];
}
}
}
}
+3 -3
View File
@@ -1,6 +1,6 @@
/* iCalAlarm+SOGo.h - this file is part of SOGo
*
* Copyright (C) 2014 Inverse inc.
* Copyright (C) 2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,11 +20,11 @@
#import <NGCards/iCalAlarm.h>
@class iCalRepeatableEntityObject;
@class iCalEntityObject;
@interface iCalAlarm (SOGoExtensions)
+ (id) alarmForEvent: (iCalRepeatableEntityObject *) theEntity
+ (id) alarmForEvent: (iCalEntityObject *) theEntity
owner: (NSString *) theOwner
action: (NSString *) reminderAction
unit: (NSString *) reminderUnit
+3 -2
View File
@@ -1,6 +1,6 @@
/* iCalAlarm+SOGo.m - this file is part of SOGo
*
* Copyright (C) 2014 Inverse inc.
* Copyright (C) 2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -28,6 +28,7 @@
#import <NGCards/iCalPerson.h>
#import <NGCards/iCalTrigger.h>
#import <NGCards/iCalEntityObject.h>
@implementation iCalAlarm (SOGoExtensions)
@@ -65,7 +66,7 @@
[alarm addChild: aAttendee];
}
+ (id) alarmForEvent: (iCalRepeatableEntityObject *) theEntity
+ (id) alarmForEvent: (iCalEntityObject *) theEntity
owner: (NSString *) theOwner
action: (NSString *) reminderAction
unit: (NSString *) reminderUnit
-2
View File
@@ -777,8 +777,6 @@ static BOOL debugSoParts = NO;
filename = [NSString stringWithFormat: @"unknown_%@", path];
else if ([mimeType isEqualToString: @"message/rfc822"])
filename = [NSString stringWithFormat: @"email_%@.eml", path];
else if ([mimeType isEqualToString: @"text/calendar"])
filename = [NSString stringWithFormat: @"calendar_%@.ics", path];
if (filename)
+4 -1
View File
@@ -1,6 +1,6 @@
/* NSString+Utilities.h - this file is part of SOGo
*
* Copyright (C) 2006-2014 Inverse inc.
* Copyright (C) 2006-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -46,6 +46,9 @@
- (NSString *) asCSSIdentifier;
- (NSString *) fromCSSIdentifier;
/* JavaScript safety */
- (NSString *) asSafeJSString;
/* SQL safety */
- (NSString *) asSafeSQLString;
+17 -6
View File
@@ -1,6 +1,6 @@
/* NSString+Utilities.m - this file is part of SOGo
*
* Copyright (C) 2006-2014 Inverse inc.
* Copyright (C) 2006-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -257,7 +257,7 @@ static int cssEscapingCount;
return selfCopy;
}
- (NSString *) doubleQuotedString
- (NSString *) asSafeJSString
{
NSMutableString *representation;
@@ -270,7 +270,12 @@ static int cssEscapingCount;
[representation replaceString: @"\r" withString: @"\\r"];
[representation replaceString: @"\t" withString: @"\\t"];
return [NSString stringWithFormat: @"\"%@\"", representation];
return representation;
}
- (NSString *) doubleQuotedString
{
return [NSString stringWithFormat: @"\"%@\"", [self asSafeJSString]];
}
//
@@ -333,12 +338,18 @@ static int cssEscapingCount;
int count;
strings = [NSArray arrayWithObjects: @"_U_", @"_D_", @"_H_", @"_A_", @"_S_",
@"_C_", @"_CO_", @"_SP_", @"_SQ_", @"_AM_", @"_P_", @"_DS_", nil];
@"_C_", @"_SC_",
@"_CO_", @"_SP_", @"_SQ_", @"_DQ_",
@"_LP_", @"_RP_", @"_LS_", @"_RS_", @"_LC_", @"_RC_",
@"_AM_", @"_P_", @"_DS_", nil];
[strings retain];
cssEscapingStrings = [strings asPointersOfObjects];
characters = [NSArray arrayWithObjects: @"_", @".", @"#", @"@", @"*", @":",
@",", @" ", @"'", @"&", @"+", @"$", nil];
characters = [NSArray arrayWithObjects: @"_", @".", @"#", @"@", @"*",
@":", @";",
@",", @" ", @"'", @"\"",
@"(", @")", @"[", @"]", @"{", @"}",
@"&", @"+", @"$", nil];
cssEscapingCount = [strings count];
cssEscapingCharacters = NSZoneMalloc (NULL,
(cssEscapingCount + 1)
+5 -2
View File
@@ -70,7 +70,6 @@
SOGoMailAutoSave = "5";
SOGoCalendarDefaultCategoryColor = "#aaa";
SOGoCalendarShouldDisplayWeekend = YES;
SOGoCalendarEventsDefaultClassification = "PUBLIC";
SOGoCalendarTasksDefaultClassification = "PUBLIC";
@@ -87,6 +86,10 @@
$label4 = ("To Do", "#3333FF");
$label5 = ("Later", "#993399");
};
SOGoCalendarCategories = ("Customer", "Calls", "Favorites", "Meeting", "Ideas", "Miscellaneous", "Birthday", "Anniversary", "Vacation", "Travel", "Projects", "Suppliers", "Gifts", "Clients", "Issues", "Business", "Holidays", "Personal", "Status", "Competition", "Follow up", "Public Holiday");
SOGoCalendarCategoriesColors = { "Customer" = "#F0F0F0"; "Calls" = "#F0F0F0"; "Favorites" = "#F0F0F0"; "Meeting" = "#F0F0F0"; "Ideas" = "#F0F0F0"; "Miscellaneous" = "#F0F0F0"; "Birthday" = "#F0F0F0"; "Anniversary" = "#F0F0F0"; "Vacation" = "#F0F0F0"; "Travel" = "#F0F0F0"; "Projects" = "#F0F0F0"; "Suppliers" = "#F0F0F0"; "Gifts" = "#F0F0F0"; "Clients" = "#F0F0F0"; "Issues" = "#F0F0F0"; "Business" = "#F0F0F0"; "Holidays" = "#F0F0F0"; "Personal" = "#F0F0F0"; "Status" = "#F0F0F0"; "Competition" = "#F0F0F0"; "Follow up" = "#F0F0F0"; "Public Holiday" = "#F0F0F0"; };
SOGoSubscriptionFolderFormat = "%{FolderName} (%{UserName} <%{Email}>)";
}
-2
View File
@@ -68,8 +68,6 @@
- (NSArray *) refreshViewIntervals;
- (NSString *) subscriptionFolderFormat;
- (NSString *) calendarDefaultCategoryColor;
- (NSArray *) freeBusyDefaultInterval;
- (int) davCalendarStartTimeLimit;
-5
View File
@@ -294,11 +294,6 @@
return [self stringForKey: @"SOGoLDAPContactInfoAttribute"];
}
- (NSString *) calendarDefaultCategoryColor
{
return [self stringForKey: @"SOGoCalendarDefaultCategoryColor"];
}
- (NSArray *) freeBusyDefaultInterval
{
return [self arrayForKey: @"SOGoFreeBusyDefaultInterval"];
+12 -1
View File
@@ -202,7 +202,18 @@
// the real login most likely is the email address.
if (r.location != NSNotFound && ![sd enableDomainBasedUID])
uid = [realUID substringToIndex: r.location-1];
else
// If we don't have the domain in the UID but SOGoEnableDomainBasedUID is
// enabled, let's add it internally so so it becomes unique across
// all potential domains.
else if (r.location == NSNotFound && [sd enableDomainBasedUID])
{
uid = [NSString stringWithString: realUID];
realUID = [NSString stringWithFormat: @"%@@%@", realUID, domain];
}
// We found the domain and SOGoEnableDomainBasedUID is enabled,
// we keep realUID.. This would happen for example if the user
// authenticates with foo@bar.com and the UIDFieldName is also foo@bar.com
else if ([sd enableDomainBasedUID])
uid = [NSString stringWithString: realUID];
}
}
+1 -1
View File
@@ -702,7 +702,7 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
- (NSDictionary *) calendarCategoriesColors
{
return [self dictionaryForKey: @"SOGoCalendarCategoriesColors"];
return [self objectForKey: @"SOGoCalendarCategoriesColors"];
}
- (void) setCalendarShouldDisplayWeekend: (BOOL) newValue
+105 -42
View File
@@ -455,6 +455,7 @@ static Class NSNullK;
while (!checkOK && (currentID = [authIDs nextObject]))
{
sogoSource = [_sources objectForKey: currentID];
checkOK = [sogoSource checkLogin: login
password: password
perr: perr
@@ -501,10 +502,10 @@ static Class NSNullK;
NSMutableDictionary *currentUser;
NSDictionary *failedCount;
NSString *dictPassword, *username, *jsonUser;
SOGoSystemDefaults *dd;
SOGoSystemDefaults *sd;
BOOL checkOK;
dd = [SOGoSystemDefaults sharedSystemDefaults];
sd = [SOGoSystemDefaults sharedSystemDefaults];
username = _login;
@@ -543,10 +544,10 @@ static Class NSNullK;
start_time = [[failedCount objectForKey: @"InitialDate"] unsignedIntValue];
delta = current_time - start_time;
block_time = [dd failedLoginBlockInterval];
block_time = [sd failedLoginBlockInterval];
if ([[failedCount objectForKey: @"FailedCount"] intValue] >= [dd maximumFailedLoginCount] &&
delta >= [dd maximumFailedLoginInterval] &&
if ([[failedCount objectForKey: @"FailedCount"] intValue] >= [sd maximumFailedLoginCount] &&
delta >= [sd maximumFailedLoginInterval] &&
delta <= block_time )
{
*_perr = PolicyAccountLocked;
@@ -565,6 +566,28 @@ static Class NSNullK;
// authentication source and try to validate there, then cache it.
jsonUser = [[SOGoCache sharedCache] userAttributesForLogin: username];
currentUser = [jsonUser objectFromJSONString];
//
// If we are using multidomain and the UIDFieldName is not part of the email address
// we must bind without the domain part since internally, SOGo will use
// UIDFieldName @ domain as its unique identifier if the UIDFieldName is used to
// authenticate. This can happen for example of one has in LDAP:
//
// dn: uid=foo,dc=example,dc=com
// uid: foo
// mail: broccoli@example.com
//
// and authenticates with "foo", using bindFields = (uid, mail) and SOGoEnableDomainBasedUID = YES;
// Otherwise, -_sourceCheckLogin:... would have failed because SOGo would try to bind using: foo@example.com
//
if ([[currentUser objectForKey: @"DomainLessLogin"] boolValue])
{
NSRange r;
r = [_login rangeOfString: [NSString stringWithFormat: @"@%@", *_domain]];
_login = [_login substringToIndex: r.location];
}
dictPassword = [currentUser objectForKey: @"password"];
if (useCache && currentUser && dictPassword)
{
@@ -584,6 +607,18 @@ static Class NSNullK;
currentUser = [NSMutableDictionary dictionary];
}
// Before caching user attributes, we must check if SOGoEnableDomainBasedUID is enabled
// but we don't have a domain. That would happen for example if the user authenticates
// without the domain part. We must also cache that information, since SOGo will try
// afterward to bind with UIDFieldName@domain, and it could potentially not exist
// in the authentication source. See the rationale in _sourceCheckLogin: ...
if ([sd enableDomainBasedUID] &&
[username rangeOfString: @"@"].location == NSNotFound)
{
username = [NSString stringWithFormat: @"%@@%@", username, *_domain];
[currentUser setObject: [NSNumber numberWithBool: YES] forKey: @"DomainLessLogin"];
}
// It's important to cache the password here as we might have cached the
// user's entry in -contactInfosForUserWithUIDorEmail: and if we don't
// set the password and recache the entry, the password would never be
@@ -597,7 +632,7 @@ static Class NSNullK;
else
{
// If failed login "rate-limiting" is enabled, we adjust the stats
if ([dd maximumFailedLoginCount])
if ([sd maximumFailedLoginCount])
{
[[SOGoCache sharedCache] setFailedCount: ([[failedCount objectForKey: @"FailedCount"] intValue] + 1)
forLogin: username];
@@ -705,9 +740,9 @@ static Class NSNullK;
//
//
//
- (void) _fillContactInfosForUser: (NSMutableDictionary *) currentUser
withUIDorEmail: (NSString *) uid
inDomain: (NSString *) domain
- (void) _fillContactInfosForUser: (NSMutableDictionary *) theCurrentUser
withUIDorEmail: (NSString *) theUID
inDomain: (NSString *) theDomain
{
NSString *sourceID, *cn, *c_domain, *c_uid, *c_imaphostname, *c_imaplogin, *c_sievehostname;
NSObject <SOGoSource> *currentSource;
@@ -726,21 +761,31 @@ static Class NSNullK;
c_imaplogin = nil;
c_sievehostname = nil;
[currentUser setObject: [NSNumber numberWithBool: YES]
forKey: @"CalendarAccess"];
[currentUser setObject: [NSNumber numberWithBool: YES]
forKey: @"MailAccess"];
[theCurrentUser setObject: [NSNumber numberWithBool: YES]
forKey: @"CalendarAccess"];
[theCurrentUser setObject: [NSNumber numberWithBool: YES]
forKey: @"MailAccess"];
sogoSources = [[self authenticationSourceIDsInDomain: domain] objectEnumerator];
if ([[theCurrentUser objectForKey: @"DomainLessLogin"] boolValue])
{
NSRange r;
r = [theUID rangeOfString: [NSString stringWithFormat: @"@%@", theDomain]];
theUID = [theUID substringToIndex: r.location];
}
sogoSources = [[self authenticationSourceIDsInDomain: theDomain] objectEnumerator];
userEntry = nil;
while (!userEntry && (sourceID = [sogoSources nextObject]))
{
currentSource = [_sources objectForKey: sourceID];
userEntry = [currentSource lookupContactEntryWithUIDorEmail: uid
inDomain: domain];
userEntry = [currentSource lookupContactEntryWithUIDorEmail: theUID
inDomain: theDomain];
if (userEntry)
{
[currentUser setObject: sourceID forKey: @"SOGoSource"];
[theCurrentUser setObject: sourceID forKey: @"SOGoSource"];
if (!cn)
cn = [userEntry objectForKey: @"c_cn"];
if (!c_uid)
@@ -758,30 +803,30 @@ static Class NSNullK;
c_sievehostname = [userEntry objectForKey: @"c_sievehostname"];
access = [[userEntry objectForKey: @"CalendarAccess"] boolValue];
if (!access)
[currentUser setObject: [NSNumber numberWithBool: NO]
forKey: @"CalendarAccess"];
[theCurrentUser setObject: [NSNumber numberWithBool: NO]
forKey: @"CalendarAccess"];
access = [[userEntry objectForKey: @"MailAccess"] boolValue];
if (!access)
[currentUser setObject: [NSNumber numberWithBool: NO]
forKey: @"MailAccess"];
[theCurrentUser setObject: [NSNumber numberWithBool: NO]
forKey: @"MailAccess"];
// We check if it's a group
isGroup = [userEntry objectForKey: @"isGroup"];
if (isGroup)
[currentUser setObject: isGroup forKey: @"isGroup"];
[theCurrentUser setObject: isGroup forKey: @"isGroup"];
// We also fill the resource attributes, if any
if ([userEntry objectForKey: @"isResource"])
[currentUser setObject: [userEntry objectForKey: @"isResource"]
forKey: @"isResource"];
[theCurrentUser setObject: [userEntry objectForKey: @"isResource"]
forKey: @"isResource"];
if ([userEntry objectForKey: @"numberOfSimultaneousBookings"])
[currentUser setObject: [userEntry objectForKey: @"numberOfSimultaneousBookings"]
forKey: @"numberOfSimultaneousBookings"];
[theCurrentUser setObject: [userEntry objectForKey: @"numberOfSimultaneousBookings"]
forKey: @"numberOfSimultaneousBookings"];
// This is Active Directory specific attribute (needed on OpenChange/* layer)
if ([userEntry objectForKey: @"samaccountname"])
[currentUser setObject: [userEntry objectForKey: @"samaccountname"]
forKey: @"sAMAccountName"];
[theCurrentUser setObject: [userEntry objectForKey: @"samaccountname"]
forKey: @"sAMAccountName"];
}
}
@@ -793,20 +838,20 @@ static Class NSNullK;
c_domain = @"";
if (c_imaphostname)
[currentUser setObject: c_imaphostname forKey: @"c_imaphostname"];
[theCurrentUser setObject: c_imaphostname forKey: @"c_imaphostname"];
if (c_imaplogin)
[currentUser setObject: c_imaplogin forKey: @"c_imaplogin"];
[theCurrentUser setObject: c_imaplogin forKey: @"c_imaplogin"];
if (c_sievehostname)
[currentUser setObject: c_sievehostname forKey: @"c_sievehostname"];
[theCurrentUser setObject: c_sievehostname forKey: @"c_sievehostname"];
[currentUser setObject: emails forKey: @"emails"];
[currentUser setObject: cn forKey: @"cn"];
[currentUser setObject: c_uid forKey: @"c_uid"];
[currentUser setObject: c_domain forKey: @"c_domain"];
[theCurrentUser setObject: emails forKey: @"emails"];
[theCurrentUser setObject: cn forKey: @"cn"];
[theCurrentUser setObject: c_uid forKey: @"c_uid"];
[theCurrentUser setObject: c_domain forKey: @"c_domain"];
// If our LDAP queries gave us nothing, we add at least one default
// email address based on the default domain.
[self _fillContactMailRecords: currentUser];
[self _fillContactMailRecords: theCurrentUser];
}
//
@@ -900,8 +945,9 @@ static Class NSNullK;
- (NSDictionary *) contactInfosForUserWithUIDorEmail: (NSString *) uid
inDomain: (NSString *) domain
{
NSMutableDictionary *currentUser;
NSString *aUID, *cacheUid, *jsonUser;
NSMutableDictionary *currentUser;
BOOL newUser;
if ([uid isEqualToString: @"anonymous"])
@@ -914,8 +960,10 @@ static Class NSNullK;
cacheUid = [NSString stringWithFormat: @"%@@%@", aUID, domain];
else
cacheUid = aUID;
jsonUser = [[SOGoCache sharedCache] userAttributesForLogin: cacheUid];
currentUser = [jsonUser objectFromJSONString];
if ([currentUser isKindOfClass: NSNullK])
currentUser = nil;
else if (!([currentUser objectForKey: @"emails"]
@@ -925,8 +973,10 @@ static Class NSNullK;
// that we have an occurence with only a cached password. In the
// latter case, we update the entry with the remaining information
// and recache the value.
if (!currentUser || ([currentUser count] == 1 && [currentUser objectForKey: @"password"]))
{
if (!currentUser ||
([currentUser count] == 1 && [currentUser objectForKey: @"password"]) ||
([currentUser count] == 2 && [currentUser objectForKey: @"password"] && [currentUser objectForKey: @"DomainLessLogin"]))
{
newUser = YES;
if (!currentUser)
@@ -946,9 +996,22 @@ static Class NSNullK;
currentUser = nil;
}
else
[self _retainUser: currentUser
withLogin: cacheUid];
}
{
SOGoSystemDefaults *sd;
sd = [SOGoSystemDefaults sharedSystemDefaults];
// SOGoEnableDomainBasedUID is set to YES but we don't have a domain part. This happens in
// multi-domain environments authenticating only with the UIDFieldName
if ([sd enableDomainBasedUID] && !domain)
{
cacheUid = [NSString stringWithFormat: @"%@@%@", cacheUid, [currentUser objectForKey: @"c_domain"]];
[currentUser setObject: [NSNumber numberWithBool: YES] forKey: @"DomainLessLogin"];
}
[self _retainUser: currentUser withLogin: cacheUid];
}
}
}
}
else