diff --git a/ActiveSync/NSString+ActiveSync.m b/ActiveSync/NSString+ActiveSync.m index e11465859f..d1d0fe0f2d 100644 --- a/ActiveSync/NSString+ActiveSync.m +++ b/ActiveSync/NSString+ActiveSync.m @@ -46,99 +46,6 @@ @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] < 0x20 || 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,7 @@ - (NSString *) sanitizedServerIdWithType: (SOGoMicrosoftActiveSyncFolderType) fo - (NSString *) activeSyncRepresentationInContext: (WOContext *) context { - return [self _stringByEscapingXMLStringUsingCharacters]; + return [self safeStringByEscapingXMLString]; } - (int) activeSyncFolderType diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 8b690f25e7..b31c28386a 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -2131,7 +2131,7 @@ - (NSDictionary *) freebusyResponseForRecipient: (iCalPerson *) recipient [content addObject: davElementWithContent (@"request-status", XMLNS_CALDAV, @"2.0;Success")]; [content addObject: davElementWithContent (@"calendar-data", XMLNS_CALDAV, - [calendarData stringByEscapingXMLString])]; + [calendarData safeStringByEscapingXMLString])]; } else [content addObject: diff --git a/SoObjects/Contacts/SOGoContactSourceFolder.m b/SoObjects/Contacts/SOGoContactSourceFolder.m index 2cfe833d07..e8ee8ddf50 100644 --- a/SoObjects/Contacts/SOGoContactSourceFolder.m +++ b/SoObjects/Contacts/SOGoContactSourceFolder.m @@ -467,7 +467,7 @@ - (NSString **) _properties: (NSString **) properties methodSel = SOGoSelectorForPropertyGetter (*currentProperty); if (methodSel && [ldifEntry respondsToSelector: methodSel]) *currentValue = [[ldifEntry performSelector: methodSel] - stringByEscapingXMLString]; + safeStringByEscapingXMLString]; currentProperty++; currentValue++; } diff --git a/SoObjects/Contacts/SOGoFolder+CardDAV.m b/SoObjects/Contacts/SOGoFolder+CardDAV.m index d193069872..a985c4788f 100644 --- a/SoObjects/Contacts/SOGoFolder+CardDAV.m +++ b/SoObjects/Contacts/SOGoFolder+CardDAV.m @@ -34,6 +34,7 @@ #import #import +#import #import #import @@ -73,7 +74,7 @@ - (void) _appendObject: (NSDictionary *) object [component davEntityTag]]; [r appendContentString: etagLine]; [r appendContentString: @""]; - contactString = [[component contentAsString] stringByEscapingXMLString]; + contactString = [[component contentAsString] safeStringByEscapingXMLString]; [r appendContentString: contactString]; [r appendContentString: @"" @""]; diff --git a/SoObjects/SOGo/NSString+Utilities.h b/SoObjects/SOGo/NSString+Utilities.h index d172ff1678..bed244bb27 100644 --- a/SoObjects/SOGo/NSString+Utilities.h +++ b/SoObjects/SOGo/NSString+Utilities.h @@ -54,6 +54,7 @@ /* Unicode safety */ - (NSString *) safeString; +- (NSString *) safeStringByEscapingXMLString; /* JSON */ - (NSString *) jsonRepresentation; diff --git a/SoObjects/SOGo/NSString+Utilities.m b/SoObjects/SOGo/NSString+Utilities.m index 9ac5c67753..3da54511a5 100644 --- a/SoObjects/SOGo/NSString+Utilities.m +++ b/SoObjects/SOGo/NSString+Utilities.m @@ -324,6 +324,100 @@ - (NSString *) safeString return AUTORELEASE(s); } + +// +// 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 avoid naming it like the one in SOPE since if the ActiveSync +// bundle is loaded, it'll overwrite the one provided by SOPE. +// +- (NSString *) safeStringByEscapingXMLString { + 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] < 0x20 || 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 *) jsonRepresentation { NSString *cleanedString; diff --git a/SoObjects/SOGo/SOGoGCSFolder.m b/SoObjects/SOGo/SOGoGCSFolder.m index adbcb348e5..123d17dadf 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.m +++ b/SoObjects/SOGo/SOGoGCSFolder.m @@ -2084,7 +2084,7 @@ - (NSString **) _properties: (NSString **) properties methodSel = SOGoSelectorForPropertyGetter (*currentProperty); if (methodSel && [sogoObject respondsToSelector: methodSel]) *currentValue = [[sogoObject performSelector: methodSel] - stringByEscapingXMLString]; + safeStringByEscapingXMLString]; currentProperty++; currentValue++; } diff --git a/SoObjects/SOGo/SOGoObject.m b/SoObjects/SOGo/SOGoObject.m index c54c0b76c8..7a4b9b223e 100644 --- a/SoObjects/SOGo/SOGoObject.m +++ b/SoObjects/SOGo/SOGoObject.m @@ -1277,7 +1277,7 @@ - (NSString *) davRecordForUser: (NSString *) user if (!cn) cn = user; [userRecord appendFormat: @"%@", - [cn stringByEscapingXMLString]]; + [cn safeStringByEscapingXMLString]]; } if (![params containsObject: @"noemail"]) diff --git a/SoObjects/SOGo/SOGoUserFolder.m b/SoObjects/SOGo/SOGoUserFolder.m index 2a44128e8a..1a8e9fa03d 100644 --- a/SoObjects/SOGo/SOGoUserFolder.m +++ b/SoObjects/SOGo/SOGoUserFolder.m @@ -52,6 +52,7 @@ #import "NSArray+Utilities.h" #import "NSDictionary+Utilities.h" +#import "NSString+Utilities.h" #import "SOGoUserManager.h" #import "SOGoPermissions.h" #import "SOGoSystemDefaults.h" @@ -286,7 +287,7 @@ - (void) _appendFolders: (NSArray *) folders [r appendContentString: @"HTTP/1.1 200 OK"]; [r appendContentString: @""]; data = [currentFolder objectForKey: @"displayName"]; - [r appendContentString: [data stringByEscapingXMLString]]; + [r appendContentString: [data safeStringByEscapingXMLString]]; [r appendContentString: @""]; /* Remove this once extensions 0.8x are no longer used */ @@ -299,12 +300,12 @@ - (void) _appendFolders: (NSArray *) folders ownerUser = [SOGoUser userWithLogin: [currentFolder objectForKey: @"owner"] roles: nil]; data = [ownerUser cn]; - [r appendContentString: [data stringByEscapingXMLString]]; + [r appendContentString: [data safeStringByEscapingXMLString]]; [r appendContentString: @""]; [r appendContentString: @""]; data = [currentFolder objectForKey: @"displayName"]; - [r appendContentString: [data stringByEscapingXMLString]]; + [r appendContentString: [data safeStringByEscapingXMLString]]; [r appendContentString: @""]; /* end of temporary compatibility hack */ @@ -436,14 +437,14 @@ - (NSString *) _davFetchUsersMatching: (NSString *) user [field stringByEscapingXMLString]]; field = [currentUser objectForKey: @"cn"]; [fetch appendFormat: @"%@", - [field stringByEscapingXMLString]]; + [field safeStringByEscapingXMLString]]; field = [currentUser objectForKey: @"c_email"]; [fetch appendFormat: @"%@", [field stringByEscapingXMLString]]; field = [currentUser objectForKey: @"c_info"]; if ([field length]) [fetch appendFormat: @"%@", - [field stringByEscapingXMLString]]; + [field safeStringByEscapingXMLString]]; [fetch appendString: @""]; } }