diff --git a/ActiveSync/NSData+ActiveSync.m b/ActiveSync/NSData+ActiveSync.m index cf059e66f..dd925c715 100644 --- a/ActiveSync/NSData+ActiveSync.m +++ b/ActiveSync/NSData+ActiveSync.m @@ -66,33 +66,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // - (NSString *) activeSyncRepresentationInContext: (WOContext *) context { - NSString *tmp, *s; - unichar *buf, *start, c; - int len, i, j; - - tmp = [self stringByEncodingBase64] ; - - len = [tmp length]; - - start = buf = (unichar *)malloc(len*sizeof(unichar)); - [tmp getCharacters: buf range: NSMakeRange(0, len)]; - - for (i = 0, j = 0; i < len; i++) - { - c = *buf; - - if (!(c == 0xA)) - { - *(start+j) = c; - j++; - } - - buf++; - } - - s = [[NSString alloc] initWithCharactersNoCopy: start length: j freeWhenDone: YES]; - - return AUTORELEASE(s); + return [[self stringByEncodingBase64] stringByReplacingString: @"\n" withString: @""]; } - (NSData *) wbxml2xml diff --git a/ActiveSync/NSString+ActiveSync.m b/ActiveSync/NSString+ActiveSync.m index 1564b7482..a66f38252 100644 --- a/ActiveSync/NSString+ActiveSync.m +++ b/ActiveSync/NSString+ActiveSync.m @@ -46,99 +46,6 @@ static NSArray *easCommandParameters = nil; @implementation NSString (ActiveSync) -// -// This is a copy from NSString+XMLEscaping.m from SOPE. -// The difference here is that we use wchar_t instead of unichar. -// This is needed to get the rigth numeric character reference. -// e.g. SMILING FACE WITH OPEN MOUTH -// ok: wchar_t -> 😃 wrong: unichar -> � � -// -// We avoir naming it like the one in SOPE since if the ActiveSync -// bundle is loaded, it'll overwrite the one provided by SOPE. -// -- (NSString *) _stringByEscapingXMLStringUsingCharacters { - register unsigned i, len, j; - register wchar_t *buf; - const wchar_t *chars; - unsigned escapeCount; - - if ([self length] == 0) return @""; - - NSData *data = [self dataUsingEncoding:NSUTF32StringEncoding]; - chars = [data bytes]; - len = [data length]/4; - - /* check for characters to escape ... */ - for (i = 0, escapeCount = 0; i < len; i++) { - switch (chars[i]) { - case '&': case '"': case '<': case '>': case '\r': - escapeCount++; - break; - default: - if (chars[i] > 127) - escapeCount++; - break; - } - } - if (escapeCount == 0 ) { - /* nothing to escape ... */ - return [[self copy] autorelease]; - } - - buf = calloc((len + 5) + (escapeCount * 16), sizeof(wchar_t)); - for (i = 0, j = 0; i < len; i++) { - switch (chars[i]) { - /* escape special chars */ - case '\r': - buf[j] = '&'; j++; buf[j] = '#'; j++; buf[j] = '1'; j++; - buf[j] = '3'; j++; buf[j] = ';'; j++; - break; - case '&': - buf[j] = '&'; j++; buf[j] = 'a'; j++; buf[j] = 'm'; j++; - buf[j] = 'p'; j++; buf[j] = ';'; j++; - break; - case '"': - buf[j] = '&'; j++; buf[j] = 'q'; j++; buf[j] = 'u'; j++; - buf[j] = 'o'; j++; buf[j] = 't'; j++; buf[j] = ';'; j++; - break; - case '<': - buf[j] = '&'; j++; buf[j] = 'l'; j++; buf[j] = 't'; j++; - buf[j] = ';'; j++; - break; - case '>': - buf[j] = '&'; j++; buf[j] = 'g'; j++; buf[j] = 't'; j++; - buf[j] = ';'; j++; - break; - - default: - /* escape big chars */ - if (chars[i] > 127) { - unsigned char nbuf[32]; - unsigned int k; - - sprintf((char *)nbuf, "&#%i;", (int)chars[i]); - for (k = 0; nbuf[k] != '\0'; k++) { - buf[j] = nbuf[k]; - j++; - } - } - else if (chars[i] == 0x9 || chars[i] == 0xA || chars[i] == 0xD || chars[i] >= 0x20) { // ignore any unsupported control character - /* nothing to escape */ - buf[j] = chars[i]; - j++; - } - break; - } - } - - self = [[NSString alloc] initWithBytesNoCopy: buf - length: (j*sizeof(wchar_t)) - encoding: NSUTF32StringEncoding - freeWhenDone: YES]; - - return [self autorelease]; -} - - (NSString *) sanitizedServerIdWithType: (SOGoMicrosoftActiveSyncFolderType) folderType { if (folderType == ActiveSyncEventFolder) @@ -158,7 +65,11 @@ static NSArray *easCommandParameters = nil; - (NSString *) activeSyncRepresentationInContext: (WOContext *) context { - return [self _stringByEscapingXMLStringUsingCharacters]; + NSString *s; + + s = [self safeString]; + + return [s stringByEscapingHTMLString]; } - (int) activeSyncFolderType diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m index 47ec81990..e0561ff31 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m +++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m @@ -453,27 +453,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType inBuffer: (NSMutableString *) theBuffer { - - id aDelete, sogoObject, value; NSArray *deletions; NSString *serverId; - BOOL deletesAsMoves, useTrash; + id aDelete, sogoObject; int i; deletions = (id)[theDocumentElement getElementsByTagName: @"Delete"]; if ([deletions count]) { - // From the documention, if DeletesAsMoves is missing, we must assume it's a YES. - // See https://msdn.microsoft.com/en-us/library/gg675480(v=exchg.80).aspx for all details. - value = [theDocumentElement getElementsByTagName: @"DeletesAsMoves"]; - deletesAsMoves = YES; - useTrash = YES; - - if ([value count] && [[[value lastObject] textValue] length]) - deletesAsMoves = [[[value lastObject] textValue] boolValue]; - for (i = 0; i < [deletions count]; i++) { aDelete = [deletions objectAtIndex: i]; @@ -485,13 +474,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. acquire: NO]; if (![sogoObject isKindOfClass: [NSException class]]) - { - // FIXME: handle errors here - if (deletesAsMoves) - [(SOGoMailFolder *)[sogoObject container] deleteUIDs: [NSArray arrayWithObjects: serverId, nil] useTrashFolder: &useTrash inContext: context]; - else - [sogoObject delete]; - } + [sogoObject delete]; [theBuffer appendString: @""]; [theBuffer appendFormat: @"%@", serverId]; diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index b107b94f0..c2f64786e 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -453,7 +453,7 @@ static BOOL debugOn = NO; [s appendString: @""]; [s appendFormat: @"%d", 1]; [s appendFormat: @"%@", syncKey]; - [s appendFormat: @"%@", [nameInContainer stringByEscapingURL]]; + [s appendFormat: @"%@", nameInContainer]; [s appendString: @""]; d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; @@ -1084,8 +1084,6 @@ static BOOL debugOn = NO; SOGoMailAccounts *accountsFolder; SOGoUserFolder *userFolder; SOGoMailObject *mailObject; - NSArray *partKeys; - int p; NSRange r1, r2; @@ -1105,14 +1103,7 @@ static BOOL debugOn = NO; acquire: NO]; mailObject = [currentCollection lookupName: messageName inContext: context acquire: NO]; - - partKeys = [pathToPart componentsSeparatedByString: @"."]; - - currentBodyPart = [mailObject lookupImap4BodyPartKey: [partKeys objectAtIndex:0] inContext: context]; - for (p = 1; p < [partKeys count]; p++) - { - currentBodyPart = [currentBodyPart lookupImap4BodyPartKey: [partKeys objectAtIndex:p] inContext: context]; - } + currentBodyPart = [mailObject lookupImap4BodyPartKey: pathToPart inContext: context]; [theResponse setHeader: [NSString stringWithFormat: @"%@/%@", [[currentBodyPart partInfo] objectForKey: @"type"], [[currentBodyPart partInfo] objectForKey: @"subtype"]] forKey: @"Content-Type"]; @@ -1167,11 +1158,11 @@ static BOOL debugOn = NO; { collectionId = [[(id)[[allCollections objectAtIndex: j] getElementsByTagName: @"CollectionId"] lastObject] textValue]; realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType]; - + if (folderType == ActiveSyncMailFolder) - nameInCache = [NSString stringWithFormat: @"folder%@", realCollectionId]; + nameInCache = [NSString stringWithFormat: @"folder%@", realCollectionId]; else - nameInCache = collectionId; + nameInCache = collectionId; realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType]; @@ -1243,11 +1234,10 @@ static BOOL debugOn = NO; - (void) processItemOperations: (id ) theDocumentElement inResponse: (WOResponse *) theResponse { - NSString *fileReference, *realCollectionId, *serverId, *bodyPreferenceType, *collectionId; + NSString *fileReference, *realCollectionId; NSMutableString *s; NSArray *fetchRequests; id aFetch; - NSData *d; int i; SOGoMicrosoftActiveSyncFolderType folderType; @@ -1257,6 +1247,8 @@ static BOOL debugOn = NO; [s appendString: @""]; [s appendString: @""]; [s appendString: @""]; + [s appendString: @"1"]; + [s appendString: @""]; fetchRequests = (id)[theDocumentElement getElementsByTagName: @"Fetch"]; @@ -1264,25 +1256,17 @@ static BOOL debugOn = NO; { NSMutableData *bytes, *parts; NSMutableArray *partLength; + NSData *d; bytes = [NSMutableData data]; parts = [NSMutableData data]; partLength = [NSMutableArray array]; - [s appendString: @"1"]; - [s appendString: @""]; - for (i = 0; i < [fetchRequests count]; i++) { aFetch = [fetchRequests objectAtIndex: i]; fileReference = [[[(id)[aFetch getElementsByTagName: @"FileReference"] lastObject] textValue] stringByUnescapingURL]; - collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue]; - - // its either a itemOperation to fetch an attachment or an email - if ([fileReference length]) - realCollectionId = [fileReference realCollectionIdWithFolderType: &folderType]; - else - realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType]; + realCollectionId = [fileReference realCollectionIdWithFolderType: &folderType]; if (folderType == ActiveSyncMailFolder) { @@ -1292,80 +1276,43 @@ static BOOL debugOn = NO; SOGoUserFolder *userFolder; SOGoMailObject *mailObject; - if ([fileReference length]) - { - // fetch attachment - NSRange r1, r2; - NSArray *partKeys; - int p; + NSRange r1, r2; - r1 = [realCollectionId rangeOfString: @"/"]; - r2 = [realCollectionId rangeOfString: @"/" options: 0 range: NSMakeRange(NSMaxRange(r1)+1, [realCollectionId length]-NSMaxRange(r1)-1)]; + r1 = [realCollectionId rangeOfString: @"/"]; + r2 = [realCollectionId rangeOfString: @"/" options: 0 range: NSMakeRange(NSMaxRange(r1)+1, [realCollectionId length]-NSMaxRange(r1)-1)]; - folderName = [realCollectionId substringToIndex: r1.location]; - messageName = [realCollectionId substringWithRange: NSMakeRange(NSMaxRange(r1), r2.location-r1.location-1)]; - pathToPart = [realCollectionId substringFromIndex: r2.location+1]; + folderName = [realCollectionId substringToIndex: r1.location]; + messageName = [realCollectionId substringWithRange: NSMakeRange(NSMaxRange(r1), r2.location-r1.location-1)]; + pathToPart = [realCollectionId substringFromIndex: r2.location+1]; - userFolder = [[context activeUser] homeFolderInContext: context]; - accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; - currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; + userFolder = [[context activeUser] homeFolderInContext: context]; + accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; + currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO]; - currentCollection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", folderName] - inContext: context - acquire: NO]; + currentCollection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", folderName] + inContext: context + acquire: NO]; - mailObject = [currentCollection lookupName: messageName inContext: context acquire: NO]; + mailObject = [currentCollection lookupName: messageName inContext: context acquire: NO]; + currentBodyPart = [mailObject lookupImap4BodyPartKey: pathToPart inContext: context]; - partKeys = [pathToPart componentsSeparatedByString: @"."]; + [s appendString: @""]; + [s appendString: @"1"]; + [s appendFormat: @"%@", [fileReference stringByEscapingURL]]; + [s appendString: @""]; - currentBodyPart = [mailObject lookupImap4BodyPartKey: [partKeys objectAtIndex:0] inContext: context]; - for (p = 1; p < [partKeys count]; p++) - { - currentBodyPart = [currentBodyPart lookupImap4BodyPartKey: [partKeys objectAtIndex:p] inContext: context]; - } - - [s appendString: @""]; - [s appendString: @"1"]; - [s appendFormat: @"%@", [fileReference stringByEscapingURL]]; - [s appendString: @""]; + [s appendFormat: @"%@/%@", [[currentBodyPart partInfo] objectForKey: @"type"], [[currentBodyPart partInfo] objectForKey: @"subtype"]]; - [s appendFormat: @"%@/%@", [[currentBodyPart partInfo] objectForKey: @"type"], [[currentBodyPart partInfo] objectForKey: @"subtype"]]; - - if ([[theResponse headerForKey: @"Content-Type"] isEqualToString:@"application/vnd.ms-sync.multipart"]) - { - NSData *d; - d = [currentBodyPart fetchBLOB]; - - [s appendFormat: @"%d", i+1]; - [partLength addObject: [NSNumber numberWithInteger: [d length]]]; - [parts appendData: d]; - } - else - { - NSString *a; - a = [[currentBodyPart fetchBLOB] activeSyncRepresentationInContext: context]; - - [s appendFormat: @"0-%d", [a length]-1]; - [s appendFormat: @"%@", a]; - } + if ([[theResponse headerForKey: @"Content-Type"] isEqualToString:@"application/vnd.ms-sync.multipart"]) + { + [s appendFormat: @"%d", i+1]; + [partLength addObject: [NSNumber numberWithInteger: [[currentBodyPart fetchBLOB] length]]]; + [parts appendData:[currentBodyPart fetchBLOB]]; } else { - // fetch mail - realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType]; - serverId = [[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue]; - bodyPreferenceType = [[(id)[[(id)[theDocumentElement getElementsByTagName: @"BodyPreference"] lastObject] getElementsByTagName: @"Type"] lastObject] textValue]; - [context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"]; - - currentCollection = [self collectionFromId: realCollectionId type: folderType]; - - mailObject = [currentCollection lookupName: serverId inContext: context acquire: NO]; - [s appendString: @""]; - [s appendString: @"1"]; - [s appendFormat: @"%@", collectionId]; - [s appendFormat: @"%@", serverId]; - [s appendString: @""]; - [s appendString: [mailObject activeSyncRepresentationInContext: context]]; + [s appendFormat: @"0-%d", [[[currentBodyPart fetchBLOB] activeSyncRepresentationInContext: context] length]-1]; + [s appendFormat: @"%@", [[currentBodyPart fetchBLOB] activeSyncRepresentationInContext: context]]; } [s appendString: @""]; @@ -1419,62 +1366,6 @@ static BOOL debugOn = NO; { [theResponse setContent: d]; } - } - else if ([theDocumentElement getElementsByTagName: @"EmptyFolderContents"]) - { - NGImap4Connection *connection; - NSEnumerator *subfolders; - NSException *error; - NSURL *currentURL; - id co; - - collectionId = [[(id)[theDocumentElement getElementsByTagName: @"CollectionId"] lastObject] textValue]; - realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType]; - realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType]; - - if (folderType == ActiveSyncMailFolder) - { - co = [self collectionFromId: realCollectionId type: folderType]; - error = [co addFlagsToAllMessages: @"deleted"]; - - if (!error) - error = [(SOGoMailFolder *)co expunge]; - - if (!error) - { - [co flushMailCaches]; - - if ([theDocumentElement getElementsByTagName: @"DeleteSubFolders"]) - { - // Delete sub-folders - connection = [co imap4Connection]; - subfolders = [[co allFolderURLs] objectEnumerator]; - - while ((currentURL = [subfolders nextObject])) - { - [[connection client] unsubscribe: [currentURL path]]; - [connection deleteMailboxAtURL: currentURL]; - } - } - - [s appendString: @"1"]; - [s appendString: @""]; - } - - if (error) - { - [s appendString: @"3"]; - [s appendString: @""]; - } - - d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml]; - [theResponse setContent: d]; - } - else - { - [theResponse setStatus: 500]; - return; - } } } diff --git a/ActiveSync/SOGoMailObject+ActiveSync.m b/ActiveSync/SOGoMailObject+ActiveSync.m index 164abb6db..d3095a966 100644 --- a/ActiveSync/SOGoMailObject+ActiveSync.m +++ b/ActiveSync/SOGoMailObject+ActiveSync.m @@ -381,6 +381,11 @@ struct GlobalObjectId { if (s) { + // We sanitize the content immediately, in case we have non-UNICODE safe + // characters that would be re-encoded later in HTML entities and thus, + // ignore afterwards. + s = [s safeString]; + body = [s dataUsingEncoding: NSUTF8StringEncoding]; } @@ -861,41 +866,10 @@ struct GlobalObjectId { if (d) { - NSMutableData *sanitizedData; NSString *content; int len, truncated; - - // Outlook fails to decode quoted-printable (see #3082) if lines are not termined by CRLF. - const char *bytes; - char *mbytes; - int mlen; - - len = [d length]; - mlen = 0; - - sanitizedData = [NSMutableData dataWithLength: len*2]; - - bytes = [d bytes]; - mbytes = [sanitizedData mutableBytes]; - - while (len > 0) - { - if (*bytes == '\n' && *(bytes-1) != '\r' && mlen > 0) - { - *mbytes = '\r'; - mbytes++; - mlen++; - } - - *mbytes = *bytes; - mbytes++; bytes++; - len--; - mlen++; - } - - [sanitizedData setLength: mlen]; - - content = [[NSString alloc] initWithData: sanitizedData encoding: NSUTF8StringEncoding]; + + content = [[NSString alloc] initWithData: d encoding: NSUTF8StringEncoding]; // FIXME: This is a hack. We should normally avoid doing this as we might get // broken encodings. We should rather tell that the data was truncated and expect @@ -905,15 +879,15 @@ struct GlobalObjectId { // for an "interesting" discussion around this. // if (!content) - content = [[NSString alloc] initWithData: sanitizedData encoding: NSISOLatin1StringEncoding]; + content = [[NSString alloc] initWithData: d encoding: NSISOLatin1StringEncoding]; AUTORELEASE(content); content = [content activeSyncRepresentationInContext: context]; truncated = 0; - + len = [content length]; - + if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"]) { [s appendFormat: @"%@", content]; diff --git a/ActiveSync/iCalEvent+ActiveSync.m b/ActiveSync/iCalEvent+ActiveSync.m index 2156b1eac..586bf5798 100644 --- a/ActiveSync/iCalEvent+ActiveSync.m +++ b/ActiveSync/iCalEvent+ActiveSync.m @@ -506,14 +506,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [self setOrganizer: person]; } + // + // iOS is plain stupid here. It seends event invitations with no Organizer. + // We check this corner-case and if MeetingStatus == 1 (see http://msdn.microsoft.com/en-us/library/ee219342(v=exchg.80).aspx or details) + // and there's no organizer, we fake one. + // if ((o = [theValues objectForKey: @"MeetingStatus"])) { - // - // iOS is plain stupid here. It seends event invitations with no Organizer. - // We check this corner-case and if MeetingStatus == 1 (see http://msdn.microsoft.com/en-us/library/ee219342(v=exchg.80).aspx or details) - // and there's no organizer, we fake one. - // - if ([o intValue] == 1 && ![theValues objectForKey: @"Organizer_Email"] && ![[[self organizer] rfc822Email] length]) + if ([o intValue] == 1 && ![theValues objectForKey: @"Organizer_Email"]) { iCalPerson *person; @@ -523,20 +523,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [person setPartStat: @"ACCEPTED"]; [self setOrganizer: person]; } - - // - // When MeetingResponse fails Outlook still sends a new calendar entry with MeetingStatus=3. - // Use the organizer from the request if the event has no organizer. - // - if ([o intValue] == 3 && [theValues objectForKey: @"Organizer_Email"] && ![[[self organizer] rfc822Email] length]) - { - iCalPerson *person; - person = [iCalPerson elementWithTag: @"organizer"]; - [person setEmail: [theValues objectForKey: @"Organizer_Email"]]; - [person setCn: [theValues objectForKey: @"Organizer_Name"]]; - [person setPartStat: @"ACCEPTED"]; - [self setOrganizer: person]; - } } diff --git a/Documentation/SOGoInstallationGuide.asciidoc b/Documentation/SOGoInstallationGuide.asciidoc index e337f6eb0..5ecd53450 100644 --- a/Documentation/SOGoInstallationGuide.asciidoc +++ b/Documentation/SOGoInstallationGuide.asciidoc @@ -1920,11 +1920,10 @@ events. This parameter is an array of arbitrary strings. Defaults to a list that depends on the language. -|U |SOGoCalendarCategoriesColors -|Parameter used to define the colour of categories. This parameter -is a dictionary of category name/color. +|U |SOGoCalendarDefaultCategoryColor +|Parameter used to define the default colour of categories. -Defaults to `#F0F0F0` for all categories when unset. +Defaults to `#F0F0F0` when unset. |U |SOGoCalendarEventsDefaultClassification |Parameter used to defined the default classification for new events. @@ -2137,8 +2136,7 @@ Multi-domains Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you want your installation to isolate two groups of users, you must -define a distinct authentication source for each _domain_. Your domain keys -must have the same value as your email domain you want to add. Following is +define a distinct authentication source for each _domain_. Following is the same configuration that now includes two domains (acme.com and coyote.com): @@ -2146,7 +2144,7 @@ coyote.com): { ... domains = { - acme.com = { + acme = { SOGoMailDomain = acme.com; SOGoDraftsFolderName = Drafts; SOGoUserSources = ( @@ -2167,7 +2165,7 @@ coyote.com): } ); }; - coyote.com = { + coyote = { SOGoMailDomain = coyote.com; SOGoIMAPServer = imap.coyote.com; SOGoUserSources = ( @@ -2198,7 +2196,7 @@ domains. [cols="3,47,50a"] |======================================================================= |S |SOGoEnableDomainBasedUID -|Parameter used to enable user identification by domain. Users will be +|Parameter used to activate user identification by domain. Users will be able (without being required) to login using the form `username@domain`, meaning that values of _UIDFieldName_ no longer have to be unique among all domains but only within the same domain. Internally, users will @@ -2713,11 +2711,6 @@ current version of SOGo from the previous release. [cols="100a"] |======================================================================= -h|2.3.1 -|The SOGoCalendarDefaultCategoryColor default has been removed. If you -want to customize the color of calendar categories, use the -SOGoCalendarCategories and SOGoCalendarCategoriesColors defaults. - h|2.3.0 |Run the shell script `sql-update-2.2.17_to_2.3.0.sh` or `sql-update-2.2.17_to_2.3.0-mysql.sh` (if you use MySQL). @@ -2725,9 +2718,6 @@ h|2.3.0 This will grow the "participant states" field of calendar quick tables to a larger size and add the the "c_description" column to calendar quick tables. -Moreover, if you are using a multi-domain configuration, make sure the keys for -your domains match the email domains you have defined. - h|2.2.8 |The configuration configuration parameters were renamed: diff --git a/NEWS b/NEWS index 080088c49..ec8cf7a00 100644 --- a/NEWS +++ b/NEWS @@ -1,23 +1,3 @@ -2.3.1 (2015-06-XX) ------------------- - -Enhancements - - improved EAS speed, especially when fetching big attachments - - now always enforce the organizer's default identity in appointments - - improved the handling of default calendar categories/colors (#3200) - - added support for DeletesAsMoves over EAS - -Bug fixes - - EAS's GetItemEstimate/ItemOperations now support fetching mails and empty folders - - fixed some rare cornercases in multidomain configurations - - properly escape folder after creation using EAS (#3237) - - fixed potential organizer highjacking when using EAS (#3131) - - properly support big characters in EAS and fix encoding QP EAS error for Outlook (#3082) - - properly encode id of DOM elements in Address Book module (#3239, #3245) - - fixed multi-domain support for sogo-tool backup/restore (#2600) - - fixed data ordering in events list of Calendar module (#3261) - - fixed data ordering in tasks list of Calendar module (#3267) - 2.3.0 (2015-06-01) ------------------- diff --git a/Scripts/sogo-systemd.conf b/Scripts/sogo-systemd.conf deleted file mode 100644 index 6755a2969..000000000 --- a/Scripts/sogo-systemd.conf +++ /dev/null @@ -1,2 +0,0 @@ -# SOGo needs directory in /var/run -d /var/run/sogo 0755 sogo sogo diff --git a/Scripts/sql-update-2.2.17_to_2.3.0-mysql.sh b/Scripts/sql-update-2.1.17_to_2.3.0-mysql.sh similarity index 95% rename from Scripts/sql-update-2.2.17_to_2.3.0-mysql.sh rename to Scripts/sql-update-2.1.17_to_2.3.0-mysql.sh index 1ed8a4818..bd8f3dd77 100755 --- a/Scripts/sql-update-2.2.17_to_2.3.0-mysql.sh +++ b/Scripts/sql-update-2.1.17_to_2.3.0-mysql.sh @@ -49,7 +49,6 @@ function adjustSchema() { echo "This script will ask for the sql password twice" >&2 echo "Converting c_partstates from VARCHAR(255) to mediumtext in calendar quick tables" >&2 -echo "Adding c_description column as mediumtext in calendar quick tables" >&2 tables=`mysql -p -s -u $username -h $hostname $database -e "select SUBSTRING_INDEX(c_quick_location, '/', -1) from $indextable where c_path3 = 'Calendar';"` for table in $tables; diff --git a/Scripts/sql-update-2.2.17_to_2.3.0.sh b/Scripts/sql-update-2.1.17_to_2.3.0.sh similarity index 89% rename from Scripts/sql-update-2.2.17_to_2.3.0.sh rename to Scripts/sql-update-2.1.17_to_2.3.0.sh index a9ff62559..fb6010d49 100755 --- a/Scripts/sql-update-2.2.17_to_2.3.0.sh +++ b/Scripts/sql-update-2.1.17_to_2.3.0.sh @@ -45,8 +45,7 @@ function adjustSchema() { IFS="$oldIFS" } -echo "Converting c_partstates from VARCHAR(255) to mediumtext in calendar quick tables" >&2 -echo "Adding c_description column as mediumtext in calendar quick tables" >&2 +echo "Converting c_cycleinfo from VARCHAR(255) to TEXT in calendar quick tables" >&2 tables=`psql -t -U $username -h $hostname $database -c "select split_part(c_quick_location, '/', 5) from $indextable where c_path3 = 'Calendar';"` for table in $tables; diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index f0d310bd7..915fd4aa5 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -3157,8 +3157,6 @@ 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]]; diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index e26bb405d..3e1fd73ff 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -1820,28 +1820,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent [self warnWithFormat: @"Invalid event: no end date; setting duration to %@", [event duration]]; } - if ([event organizer]) + if ([event organizer] && ![[[event organizer] cn] length]) { - 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"]]; - } + [[event organizer] setCn: [[event organizer] rfc822Email]]; } } } diff --git a/SoObjects/Appointments/iCalAlarm+SOGo.h b/SoObjects/Appointments/iCalAlarm+SOGo.h index 741be01fa..2980f7bc6 100644 --- a/SoObjects/Appointments/iCalAlarm+SOGo.h +++ b/SoObjects/Appointments/iCalAlarm+SOGo.h @@ -1,6 +1,6 @@ /* iCalAlarm+SOGo.h - this file is part of SOGo * - * Copyright (C) 2015 Inverse inc. + * Copyright (C) 2014 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 -@class iCalEntityObject; +@class iCalRepeatableEntityObject; @interface iCalAlarm (SOGoExtensions) -+ (id) alarmForEvent: (iCalEntityObject *) theEntity ++ (id) alarmForEvent: (iCalRepeatableEntityObject *) theEntity owner: (NSString *) theOwner action: (NSString *) reminderAction unit: (NSString *) reminderUnit diff --git a/SoObjects/Appointments/iCalAlarm+SOGo.m b/SoObjects/Appointments/iCalAlarm+SOGo.m index cb1cc57c0..c98c316db 100644 --- a/SoObjects/Appointments/iCalAlarm+SOGo.m +++ b/SoObjects/Appointments/iCalAlarm+SOGo.m @@ -1,6 +1,6 @@ /* iCalAlarm+SOGo.m - this file is part of SOGo * - * Copyright (C) 2015 Inverse inc. + * Copyright (C) 2014 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,7 +28,6 @@ #import #import -#import @implementation iCalAlarm (SOGoExtensions) @@ -66,7 +65,7 @@ [alarm addChild: aAttendee]; } -+ (id) alarmForEvent: (iCalEntityObject *) theEntity ++ (id) alarmForEvent: (iCalRepeatableEntityObject *) theEntity owner: (NSString *) theOwner action: (NSString *) reminderAction unit: (NSString *) reminderUnit diff --git a/SoObjects/Mailer/SOGoMailObject.m b/SoObjects/Mailer/SOGoMailObject.m index 2aabcaa75..8dd632d65 100644 --- a/SoObjects/Mailer/SOGoMailObject.m +++ b/SoObjects/Mailer/SOGoMailObject.m @@ -777,6 +777,8 @@ 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) diff --git a/SoObjects/SOGo/NSString+Utilities.h b/SoObjects/SOGo/NSString+Utilities.h index d172ff167..8474dc4e3 100644 --- a/SoObjects/SOGo/NSString+Utilities.h +++ b/SoObjects/SOGo/NSString+Utilities.h @@ -1,6 +1,6 @@ /* NSString+Utilities.h - this file is part of SOGo * - * Copyright (C) 2006-2015 Inverse inc. + * Copyright (C) 2006-2014 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,9 +46,6 @@ - (NSString *) asCSSIdentifier; - (NSString *) fromCSSIdentifier; -/* JavaScript safety */ -- (NSString *) asSafeJSString; - /* SQL safety */ - (NSString *) asSafeSQLString; diff --git a/SoObjects/SOGo/NSString+Utilities.m b/SoObjects/SOGo/NSString+Utilities.m index d7d85e5f5..46f42af8e 100644 --- a/SoObjects/SOGo/NSString+Utilities.m +++ b/SoObjects/SOGo/NSString+Utilities.m @@ -1,6 +1,6 @@ /* NSString+Utilities.m - this file is part of SOGo * - * Copyright (C) 2006-2015 Inverse inc. + * Copyright (C) 2006-2014 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 *) asSafeJSString +- (NSString *) doubleQuotedString { NSMutableString *representation; @@ -270,12 +270,7 @@ static int cssEscapingCount; [representation replaceString: @"\r" withString: @"\\r"]; [representation replaceString: @"\t" withString: @"\\t"]; - return representation; -} - -- (NSString *) doubleQuotedString -{ - return [NSString stringWithFormat: @"\"%@\"", [self asSafeJSString]]; + return [NSString stringWithFormat: @"\"%@\"", representation]; } // @@ -338,18 +333,12 @@ static int cssEscapingCount; int count; strings = [NSArray arrayWithObjects: @"_U_", @"_D_", @"_H_", @"_A_", @"_S_", - @"_C_", @"_SC_", - @"_CO_", @"_SP_", @"_SQ_", @"_DQ_", - @"_LP_", @"_RP_", @"_LS_", @"_RS_", @"_LC_", @"_RC_", - @"_AM_", @"_P_", @"_DS_", nil]; + @"_C_", @"_CO_", @"_SP_", @"_SQ_", @"_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) diff --git a/SoObjects/SOGo/SOGoDefaults.plist b/SoObjects/SOGo/SOGoDefaults.plist index d0557c666..e804095f0 100644 --- a/SoObjects/SOGo/SOGoDefaults.plist +++ b/SoObjects/SOGo/SOGoDefaults.plist @@ -70,6 +70,7 @@ SOGoMailAutoSave = "5"; + SOGoCalendarDefaultCategoryColor = "#aaa"; SOGoCalendarShouldDisplayWeekend = YES; SOGoCalendarEventsDefaultClassification = "PUBLIC"; SOGoCalendarTasksDefaultClassification = "PUBLIC"; @@ -86,10 +87,6 @@ $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}>)"; } diff --git a/SoObjects/SOGo/SOGoDomainDefaults.h b/SoObjects/SOGo/SOGoDomainDefaults.h index 7dea88121..fbc665852 100644 --- a/SoObjects/SOGo/SOGoDomainDefaults.h +++ b/SoObjects/SOGo/SOGoDomainDefaults.h @@ -68,6 +68,8 @@ - (NSArray *) refreshViewIntervals; - (NSString *) subscriptionFolderFormat; +- (NSString *) calendarDefaultCategoryColor; + - (NSArray *) freeBusyDefaultInterval; - (int) davCalendarStartTimeLimit; diff --git a/SoObjects/SOGo/SOGoDomainDefaults.m b/SoObjects/SOGo/SOGoDomainDefaults.m index b55cc0851..ff02b6a96 100644 --- a/SoObjects/SOGo/SOGoDomainDefaults.m +++ b/SoObjects/SOGo/SOGoDomainDefaults.m @@ -294,6 +294,11 @@ return [self stringForKey: @"SOGoLDAPContactInfoAttribute"]; } +- (NSString *) calendarDefaultCategoryColor +{ + return [self stringForKey: @"SOGoCalendarDefaultCategoryColor"]; +} + - (NSArray *) freeBusyDefaultInterval { return [self arrayForKey: @"SOGoFreeBusyDefaultInterval"]; diff --git a/SoObjects/SOGo/SOGoUser.m b/SoObjects/SOGo/SOGoUser.m index d81dcf52c..91d3b1b5a 100644 --- a/SoObjects/SOGo/SOGoUser.m +++ b/SoObjects/SOGo/SOGoUser.m @@ -165,9 +165,10 @@ // The domain is probably appended to the username; // make sure it is defined as a domain in the configuration. domain = [newLogin substringFromIndex: (r.location + r.length)]; - if ([[sd domainIds] containsObject: domain] && - ![sd enableDomainBasedUID]) + if ([[sd domainIds] containsObject: domain]) newLogin = [newLogin substringToIndex: r.location]; + else + domain = nil; if (domain != nil && ![sd enableDomainBasedUID]) // Login domains are enabled (SOGoLoginDomains) but not @@ -196,25 +197,8 @@ // When the user is associated to a domain, the [SOGoUser login] // method returns the combination login@domain while // [SOGoUser loginInDomain] only returns the login. - r = [realUID rangeOfString: domain options: NSBackwardsSearch|NSCaseInsensitiveSearch]; - - // Do NOT strip @domain.com if SOGoEnableDomainBasedUID is enabled since - // the real login most likely is the email address. - if (r.location != NSNotFound && ![sd enableDomainBasedUID]) - uid = [realUID substringToIndex: r.location-1]; - // 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]; + uid = [NSString stringWithString: realUID]; + realUID = [NSString stringWithFormat: @"%@@%@", realUID, domain]; } } diff --git a/SoObjects/SOGo/SOGoUserDefaults.m b/SoObjects/SOGo/SOGoUserDefaults.m index 9cc4b38e2..5d975f146 100644 --- a/SoObjects/SOGo/SOGoUserDefaults.m +++ b/SoObjects/SOGo/SOGoUserDefaults.m @@ -702,7 +702,7 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek"; - (NSDictionary *) calendarCategoriesColors { - return [self objectForKey: @"SOGoCalendarCategoriesColors"]; + return [self dictionaryForKey: @"SOGoCalendarCategoriesColors"]; } - (void) setCalendarShouldDisplayWeekend: (BOOL) newValue diff --git a/SoObjects/SOGo/SOGoUserManager.m b/SoObjects/SOGo/SOGoUserManager.m index f93cef7e6..127c6756c 100644 --- a/SoObjects/SOGo/SOGoUserManager.m +++ b/SoObjects/SOGo/SOGoUserManager.m @@ -494,10 +494,10 @@ static Class NSNullK; NSMutableDictionary *currentUser; NSDictionary *failedCount; NSString *dictPassword, *username, *jsonUser; - SOGoSystemDefaults *sd; + SOGoSystemDefaults *dd; BOOL checkOK; - sd = [SOGoSystemDefaults sharedSystemDefaults]; + dd = [SOGoSystemDefaults sharedSystemDefaults]; username = _login; @@ -517,9 +517,21 @@ static Class NSNullK; if (r.location != NSNotFound) { + NSArray *allDomains; + int i; + *_domain = [username substringFromIndex: r.location+1]; - if (![[[SOGoSystemDefaults sharedSystemDefaults] domainIds] containsObject: *_domain]) + allDomains = [[dd dictionaryForKey: @"domains"] allValues]; + + for (i = 0; i < [allDomains count]; i++) + { + if ([*_domain isEqualToString: [[allDomains objectAtIndex: i] objectForKey: @"SOGoMailDomain"]]) + break; + } + + // We haven't found one + if (i == [allDomains count]) *_domain = nil; } } @@ -536,10 +548,10 @@ static Class NSNullK; start_time = [[failedCount objectForKey: @"InitialDate"] unsignedIntValue]; delta = current_time - start_time; - block_time = [sd failedLoginBlockInterval]; + block_time = [dd failedLoginBlockInterval]; - if ([[failedCount objectForKey: @"FailedCount"] intValue] >= [sd maximumFailedLoginCount] && - delta >= [sd maximumFailedLoginInterval] && + if ([[failedCount objectForKey: @"FailedCount"] intValue] >= [dd maximumFailedLoginCount] && + delta >= [dd maximumFailedLoginInterval] && delta <= block_time ) { *_perr = PolicyAccountLocked; @@ -558,28 +570,6 @@ 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) { @@ -599,18 +589,6 @@ 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 @@ -624,7 +602,7 @@ static Class NSNullK; else { // If failed login "rate-limiting" is enabled, we adjust the stats - if ([sd maximumFailedLoginCount]) + if ([dd maximumFailedLoginCount]) { [[SOGoCache sharedCache] setFailedCount: ([[failedCount objectForKey: @"FailedCount"] intValue] + 1) forLogin: username]; @@ -732,9 +710,9 @@ static Class NSNullK; // // // -- (void) _fillContactInfosForUser: (NSMutableDictionary *) theCurrentUser - withUIDorEmail: (NSString *) theUID - inDomain: (NSString *) theDomain +- (void) _fillContactInfosForUser: (NSMutableDictionary *) currentUser + withUIDorEmail: (NSString *) uid + inDomain: (NSString *) domain { NSString *sourceID, *cn, *c_domain, *c_uid, *c_imaphostname, *c_imaplogin, *c_sievehostname; NSObject *currentSource; @@ -761,28 +739,19 @@ static Class NSNullK; enumerator = [access_types_list objectEnumerator]; while ((access_type = [enumerator nextObject]) != nil) - [theCurrentUser setObject: [NSNumber numberWithBool: YES] - forKey: access_type]; + [currentUser setObject: [NSNumber numberWithBool: YES] + forKey: access_type]; - if ([[theCurrentUser objectForKey: @"DomainLessLogin"] boolValue]) - { - NSRange r; - - r = [theUID rangeOfString: [NSString stringWithFormat: @"@%@", theDomain]]; - theUID = [theUID substringToIndex: r.location]; - } - - sogoSources = [[self authenticationSourceIDsInDomain: theDomain] objectEnumerator]; + sogoSources = [[self authenticationSourceIDsInDomain: domain] objectEnumerator]; userEntry = nil; while (!userEntry && (sourceID = [sogoSources nextObject])) { currentSource = [_sources objectForKey: sourceID]; - - userEntry = [currentSource lookupContactEntryWithUIDorEmail: theUID - inDomain: theDomain]; + userEntry = [currentSource lookupContactEntryWithUIDorEmail: uid + inDomain: domain]; if (userEntry) { - [theCurrentUser setObject: sourceID forKey: @"SOGoSource"]; + [currentUser setObject: sourceID forKey: @"SOGoSource"]; if (!cn) cn = [userEntry objectForKey: @"c_cn"]; if (!c_uid) @@ -804,27 +773,27 @@ static Class NSNullK; { access = [[userEntry objectForKey: access_type] boolValue]; if (!access) - [theCurrentUser setObject: [NSNumber numberWithBool: NO] - forKey: access_type]; + [currentUser setObject: [NSNumber numberWithBool: NO] + forKey: access_type]; } // We check if it's a group isGroup = [userEntry objectForKey: @"isGroup"]; if (isGroup) - [theCurrentUser setObject: isGroup forKey: @"isGroup"]; + [currentUser setObject: isGroup forKey: @"isGroup"]; // We also fill the resource attributes, if any if ([userEntry objectForKey: @"isResource"]) - [theCurrentUser setObject: [userEntry objectForKey: @"isResource"] - forKey: @"isResource"]; + [currentUser setObject: [userEntry objectForKey: @"isResource"] + forKey: @"isResource"]; if ([userEntry objectForKey: @"numberOfSimultaneousBookings"]) - [theCurrentUser setObject: [userEntry objectForKey: @"numberOfSimultaneousBookings"] - forKey: @"numberOfSimultaneousBookings"]; + [currentUser setObject: [userEntry objectForKey: @"numberOfSimultaneousBookings"] + forKey: @"numberOfSimultaneousBookings"]; // This is Active Directory specific attribute (needed on OpenChange/* layer) if ([userEntry objectForKey: @"samaccountname"]) - [theCurrentUser setObject: [userEntry objectForKey: @"samaccountname"] - forKey: @"sAMAccountName"]; + [currentUser setObject: [userEntry objectForKey: @"samaccountname"] + forKey: @"sAMAccountName"]; } } @@ -836,20 +805,20 @@ static Class NSNullK; c_domain = @""; if (c_imaphostname) - [theCurrentUser setObject: c_imaphostname forKey: @"c_imaphostname"]; + [currentUser setObject: c_imaphostname forKey: @"c_imaphostname"]; if (c_imaplogin) - [theCurrentUser setObject: c_imaplogin forKey: @"c_imaplogin"]; + [currentUser setObject: c_imaplogin forKey: @"c_imaplogin"]; if (c_sievehostname) - [theCurrentUser setObject: c_sievehostname forKey: @"c_sievehostname"]; + [currentUser setObject: c_sievehostname forKey: @"c_sievehostname"]; - [theCurrentUser setObject: emails forKey: @"emails"]; - [theCurrentUser setObject: cn forKey: @"cn"]; - [theCurrentUser setObject: c_uid forKey: @"c_uid"]; - [theCurrentUser setObject: c_domain forKey: @"c_domain"]; + [currentUser setObject: emails forKey: @"emails"]; + [currentUser setObject: cn forKey: @"cn"]; + [currentUser setObject: c_uid forKey: @"c_uid"]; + [currentUser 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: theCurrentUser]; + [self _fillContactMailRecords: currentUser]; } // @@ -943,9 +912,8 @@ static Class NSNullK; - (NSDictionary *) contactInfosForUserWithUIDorEmail: (NSString *) uid inDomain: (NSString *) domain { - NSString *aUID, *cacheUid, *jsonUser; NSMutableDictionary *currentUser; - + NSString *aUID, *cacheUid, *jsonUser; BOOL newUser; if ([uid isEqualToString: @"anonymous"]) @@ -954,14 +922,12 @@ static Class NSNullK; { // Remove the "@" prefix used to identified groups in the ACL tables. aUID = [uid hasPrefix: @"@"] ? [uid substringFromIndex: 1] : uid; - if (domain && [aUID rangeOfString: @"@"].location == NSNotFound) + if (domain) 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"] @@ -971,10 +937,8 @@ 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"]) || - ([currentUser count] == 2 && [currentUser objectForKey: @"password"] && [currentUser objectForKey: @"DomainLessLogin"])) - { + if (!currentUser || ([currentUser count] == 1 && [currentUser objectForKey: @"password"])) + { newUser = YES; if (!currentUser) @@ -994,22 +958,9 @@ static Class NSNullK; currentUser = nil; } else - { - 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]; - } - } + [self _retainUser: currentUser + withLogin: cacheUid]; + } } } else diff --git a/Tools/SOGoToolBackup.m b/Tools/SOGoToolBackup.m index fb2b4d016..8dd7ea04a 100644 --- a/Tools/SOGoToolBackup.m +++ b/Tools/SOGoToolBackup.m @@ -1,6 +1,9 @@ /* SOGoToolBackup.m - this file is part of SOGo * - * Copyright (C) 2009-2015 Inverse inc. + * Copyright (C) 2009-2011 Inverse inc. + * + * Author: Wolfgang Sourdeau + * Francis Lachapelle * * 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 @@ -39,7 +42,6 @@ #import #import #import -#import #import #import "SOGoTool.h" @@ -53,7 +55,7 @@ @interface SOGoToolBackup : SOGoTool { NSString *directory; - NSArray *usersToBackup; + NSArray *userIDs; } @end @@ -81,7 +83,7 @@ if ((self = [super init])) { directory = nil; - usersToBackup = nil; + userIDs = nil; } return self; @@ -90,7 +92,7 @@ - (void) dealloc { [directory release]; - [usersToBackup release]; + [userIDs release]; [super dealloc]; } @@ -141,7 +143,6 @@ lm = [SOGoUserManager sharedUserManager]; pool = [[NSAutoreleasePool alloc] init]; - max = [users count]; user = [users objectAtIndex: 0]; @@ -197,11 +198,11 @@ NSLog (@"user '%@' unknown", user); } [allUsers autorelease]; - - ASSIGN (usersToBackup, allUsers); + + ASSIGN (userIDs, [allUsers objectsForKey: @"c_uid" notFoundMarker: nil]); DESTROY(pool); - return ([usersToBackup count] > 0); + return ([userIDs count] > 0); } - (BOOL) parseArguments @@ -409,29 +410,19 @@ return YES; } -- (BOOL) exportUser: (NSDictionary *) theUser +- (BOOL) exportUser: (NSString *) uid { - NSString *exportPath, *gcsUID, *ldapUID; NSMutableDictionary *userRecord; - SOGoSystemDefaults *sd; - - sd = [SOGoSystemDefaults sharedSystemDefaults]; + NSString *exportPath; + userRecord = [NSMutableDictionary dictionary]; + exportPath = [directory stringByAppendingPathComponent: uid]; - ldapUID = [theUser objectForKey: @"c_uid"]; - exportPath = [directory stringByAppendingPathComponent: ldapUID]; - - gcsUID = [theUser objectForKey: @"c_uid"]; - - if ([sd enableDomainBasedUID] && [gcsUID rangeOfString: @"@"].location == NSNotFound) - gcsUID = [NSString stringWithFormat: @"%@@%@", gcsUID, [theUser objectForKey: @"c_domain"]]; - - - return ([self extractUserFolders: gcsUID + return ([self extractUserFolders: uid intoRecord: userRecord] - && [self extractUserLDIFRecord: ldapUID + && [self extractUserLDIFRecord: uid intoRecord: userRecord] - && [self extractUserPreferences: gcsUID + && [self extractUserPreferences: uid intoRecord: userRecord] && [userRecord writeToFile: exportPath atomically: NO]); @@ -447,10 +438,10 @@ pool = [NSAutoreleasePool new]; - max = [usersToBackup count]; + max = [userIDs count]; for (count = 0; rc && count < max; count++) { - rc = [self exportUser: [usersToBackup objectAtIndex: count]]; + rc = [self exportUser: [userIDs objectAtIndex: count]]; if ((count % 10) == 0) [pool emptyPool]; } diff --git a/Tools/SOGoToolRestore.h b/Tools/SOGoToolRestore.h index 61438ac18..f5be03928 100644 --- a/Tools/SOGoToolRestore.h +++ b/Tools/SOGoToolRestore.h @@ -33,7 +33,6 @@ typedef enum SOGoToolRestoreMode { { NSString *directory; NSString *userID; - NSString *filename; NSString *restoreFolder; BOOL destructive; /* destructive mode not handled */ SOGoToolRestoreMode restoreMode; diff --git a/Tools/SOGoToolRestore.m b/Tools/SOGoToolRestore.m index 712c680ba..a0f411bd9 100644 --- a/Tools/SOGoToolRestore.m +++ b/Tools/SOGoToolRestore.m @@ -1,6 +1,6 @@ /* SOGoToolRestore.m - this file is part of SOGo * - * Copyright (C) 2009-2015 Inverse inc. + * Copyright (C) 2009-2014 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 @@ -73,7 +73,6 @@ { directory = nil; userID = nil; - filename = nil; restoreFolder = nil; destructive = NO; } @@ -85,7 +84,6 @@ { [directory release]; [userID release]; - [filename release]; [restoreFolder release]; [super dealloc]; } @@ -154,35 +152,25 @@ - (BOOL) fetchUserID: (NSString *) identifier { - SOGoSystemDefaults *sd; + BOOL rc; SOGoUserManager *lm; NSDictionary *infos; + SOGoSystemDefaults *sd; NSString *uid = nil; - BOOL rc; - lm = [SOGoUserManager sharedUserManager]; infos = [lm contactInfosForUserWithUIDorEmail: identifier]; - uid = nil; - if (infos) { sd = [SOGoSystemDefaults sharedSystemDefaults]; - uid = [infos objectForKey: @"c_uid"]; - - if ([sd enableDomainBasedUID] && [uid rangeOfString: @"@"].location == NSNotFound) + if ([sd enableDomainBasedUID]) uid = [NSString stringWithFormat: @"%@@%@", - [infos objectForKey: @"c_uid"], - [infos objectForKey: @"c_domain"]]; - - if ([[infos objectForKey: @"DomainLessLogin"] boolValue]) - ASSIGN(filename, [infos objectForKey: @"c_uid"]); + [infos objectForKey: @"c_uid"], + [infos objectForKey: @"c_domain"]]; else - ASSIGN(filename, uid); + uid = [infos objectForKey: @"c_uid"]; } - ASSIGN (userID, uid); - if (userID) rc = YES; else @@ -620,7 +608,7 @@ NSString *importPath; BOOL rc; - importPath = [directory stringByAppendingPathComponent: filename]; + importPath = [directory stringByAppendingPathComponent: userID]; userRecord = [NSDictionary dictionaryWithContentsOfFile: importPath]; if (userRecord) { @@ -638,7 +626,7 @@ else { rc = NO; - NSLog(@"user backup (%@) file could not be loaded", importPath); + NSLog (@"user backup file could not be loaded"); } return rc; diff --git a/Tools/sogo-tool.m b/Tools/sogo-tool.m index bc4755416..d2d8b5c87 100644 --- a/Tools/sogo-tool.m +++ b/Tools/sogo-tool.m @@ -1,6 +1,8 @@ /* sogo-tool.m - this file is part of SOGo * - * Copyright (C) 2009-2015 Inverse inc. + * Copyright (C) 2009 Inverse inc. + * + * Author: Wolfgang Sourdeau * * 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 diff --git a/UI/Contacts/UIxContactEditor.m b/UI/Contacts/UIxContactEditor.m index 418945337..82ad1941e 100644 --- a/UI/Contacts/UIxContactEditor.m +++ b/UI/Contacts/UIxContactEditor.m @@ -1,6 +1,6 @@ /* Copyright (C) 2004-2005 SKYRIX Software AG - Copyright (C) 2005-2015 Inverse inc. + Copyright (C) 2005-2010 Inverse inc. This file is part of SOGo @@ -378,8 +378,9 @@ static Class SOGoContactGCSEntryK = Nil; result = [self redirectToLocation: [self modulePath]]; else { - jsRefreshMethod = [NSString stringWithFormat: @"refreshContacts('%@')", - [contact nameInContainer]]; + jsRefreshMethod + = [NSString stringWithFormat: @"refreshContacts(\"%@\")", + [contact nameInContainer]]; result = [self jsCloseWithRefreshMethod: jsRefreshMethod]; } diff --git a/UI/Contacts/UIxContactView.m b/UI/Contacts/UIxContactView.m index 4a4541794..259a518e9 100644 --- a/UI/Contacts/UIxContactView.m +++ b/UI/Contacts/UIxContactView.m @@ -1,6 +1,6 @@ /* Copyright (C) 2004 SKYRIX Software AG - Copyright (C) 2005-2015 Inverse inc. + Copyright (C) 2005-2014 Inverse inc. This file is part of SOGo. @@ -138,13 +138,9 @@ if ([email length] > 0) { fn = [card fn]; - if ([fn length] > 0) - attrs = [NSString stringWithFormat: @"%@ <%@>", fn, email]; - else - attrs = email; - attrs = [attrs stringByReplacingString: @"'" withString: @"\\'"]; - attrs = [attrs stringByReplacingString: @"\"" withString: @"\\\""]; - attrs = [NSString stringWithFormat: @"onclick=\"return openMailTo('%@');\"", attrs]; + fn = [fn stringByReplacingString: @"\"" withString: @""]; + fn = [fn stringByReplacingString: @"'" withString: @"\\\'"]; + attrs = [NSString stringWithFormat: @"onclick=\"return openMailTo('%@ <%@>');\"", fn, email]; } else { @@ -185,23 +181,16 @@ for (i = 0; i < [emails count]; i++) { email = [[emails objectAtIndex: i] flattenedValuesForKey: @""]; - if ([email length]) - { - fn = [card fn]; - if ([fn length]) - attrs = [NSString stringWithFormat: @"%@ <%@>", fn, email]; - else - attrs = email; - attrs = [attrs stringByReplacingString: @"'" withString: @"\\'"]; - attrs = [attrs stringByReplacingString: @"\"" withString: @"\\\""]; - attrs = [NSString stringWithFormat: @"onclick=\"return openMailTo('%@');\"", attrs]; - - [secondaryEmails addObject: [self _cardStringWithLabel: nil - value: email - byEscapingHTMLString: YES - asLinkScheme: @"mailto:" - withLinkAttributes: attrs]]; - } + fn = [card fn]; + fn = [fn stringByReplacingString: @"\"" withString: @""]; + fn = [fn stringByReplacingString: @"'" withString: @"\\\'"]; + attrs = [NSString stringWithFormat: @"onclick=\"return openMailTo('%@ <%@>');\"", fn, email]; + + [secondaryEmails addObject: [self _cardStringWithLabel: nil + value: email + byEscapingHTMLString: YES + asLinkScheme: @"mailto:" + withLinkAttributes: attrs]]; } } else diff --git a/UI/Contacts/UIxListEditor.m b/UI/Contacts/UIxListEditor.m index ea4920b15..69d63bd21 100644 --- a/UI/Contacts/UIxListEditor.m +++ b/UI/Contacts/UIxListEditor.m @@ -1,6 +1,6 @@ /* UIxListEditor.m - this file is part of SOGo * - * Copyright (C) 2008-2015 Inverse inc. + * Copyright (C) 2008-2014 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 @@ -29,8 +29,6 @@ #import #import -#import - #import #import #import @@ -291,8 +289,9 @@ result = [self redirectToLocation: [self modulePath]]; else { - jsRefreshMethod = [NSString stringWithFormat: @"refreshContacts('%@')", - [co nameInContainer]]; + jsRefreshMethod + = [NSString stringWithFormat: @"refreshContacts(\"%@\")", + [co nameInContainer]]; result = [self jsCloseWithRefreshMethod: jsRefreshMethod]; } } diff --git a/UI/MainUI/SOGoUserHomePage.m b/UI/MainUI/SOGoUserHomePage.m index 762de1f85..42f373f8d 100644 --- a/UI/MainUI/SOGoUserHomePage.m +++ b/UI/MainUI/SOGoUserHomePage.m @@ -463,7 +463,7 @@ if (!activeUserIsInDomain || ![uid isEqualToString: login]) { jsonLine = [NSMutableArray arrayWithCapacity: 4]; - if ([domain length] && [uid rangeOfString: @"@"].location == NSNotFound) + if ([domain length]) uid = [NSString stringWithFormat: @"%@@%@", uid, domain]; [jsonLine addObject: uid]; [jsonLine addObject: [contact objectForKey: @"cn"]]; diff --git a/UI/PreferencesUI/UIxPreferences.h b/UI/PreferencesUI/UIxPreferences.h index 51607c9a1..bbbb0a2b1 100644 --- a/UI/PreferencesUI/UIxPreferences.h +++ b/UI/PreferencesUI/UIxPreferences.h @@ -44,6 +44,7 @@ NSDictionary *calendarCategoriesColors; NSArray *contactsCategories; + NSString *defaultCategoryColor; NSCalendarDate *today; // Mail labels/tags diff --git a/UI/PreferencesUI/UIxPreferences.m b/UI/PreferencesUI/UIxPreferences.m index c73206d0b..990fda1ed 100644 --- a/UI/PreferencesUI/UIxPreferences.m +++ b/UI/PreferencesUI/UIxPreferences.m @@ -128,6 +128,7 @@ static NSArray *reminderValues = nil; calendarCategories = nil; calendarCategoriesColors = nil; + defaultCategoryColor = nil; category = nil; label = nil; @@ -174,6 +175,7 @@ static NSArray *reminderValues = nil; [vacationOptions release]; [calendarCategories release]; [calendarCategoriesColors release]; + [defaultCategoryColor release]; [category release]; [label release]; [mailLabels release]; @@ -1519,6 +1521,15 @@ static NSArray *reminderValues = nil; ASSIGN (calendarCategoriesColors, [userDefaults calendarCategoriesColors]); categoryColor = [calendarCategoriesColors objectForKey: category]; + if (!categoryColor) + { + if (!defaultCategoryColor) + { + dd = [[context activeUser] domainDefaults]; + ASSIGN (defaultCategoryColor, [dd calendarDefaultCategoryColor]); + } + categoryColor = defaultCategoryColor; + } return categoryColor; } diff --git a/UI/SOGoUI/UIxComponent.m b/UI/SOGoUI/UIxComponent.m index b2a1315af..336bd734d 100644 --- a/UI/SOGoUI/UIxComponent.m +++ b/UI/SOGoUI/UIxComponent.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2007-2015 Inverse inc. + Copyright (C) 2007-2012 Inverse inc. Copyright (C) 2004 SKYRIX Software AG This file is part of SOGo @@ -452,7 +452,7 @@ static SoProduct *commonProduct = nil; jsClose = [UIxJSClose new]; [jsClose autorelease]; - [jsClose setRefreshMethod: [methodName doubleQuotedString]]; + [jsClose setRefreshMethod: methodName]; return jsClose; } diff --git a/UI/Templates/ContactsUI/UIxContactFoldersView.wox b/UI/Templates/ContactsUI/UIxContactFoldersView.wox index 323badb7d..2f8a4c415 100644 --- a/UI/Templates/ContactsUI/UIxContactFoldersView.wox +++ b/UI/Templates/ContactsUI/UIxContactFoldersView.wox @@ -149,11 +149,11 @@ - - + var:categories="currentContact.c_categories" + var:id="currentContact.c_name" + var:contactname="currentContact.c_cn"> + + diff --git a/UI/Templates/UIxJSClose.wox b/UI/Templates/UIxJSClose.wox index f1bdef0ef..3a4487f5b 100644 --- a/UI/Templates/UIxJSClose.wox +++ b/UI/Templates/UIxJSClose.wox @@ -17,7 +17,7 @@ > diff --git a/UI/WebServerResources/ContactsUI.js b/UI/WebServerResources/ContactsUI.js index 3386bff6e..fd460859c 100644 --- a/UI/WebServerResources/ContactsUI.js +++ b/UI/WebServerResources/ContactsUI.js @@ -31,7 +31,7 @@ function openContactsFolder(contactsFolder, reload, idx) { var selection; if (idx) { - selection = [idx.asCSSIdentifier()]; + selection = [idx]; } else if (contactsFolder == Contact.currentAddressBook) { var contactsList = $("contactsList"); @@ -74,7 +74,7 @@ function contactsListCallback(http) { var contact = data[i]; var row = rows[i]; row.className = contact["c_component"]; - row.setAttribute("id", contact["c_name"].asCSSIdentifier()); + row.setAttribute("id", contact["c_name"]); row.setAttribute("categories", contact["c_categories"]); row.setAttribute("contactname", contact["c_cn"]); var cells = row.getElementsByTagName("TD"); @@ -111,7 +111,7 @@ function contactsListCallback(http) { for (var j = i; j < data.length; j++) { var contact = data[j]; var row = createElement("tr", - contact["c_name"].asCSSIdentifier(), + contact["c_name"], contact["c_component"], null, { categories: contact["c_categories"], @@ -272,7 +272,7 @@ function _onContactMenuAction(folderItem, action, refresh) { if (Object.isArray(document.menuTarget) && selectedFolders.length > 0) { var selectedFolderId = $(selectedFolders[0]).readAttribute("id"); var contactIds = $(document.menuTarget).collect(function(row) { - return row.getAttribute("id").fromCSSIdentifier(); + return row.getAttribute("id"); }); for (var i = 0; i < contactIds.length; i++) { @@ -283,7 +283,9 @@ function _onContactMenuAction(folderItem, action, refresh) { } var url = ApplicationBaseURL + "/" + selectedFolderId + "/" + action; - var uids = contactIds.collect(encodeURIComponent).join('&uid='); + var uids = contactIds.collect(function (s) { + return encodeURIComponent(s.unescapeHTML()); + }).join('&uid='); if (refresh) triggerAjaxRequest(url, actionContactCallback, selectedFolderId, ('folder='+ folderId + '&uid=' + uids), @@ -310,22 +312,22 @@ function onMenuExportContact (event) { if (canExport) { var selectedFolderId = $(selectedFolders[0]).readAttribute("id"); var contactIds = document.menuTarget.collect(function(row) { - return row.readAttribute("id").fromCSSIdentifier(); + return row.readAttribute("id"); }); var url = ApplicationBaseURL + "/" + selectedFolderId + "/export" - + "?uid=" + contactIds.collect(encodeURIComponent).join("&uid="); + + "?uid=" + contactIds.join("&uid="); window.location.href = url; } } function onMenuRawContact (event) { var cname = document.menuTarget.collect(function(row) { - return row.readAttribute("id").fromCSSIdentifier(); + return row.readAttribute("id"); }); $(function() { openGenericWindow(URLForFolderID(Contact.currentAddressBook) - + "/" + encodeURIComponent(cname) + "/raw"); + + "/" + cname + "/raw"); }).delay(0.1); } @@ -348,22 +350,22 @@ function actionContactCallback(http) { } } -function loadContact(cname) { +function loadContact(idx) { if (document.contactAjaxRequest) { document.contactAjaxRequest.aborted = true; document.contactAjaxRequest.abort(); } - if (cachedContacts[Contact.currentAddressBook + "/" + cname]) { + if (cachedContacts[Contact.currentAddressBook + "/" + idx]) { var div = $('contactView'); - Contact.currentContactId = cname; - div.innerHTML = cachedContacts[Contact.currentAddressBook + "/" + cname]; + Contact.currentContactId = idx; + div.innerHTML = cachedContacts[Contact.currentAddressBook + "/" + idx]; } else { var url = (URLForFolderID(Contact.currentAddressBook) - + "/" + encodeURIComponent(cname) + "/view?noframe=1"); + + "/" + encodeURIComponent(idx.unescapeHTML()) + "/view?noframe=1"); document.contactAjaxRequest - = triggerAjaxRequest(url, contactLoadCallback, cname); + = triggerAjaxRequest(url, contactLoadCallback, idx); } } @@ -416,9 +418,8 @@ function moveTo(uri) { /* contact menu entries */ function onContactRowDblClick(event) { var t = getTarget(event); - var cname = t.parentNode.getAttribute('id').fromCSSIdentifier(); + var cname = t.parentNode.getAttribute('id'); - cname = encodeURIComponent(cname); openContactWindow(URLForFolderID(Contact.currentAddressBook) + "/" + cname + "/edit", cname); @@ -437,7 +438,7 @@ function onContactSelectionChange(event) { if (rows.length == 1) { var node = $(rows[0]); - loadContact(node.getAttribute('id').fromCSSIdentifier()); + loadContact(node.getAttribute('id')); } else if (rows.length > 1) { $('contactView').update(); @@ -478,9 +479,8 @@ function onToolbarEditSelectedContacts(event) { } for (var i = 0; i < rows.length; i++) { - var id = encodeURIComponent(rows[i].fromCSSIdentifier()); openContactWindow(URLForFolderID(Contact.currentAddressBook) - + "/" + id + "/edit", rows[i]); + + "/" + rows[i] + "/edit", rows[i]); } return false; @@ -488,17 +488,16 @@ function onToolbarEditSelectedContacts(event) { function onToolbarWriteToSelectedContacts(event) { var contactsList = $('contactsList'); - var rowIds = contactsList.getSelectedRowsId(); + var rows = contactsList.getSelectedRowsId(); + var rowsWithEmail = 0; - if (rowIds.length == 0) { + if (rows.length == 0) { showAlertDialog(_("Please select a contact.")); } else { openMailComposeWindow(ApplicationBaseURL + "/../Mail/compose" + "?folder=" + Contact.currentAddressBook.substring(1) - + "&uid=" + rowIds.collect(function(id) { - return encodeURIComponent(id.fromCSSIdentifier()); - }).join("&uid=")); + + "&uid=" + rows.join("&uid=")); if (document.body.hasClassName("popup")) window.close(); } @@ -525,28 +524,26 @@ function onToolbarDeleteSelectedContactsConfirm(dialogId) { var contactsList = $('contactsList'); var rowIds = contactsList.getSelectedRowsId(); var urlstr = (URLForFolderID(Contact.currentAddressBook) + "/batchDelete"); - for (var i = 0; i < rowIds.length; i++) $(rowIds[i]).hide(); triggerAjaxRequest(urlstr, onContactDeleteEventCallback, rowIds, - ('ids=' + rowIds.collect(function(id) { - return encodeURIComponent(id.fromCSSIdentifier()); + ('ids=' + rowIds.collect(function (s) { + return encodeURIComponent(s.unescapeHTML()); }).join(",")), { "Content-type": "application/x-www-form-urlencoded" }); } function onContactDeleteEventCallback(http) { + var rowIds = http.callbackData; if (http.readyState == 4) { if (isHttpStatus204(http.status)) { - var rowIds = http.callbackData; var row; var nextRow = null; for (var i = 0; i < rowIds.length; i++) { - var id = rowIds[i].fromCSSIdentifier(); - delete cachedContacts[Contact.currentAddressBook + "/" + id]; + delete cachedContacts[Contact.currentAddressBook + "/" + rowIds[i]]; row = $(rowIds[i]); var displayName = row.readAttribute("contactname"); - if (Contact.currentContactId == id) { + if (Contact.currentContactId == row) { Contact.currentContactId = null; } var nextRow = row.next("tr"); @@ -558,7 +555,7 @@ function onContactDeleteEventCallback(http) { } } if (nextRow) { - Contact.currentContactId = nextRow.getAttribute("id").fromCSSIdentifier(); + Contact.currentContactId = nextRow.getAttribute("id"); nextRow.selectElement(); loadContact(Contact.currentContactId); } @@ -673,7 +670,7 @@ function onConfirmContactSelection(event) { var contactsList = $("contactsList"); var rows = contactsList.getSelectedRows(); for (i = 0; i < rows.length; i++) { - var cid = rows[i].getAttribute("id").fromCSSIdentifier(); + var cid = rows[i].getAttribute("id"); if (cid.endsWith(".vlf")) { addListToOpener(tag, Contact.currentAddressBook, currentAddressBookName, cid); } @@ -1298,7 +1295,7 @@ function onDocumentKeydown(event) { else if (keyCode == Event.KEY_DOWN || keyCode == Event.KEY_UP) { if (Contact.currentContactId) { - var row = $(Contact.currentContactId.asCSSIdentifier()); + var row = $(Contact.currentContactId); var nextRow; if (keyCode == Event.KEY_DOWN) nextRow = row.next("tr"); @@ -1322,7 +1319,7 @@ function onDocumentKeydown(event) { // Select and load the next message nextRow.selectElement(); - loadContact(nextRow.readAttribute("id").fromCSSIdentifier()); + loadContact(nextRow.readAttribute("id")); } Event.stop(event); } @@ -1468,12 +1465,11 @@ function onCategoriesMenuItemClick() { var rowIds = contactsList.getSelectedRowsId(); if (rowIds.length > 0) { for (var i = 0; i < rowIds.length; i++) { - var id = rowIds[i].fromCSSIdentifier(); var url = (URLForFolderID(Contact.currentAddressBook) - + "/" + encodeURIComponent(id) + "/" + method); + + "/" + rowIds[i] + "/" + method); url += "?category=" + encodeURIComponent(this.category); triggerAjaxRequest(url, onCategoriesMenuItemCallback, - { 'addressBook' : Contact.currentAddressBook, 'id' : id }); + { 'addressBook' : Contact.currentAddressBook, 'id' : rowIds[i] }); if (set) { setCategoryOnNode($(rowIds[i]), this.category); } @@ -1501,7 +1497,7 @@ function onCategoriesMenuItemCallback(http) { function setCategoryOnNode(contactNode, category) { var catList = contactNode.getAttribute("categories"); - var catsArray = catList? catList.split(",") : []; + var catsArray = catList.split(","); if (catsArray.indexOf(category) == -1) { catsArray.push(category); contactNode.setAttribute("categories", catsArray.join(",")); @@ -1611,9 +1607,9 @@ function dropSelectedContacts(action, toId) { if ((!currentFolderIsRemote() || action != "move") && fromId.substring(1) != toId) { - var url = ApplicationBaseURL + fromId + "/" + action; - var uids = contactIds.collect(function(id) { - return encodeURIComponent(id.fromCSSIdentifier()); + var url = ApplicationBaseURL + "/" + fromId + "/" + action; + var uids = contactIds.collect(function (s) { + return encodeURIComponent(s.unescapeHTML()); }).join('&uid='); triggerAjaxRequest(url, actionContactCallback, fromId, ('folder='+ toId + '&uid=' + uids), diff --git a/UI/WebServerResources/JavascriptAPIExtensions.js b/UI/WebServerResources/JavascriptAPIExtensions.js index 12db867bd..245078268 100644 --- a/UI/WebServerResources/JavascriptAPIExtensions.js +++ b/UI/WebServerResources/JavascriptAPIExtensions.js @@ -73,10 +73,6 @@ String.prototype.decodeEntities = function() { }); }; -String.prototype.unescapeHTMLEntities = function() { - return this.unescapeHTML().replace(/"/g,'"'); -}; - String.prototype.asDate = function () { var newDate; var date = this.split("/"); @@ -98,39 +94,20 @@ String.prototype.asDate = function () { return newDate; }; -RegExp.escape = function(text) { - return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); -} - -var css_invalid_characters = [ '_' , '.', '#' , '@' , '*', ':' , ';' , ',' , ' ', - '(', ')', '[', ']', '{', '}', - "'", '"', '&', '+' ]; -var css_escape_characters = [ '_U_', '_D_', '_H_', '_A_', '_S_', '_C_', '_SC_', '_CO_', '_SP_', - '_LP_', '_RP_', '_LS_', '_RQ_', '_LC_', '_RC_', - '_SQ_', '_DQ_', '_AM_', '_P_' ]; - String.prototype.asCSSIdentifier = function() { + var characters = [ '_' , '\\.', '#' , '@' , '\\*', ':' , ',' , ' ' + , "'", '&', '\\+' ]; + var escapeds = [ '_U_', '_D_', '_H_', '_A_', '_S_', '_C_', '_CO_', + '_SP_', '_SQ_', '_AM_', '_P_' ]; + var newString = this; - for (var i = 0; i < css_invalid_characters.length; i++) { - var re = new RegExp(RegExp.escape(css_invalid_characters[i]), 'g'); - newString = newString.replace(re, css_escape_characters[i]); + for (var i = 0; i < characters.length; i++) { + var re = new RegExp(characters[i], 'g'); + newString = newString.replace(re, escapeds[i]); } - if (/^\d/.test(newString)) + if (/^\d+/.test(newString)) { newString = '_' + newString; - - return newString; -}; - -String.prototype.fromCSSIdentifier = function() { - var newString = this; - - if (/^_\d/.test(newString)) - newString = newString.substring(1); - - for (var i = 0; i < css_escape_characters.length; i++) { - var re = new RegExp(css_escape_characters[i], 'g'); - newString = newString.replace(re, css_invalid_characters[i]); } return newString; diff --git a/UI/WebServerResources/SchedulerUI.js b/UI/WebServerResources/SchedulerUI.js index 2151ce95e..5ee6cb528 100644 --- a/UI/WebServerResources/SchedulerUI.js +++ b/UI/WebServerResources/SchedulerUI.js @@ -1015,32 +1015,6 @@ function eventsListCallback(http) { if (http.responseText.length > 0) { var data = http.responseText.evalJSON(true); - - // [0] Event ID - // [1] Calendar ID - // [2] Calendar name - // [3] Status - // [4] Title - // [5] Start date - // [6] End date - // [7] Location - // [8] Is all day? - // [9] Classification (0 = public, 1, = private, 2 = confidential) - // [10] Category - // [11] Participants email addresses - // [12] Participants states - // [13] Owner - // [14] Is cyclic? - // [15] Next alarm - // [16] recurrence-id - // [17] isException - // [18] Editable? - // [19] Erasable? - // [20] Owner is organizer? - // [21] Description - // [22] Formatted start date - // [23] Formatted end date - for (var i = 0; i < data.length; i++) { var row = createElement("tr"); table.tBodies[0].appendChild(row); @@ -1082,12 +1056,12 @@ function eventsListCallback(http) { td = createElement("td"); row.appendChild(td); td.observe("mousedown", listRowMouseDownHandler, true); - td.update(data[i][22]); // start date + td.update(data[i][21]); // start date td = createElement("td"); row.appendChild(td); td.observe("mousedown", listRowMouseDownHandler, true); - td.update(data[i][23]); // end date + td.update(data[i][22]); // end date td = createElement("td"); row.appendChild(td); @@ -1191,9 +1165,8 @@ function tasksListCallback(http) { // [12] Owner // [13] recurrence-id // [14] isException - // [15] Description - // [16] Status CSS class (duelater, completed, etc) - // [17] Due date (formatted) + // [15] Status CSS class (duelater, completed, etc) + // [16] Due date (formatted) for (var i = 0; i < data.length; i++) { var row = createElement("tr"); @@ -1209,12 +1182,16 @@ function tasksListCallback(http) { if (rTime) id += "-" + escape(rTime); row.setAttribute("id", id); + //row.cname = escape(data[i][0]); + //row.calendar = calendar; if (rTime) row.recurrenceTime = escape(rTime); row.isException = data[i][14]; + + //row.setAttribute("id", calendar + "-" + cname); //listItem.addClassName(data[i][5]); // Classification - row.addClassName(data[i][16]); // status + //row.addClassName(data[i][14]); // status row.addClassName("taskRow"); row.calendar = calendar; row.cname = cname; @@ -1259,8 +1236,8 @@ function tasksListCallback(http) { cell = createElement("td"); row.appendChild(cell); - if (data[i][17]) - cell.update(data[i][17]); // end date + if (data[i][16]) + cell.update(data[i][16]); // end date cell = createElement("td"); row.appendChild(cell); diff --git a/packaging/rhel/sogo.spec b/packaging/rhel/sogo.spec index d853d23b0..0b73d702b 100644 --- a/packaging/rhel/sogo.spec +++ b/packaging/rhel/sogo.spec @@ -253,9 +253,6 @@ cp Scripts/logrotate ${RPM_BUILD_ROOT}/etc/logrotate.d/sogo %if 0%{?_with_systemd} cp Scripts/sogo-systemd-redhat ${RPM_BUILD_ROOT}/usr/lib/systemd/system/sogod.service chmod 644 ${RPM_BUILD_ROOT}/usr/lib/systemd/system/sogod.service - mkdir ${RPM_BUILD_ROOT}/etc/tmpfiles.d - cp Scripts/sogo-systemd.conf ${RPM_BUILD_ROOT}/etc/tmpfiles.d/sogo.conf - chmod 644 ${RPM_BUILD_ROOT}/etc/tmpfiles.d/sogo.conf %else cp Scripts/sogo-init.d-redhat ${RPM_BUILD_ROOT}/etc/init.d/sogod chmod 755 ${RPM_BUILD_ROOT}/etc/init.d/sogod @@ -292,7 +289,6 @@ rm -fr ${RPM_BUILD_ROOT} %if 0%{?_with_systemd} /usr/lib/systemd/system/sogod.service -/etc/tmpfiles.d/sogo.conf %else /etc/init.d/sogod %endif