diff --git a/ActiveSync/NSString+ActiveSync.m b/ActiveSync/NSString+ActiveSync.m index 6b7d03a999..5b0ed646d5 100644 --- a/ActiveSync/NSString+ActiveSync.m +++ b/ActiveSync/NSString+ActiveSync.m @@ -43,99 +43,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) @@ -155,7 +62,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 11cc9dfbd7..b75d5c3592 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -2140,7 +2140,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 c0ec7c6e8c..2cb3aee46d 100644 --- a/SoObjects/Contacts/SOGoContactSourceFolder.m +++ b/SoObjects/Contacts/SOGoContactSourceFolder.m @@ -498,7 +498,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 fe976d355c..e6e1464165 100644 --- a/SoObjects/Contacts/SOGoFolder+CardDAV.m +++ b/SoObjects/Contacts/SOGoFolder+CardDAV.m @@ -28,6 +28,7 @@ #import #import +#import #import #import @@ -67,7 +68,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 32756d4fc2..2f5585a1fe 100644 --- a/SoObjects/SOGo/NSString+Utilities.m +++ b/SoObjects/SOGo/NSString+Utilities.m @@ -319,6 +319,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 c60eb348e4..3eeb7ffaf2 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.m +++ b/SoObjects/SOGo/SOGoGCSFolder.m @@ -2076,7 +2076,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 b905c4b37a..99a25b2522 100644 --- a/SoObjects/SOGo/SOGoObject.m +++ b/SoObjects/SOGo/SOGoObject.m @@ -1255,7 +1255,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 887011510c..c9241e586c 100644 --- a/SoObjects/SOGo/SOGoUserFolder.m +++ b/SoObjects/SOGo/SOGoUserFolder.m @@ -37,6 +37,7 @@ #import "NSArray+Utilities.h" #import "NSDictionary+Utilities.h" +#import "NSString+Utilities.h" #import "SOGoUserManager.h" #import "SOGoPermissions.h" #import "SOGoSystemDefaults.h" @@ -271,7 +272,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 */ @@ -284,12 +285,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 */ @@ -421,14 +422,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: @""]; } }