diff --git a/SOPE/sope-patchset-r1551.diff b/SOPE/sope-patchset-r1551.diff new file mode 100644 index 000000000..6faa58df8 --- /dev/null +++ b/SOPE/sope-patchset-r1551.diff @@ -0,0 +1,1198 @@ +Index: sope-gdl1/PostgreSQL/PostgreSQL72Channel.m +=================================================================== +--- sope-gdl1/PostgreSQL/PostgreSQL72Channel.m (révision 1551) ++++ sope-gdl1/PostgreSQL/PostgreSQL72Channel.m (copie de travail) +@@ -713,6 +713,39 @@ + return ms; + } + ++/* GCSEOAdaptorChannel protocol */ ++static NSString *sqlFolderFormat = (@"CREATE TABLE %@ (\n" \ ++ @" c_name VARCHAR (256) NOT NULL,\n" ++ @" c_content VARCHAR (100000) NOT NULL,\n" ++ @" c_creationdate INT4 NOT NULL,\n" ++ @" c_lastmodified INT4 NOT NULL,\n" ++ @" c_version INT4 NOT NULL,\n" ++ @" c_deleted INT4 NULL\n" ++ @")"); ++static NSString *sqlFolderACLFormat = (@"CREATE TABLE %@ (\n" \ ++ @" c_uid VARCHAR (256) NOT NULL,\n" ++ @" c_object VARCHAR (256) NOT NULL,\n" ++ @" c_role VARCHAR (80) NOT NULL\n" ++ @")"); ++ ++- (NSException *) createGCSFolderTableWithName: (NSString *) tableName ++{ ++ NSString *sql; ++ ++ sql = [NSString stringWithFormat: sqlFolderFormat, tableName]; ++ ++ return [self evaluateExpressionX: sql]; ++} ++ ++- (NSException *) createGCSFolderACLTableWithName: (NSString *) tableName ++{ ++ NSString *sql; ++ ++ sql = [NSString stringWithFormat: sqlFolderACLFormat, tableName]; ++ ++ return [self evaluateExpressionX: sql]; ++} ++ + @end /* PostgreSQL72Channel */ + + @implementation PostgreSQL72Channel(PrimaryKeyGeneration) +Index: sope-mime/NGImap4/NGImap4Connection.m +=================================================================== +--- sope-mime/NGImap4/NGImap4Connection.m (révision 1551) ++++ sope-mime/NGImap4/NGImap4Connection.m (copie de travail) +@@ -381,7 +381,7 @@ + + if (debugCache) [self logWithFormat:@" no folders cached yet .."]; + +- result = [[self client] list:(onlyFetchInbox ? @"INBOX" : @"*") ++ result = [[self client] list:(onlyFetchInbox ? @"INBOX" : @"") + pattern:@"*"]; + if (![[result valueForKey:@"result"] boolValue]) { + [self errorWithFormat:@"Could not list mailbox hierarchy!"]; +Index: sope-mime/NGImap4/NGImap4ResponseNormalizer.m +=================================================================== +--- sope-mime/NGImap4/NGImap4ResponseNormalizer.m (révision 1551) ++++ sope-mime/NGImap4/NGImap4ResponseNormalizer.m (copie de travail) +@@ -648,14 +648,13 @@ + enumerator = [_flags objectEnumerator]; + cnt = 0; + while ((obj = [enumerator nextObject])) { +- if (![obj isNotEmpty]) +- continue; +- +- if (![[obj substringToIndex:1] isEqualToString:@"\\"]) +- continue; +- +- objs[cnt] = [obj substringFromIndex:1]; +- cnt++; ++ if ([obj isNotEmpty]) { ++ if ([obj hasPrefix:@"\\"]) ++ objs[cnt] = [obj substringFromIndex:1]; ++ else ++ objs[cnt] = obj; ++ cnt++; ++ } + } + result = [NSArray arrayWithObjects:objs count:cnt]; + if (objs) free(objs); +Index: sope-mime/NGImap4/NGImap4ResponseParser.m +=================================================================== +--- sope-mime/NGImap4/NGImap4ResponseParser.m (révision 1551) ++++ sope-mime/NGImap4/NGImap4ResponseParser.m (copie de travail) +@@ -84,6 +84,8 @@ + static NSDictionary *_parseMultipartBody(NGImap4ResponseParser *self, + BOOL isBodyStructure); + ++static NSArray *_parseLanguages(); ++ + static NSString *_parseBodyString(NGImap4ResponseParser *self, + BOOL _convertString); + static NSString *_parseBodyDecodeString(NGImap4ResponseParser *self, +@@ -111,6 +113,7 @@ + static NSNumber *_parseUnsigned(NGImap4ResponseParser *self); + static NSString *_parseUntil(NGImap4ResponseParser *self, char _c); + static NSString *_parseUntil2(NGImap4ResponseParser *self, char _c1, char _c2); ++static BOOL _endsWithCQuote(NSString *_string); + + static __inline__ NSException *_consumeIfMatch + (NGImap4ResponseParser *self, unsigned char _m); +@@ -649,12 +652,31 @@ + } + + - (NSString *)_parseQuotedString { ++ NSMutableString *quotedString; ++ NSString *tmpString; ++ BOOL stop; ++ + /* parse a quoted string, eg '"' */ + if (_la(self, 0) == '"') { + _consume(self, 1); +- return _parseUntil(self, '"'); ++ quotedString = [NSMutableString string]; ++ stop = NO; ++ while (!stop) { ++ tmpString = _parseUntil(self, '"'); ++ [quotedString appendString: tmpString]; ++ if(_endsWithCQuote(tmpString)) { ++ [quotedString deleteSuffix: @"\\"]; ++ [quotedString appendString: @"\""]; ++ } ++ else { ++ stop = YES; ++ } ++ } + } +- return nil; ++ else { ++ quotedString = nil; ++ } ++ return quotedString; + } + - (void)_consumeOptionalSpace { + if (_la(self, 0) == ' ') _consume(self, 1); +@@ -1185,7 +1207,7 @@ + route = [self _parseQuotedStringOrNIL]; [self _consumeOptionalSpace]; + mailbox = [self _parseQuotedStringOrNIL]; [self _consumeOptionalSpace]; + host = [self _parseQuotedStringOrNIL]; [self _consumeOptionalSpace]; +- ++ + if (_la(self, 0) != ')') { + [self logWithFormat:@"WARNING: IMAP4 envelope " + @"address not properly closed (c0=%c,c1=%c): %@", +@@ -1197,6 +1219,7 @@ + address = [[NGImap4EnvelopeAddress alloc] initWithPersonalName:pname + sourceRoute:route mailbox:mailbox + host:host]; ++ + return address; + } + +@@ -1627,6 +1650,29 @@ + return _parseBodyDecodeString(self, _convertString, NO /* no decode */); + } + ++static NSArray *_parseLanguages(NGImap4ResponseParser *self) { ++ NSMutableArray *languages; ++ NSString *language; ++ ++ languages = [NSMutableArray array]; ++ if (_la(self, 0) == '(') { ++ while (_la(self, 0) != ')') { ++ _consume(self,1); ++ language = _parseBodyString(self, YES); ++ if ([language length]) ++ [languages addObject: language]; ++ } ++ _consume(self,1); ++ } ++ else { ++ language = _parseBodyString(self, YES); ++ if ([language length]) ++ [languages addObject: language]; ++ } ++ ++ return languages; ++} ++ + static NSDictionary *_parseBodyParameterList(NGImap4ResponseParser *self) + { + NSMutableDictionary *list; +@@ -1734,10 +1780,11 @@ + *encoding, *bodysize; + NSDictionary *parameterList; + NSMutableDictionary *dict; ++ NSArray *languages; + + type = [_parseBodyString(self, YES) lowercaseString]; + _consumeIfMatch(self, ' '); +- subtype = _parseBodyString(self, YES); ++ subtype = [_parseBodyString(self, YES) lowercaseString]; + _consumeIfMatch(self, ' '); + parameterList = _parseBodyParameterList(self); + _consumeIfMatch(self, ' '); +@@ -1762,7 +1809,8 @@ + _consumeIfMatch(self, ' '); + [dict setObject:_parseBodyString(self, YES) forKey:@"lines"]; + } +- else if ([type isEqualToString:@"message"]) { ++ else if ([type isEqualToString:@"message"] ++ && [subtype isEqualToString:@"rfc822"]) { + if (_la(self, 0) != ')') { + _consumeIfMatch(self, ' '); + _consumeIfMatch(self, '('); +@@ -1805,14 +1853,9 @@ + forKey: @"disposition"]; + if (_la(self, 0) != ')') { + _consume(self,1); +- if (_la(self, 0) == '(') { +- [dict setObject: _parseBodyParameterList(self) +- forKey: @"language"]; +- } +- else { +- [dict setObject: _parseBodyString(self, YES) +- forKey: @"language"]; +- } ++ languages = _parseLanguages(self); ++ if ([languages count]) ++ [dict setObject: languages forKey: @"languages"]; + if (_la(self, 0) != ')') { + _consume(self,1); + [dict setObject: _parseBodyString(self, YES) +@@ -1829,6 +1872,7 @@ + static NSDictionary *_parseMultipartBody(NGImap4ResponseParser *self, + BOOL isBodyStructure) { + NSMutableArray *parts; ++ NSArray *languages; + NSString *kind; + NSMutableDictionary *dict; + +@@ -1854,14 +1898,9 @@ + forKey: @"disposition"]; + if (_la(self, 0) != ')') { + _consume(self,1); +- if (_la(self, 0) == '(') { +- [dict setObject: _parseBodyParameterList(self) +- forKey: @"language"]; +- } +- else { +- [dict setObject: _parseBodyString(self, YES) +- forKey: @"language"]; +- } ++ languages = _parseLanguages(self); ++ if ([languages count]) ++ [dict setObject: languages forKey: @"languages"]; + if (_la(self, 0) != ')') { + _consume(self,1); + [dict setObject: _parseBodyString(self, YES) +@@ -2170,6 +2209,21 @@ + } + } + ++static BOOL _endsWithCQuote(NSString *_string){ ++ unsigned int quoteSlashes; ++ int pos; ++ ++ quoteSlashes = 0; ++ pos = [_string length] - 1; ++ while (pos > -1 ++ && [_string characterAtIndex: pos] == '\\') { ++ quoteSlashes++; ++ pos--; ++ } ++ ++ return ((quoteSlashes % 2) == 1); ++} ++ + - (NSException *)exceptionForFailedMatch:(unsigned char)_match + got:(unsigned char)_avail + { +Index: sope-mime/NGMail/NGSmtpClient.m +=================================================================== +--- sope-mime/NGMail/NGSmtpClient.m (révision 1551) ++++ sope-mime/NGMail/NGSmtpClient.m (copie de travail) +@@ -24,6 +24,82 @@ + #include "NGSmtpReplyCodes.h" + #include "common.h" + ++// ++// Useful extension that comes from Pantomime which is also ++// released under the LGPL. ++// ++@interface NSMutableData (DataCleanupExtension) ++ ++- (NSRange) rangeOfCString: (const char *) theCString; ++- (NSRange) rangeOfCString: (const char *) theCString ++ options: (unsigned int) theOptions ++ range: (NSRange) theRange; ++@end ++ ++@implementation NSMutableData (DataCleanupExtension) ++ ++- (NSRange) rangeOfCString: (const char *) theCString ++{ ++ return [self rangeOfCString: theCString ++ options: 0 ++ range: NSMakeRange(0,[self length])]; ++} ++ ++-(NSRange) rangeOfCString: (const char *) theCString ++ options: (unsigned int) theOptions ++ range: (NSRange) theRange ++{ ++ const char *b, *bytes; ++ int i, len, slen; ++ ++ if (!theCString) ++ { ++ return NSMakeRange(NSNotFound,0); ++ } ++ ++ bytes = [self bytes]; ++ len = [self length]; ++ slen = strlen(theCString); ++ ++ b = bytes; ++ ++ if (len > theRange.location + theRange.length) ++ { ++ len = theRange.location + theRange.length; ++ } ++ ++ if (theOptions == NSCaseInsensitiveSearch) ++ { ++ i = theRange.location; ++ b += i; ++ ++ for (; i <= len-slen; i++, b++) ++ { ++ if (!strncasecmp(theCString,b,slen)) ++ { ++ return NSMakeRange(i,slen); ++ } ++ } ++ } ++ else ++ { ++ i = theRange.location; ++ b += i; ++ ++ for (; i <= len-slen; i++, b++) ++ { ++ if (!memcmp(theCString,b,slen)) ++ { ++ return NSMakeRange(i,slen); ++ } ++ } ++ } ++ ++ return NSMakeRange(NSNotFound,0); ++} ++ ++@end ++ + @interface NGSmtpClient(PrivateMethods) + - (void)_fetchExtensionInfo; + @end +@@ -429,7 +505,9 @@ + + - (BOOL)sendData:(NSData *)_data { + NGSmtpResponse *reply = nil; +- ++ NSMutableData *cleaned_data; ++ NSRange r1, r2; ++ + [self requireState:NGSmtpState_TRANSACTION]; + + reply = [self sendCommand:@"DATA"]; +@@ -441,11 +519,54 @@ + } + [self->text flush]; + ++ cleaned_data = [NSMutableData dataWithData: _data]; ++ ++ // ++ // According to RFC 2821 section 4.5.2, we must check for the character ++ // sequence "."; any occurrence have its period duplicated ++ // to avoid data transparency. ++ // ++ // The following code was copied from Pantomime (and also the one ++ // that strips Bcc: headers from the mail's content) ++ // ++ r1 = [cleaned_data rangeOfCString: "\r\n."]; ++ ++ while (r1.location != NSNotFound) ++ { ++ [cleaned_data replaceBytesInRange: r1 withBytes: "\r\n.." length: 4]; ++ ++ r1 = [cleaned_data rangeOfCString: "\r\n." ++ options: 0 ++ range: NSMakeRange(NSMaxRange(r1)+1, [cleaned_data length]-NSMaxRange(r1)-1)]; ++ } ++ ++ // ++ // We now look for the Bcc: header. If it is present, we remove it. ++ // Some servers, like qmail, do not remove it automatically. ++ // ++ r1 = [cleaned_data rangeOfCString: "\r\n\r\n"]; ++ r1 = [cleaned_data rangeOfCString: "\r\nBcc: " ++ options: 0 ++ range: NSMakeRange(0,r1.location-1)]; ++ ++ if (r1.location != NSNotFound) ++ { ++ // We search for the first \r\n AFTER the Bcc: header and ++ // replace the whole thing with \r\n. ++ r2 = [cleaned_data rangeOfCString: "\r\n" ++ options: 0 ++ range: NSMakeRange(NSMaxRange(r1)+1,[cleaned_data length]-NSMaxRange(r1)-1)]; ++ [cleaned_data replaceBytesInRange: NSMakeRange(r1.location, NSMaxRange(r2)-r1.location) ++ withBytes: "\r\n" ++ length: 2]; ++ } ++ ++ + if (self->isDebuggingEnabled) +- [NGTextErr writeFormat:@"C: data(%i bytes) ..\n", [_data bytes]]; ++ [NGTextErr writeFormat:@"C: data(%i bytes) ..\n", [cleaned_data length]]; + +- [self->connection safeWriteBytes:[_data bytes] count:[_data length]]; +- [self->connection safeWriteBytes:".\r\n" count:3]; ++ [self->connection safeWriteBytes:[cleaned_data bytes] count:[cleaned_data length]]; ++ [self->connection safeWriteBytes:"\r\n.\r\n" count:5]; + [self->connection flush]; + + reply = [self receiveReply]; +Index: sope-mime/NGMail/NGMimeMessageGenerator.m +=================================================================== +--- sope-mime/NGMail/NGMimeMessageGenerator.m (révision 1551) ++++ sope-mime/NGMail/NGMimeMessageGenerator.m (copie de travail) +@@ -86,37 +86,40 @@ + char *des = NULL; + unsigned int cnt; + BOOL doEnc; +- NSString *str; ++// NSString *str; + + // TODO: this s***s big time! ++// NSLog (@"class: '%@'", NSStringFromClass ([_data class])); ++// #if APPLE_Foundation_LIBRARY || NeXT_Foundation_LIBRARY ++// str = [[NSString alloc] initWithData:_data ++// encoding:NSISOLatin1StringEncoding]; ++// str = [str autorelease]; ++ ++// #else ++// str = [[NSString alloc] initWithData:_data ++// encoding:NSISOLatin9StringEncoding]; ++// #endif ++// bytes = [str cString]; ++// length = [str cStringLength]; + +-#if APPLE_Foundation_LIBRARY || NeXT_Foundation_LIBRARY +- str = [[NSString alloc] initWithData:_data +- encoding:NSISOLatin1StringEncoding]; +-#else +- str = [[NSString alloc] initWithData:_data +- encoding:NSISOLatin9StringEncoding]; +-#endif +- str = [str autorelease]; +- +- bytes = [str cString]; +- length = [str cStringLength]; +- ++ bytes = [_data bytes]; ++ length = [_data length]; ++ + /* check whether we need to encode */ +- +- for (cnt = 0, doEnc = NO; cnt < length; cnt++) { +- if ((unsigned char)bytes[cnt] > 127) { ++ cnt = 0; ++ doEnc = NO; ++ while (!doEnc && cnt < length) ++ if ((unsigned char)bytes[cnt] > 127) + doEnc = YES; +- break; +- } +- } +- ++ else ++ cnt++; ++ + if (!doEnc) + return _data; + + /* encode quoted printable */ + { +- char iso[] = "=?iso-8859-15?q?"; ++ char iso[] = "=?utf-8?q?"; + unsigned isoLen = 16; + char isoEnd[] = "?="; + unsigned isoEndLen = 2; +Index: sope-mime/NGMime/NGMimeRFC822DateHeaderFieldParser.m +=================================================================== +--- sope-mime/NGMime/NGMimeRFC822DateHeaderFieldParser.m (révision 1551) ++++ sope-mime/NGMime/NGMimeRFC822DateHeaderFieldParser.m (copie de travail) +@@ -285,24 +285,16 @@ + - (id)parseValue:(id)_data ofHeaderField:(NSString *)_field { + // TODO: use UNICODE + NSCalendarDate *date = nil; +- unsigned char buf[256]; +- unsigned char *bytes = buf, *pe; ++ unsigned char *bytes, *pe; + unsigned length = 0; + NSTimeZone *tz = nil; + char dayOfMonth, monthOfYear, hour, minute, second; + short year; + BOOL flag; +- +- if ((length = [_data cStringLength]) > 254) { +- [self logWithFormat: +- @"header field value to large for date parsing: '%@'(%i)", +- _data, length]; +- length = 254; +- } +- +- [_data getCString:(char *)buf maxLength:length]; +- buf[length] = '\0'; +- ++ ++ length = [_data length]; ++ bytes = [_data cStringUsingEncoding: NSASCIIStringEncoding]; ++ + /* remove leading chars (skip to first digit, the day of the month) */ + while (length > 0 && (!isdigit(*bytes))) { + bytes++; +Index: sope-mime/NGMime/NGMimeBodyPart.m +=================================================================== +--- sope-mime/NGMime/NGMimeBodyPart.m (révision 1551) ++++ sope-mime/NGMime/NGMimeBodyPart.m (copie de travail) +@@ -31,18 +31,6 @@ + return 2; + } + +-static NGMimeType *defaultType = nil; +- +-+ (void)initialize { +- static BOOL isInitialized = NO; +- if (!isInitialized) { +- isInitialized = YES; +- +- defaultType = +- [[NGMimeType mimeType:@"text/plain; charset=us-ascii"] retain]; +- } +-} +- + + (id)bodyPartWithHeader:(NGHashMap *)_header { + return [[[self alloc] initWithHeader:_header] autorelease]; + } +@@ -156,13 +144,12 @@ + if (!Fields) + Fields = (NGMimeHeaderNames *)[NGMimePartParser headerFieldNames]; + +- + type = [self->header objectForKey:Fields->contentType]; + + if (![type isKindOfClass:[NGMimeType class]]) + type = [NGMimeType mimeType:[type stringValue]]; + +- return (type != nil ? type : (id)defaultType); ++ return type; + } + + - (NSString *)contentId { +Index: sope-mime/NGMime/NGMimeBodyParser.m +=================================================================== +--- sope-mime/NGMime/NGMimeBodyParser.m (révision 1551) ++++ sope-mime/NGMime/NGMimeBodyParser.m (copie de travail) +@@ -67,7 +67,10 @@ + if (_data == nil) return nil; + + ctype = [_part contentType]; +- ++ if (!ctype ++ && [_d respondsToSelector: @selector(parser:contentTypeOfPart:)]) ++ ctype = [_d parser: self contentTypeOfPart: _part]; ++ + if (![ctype isKindOfClass:[NGMimeType class]]) + ctype = [NGMimeType mimeType:[ctype stringValue]]; + +Index: sope-mime/NGMime/NGMimePartParser.h +=================================================================== +--- sope-mime/NGMime/NGMimePartParser.h (révision 1551) ++++ sope-mime/NGMime/NGMimePartParser.h (copie de travail) +@@ -117,6 +117,7 @@ + BOOL parserParseRawBodyDataOfPart:1; + BOOL parserBodyParserForPart:1; + BOOL parserDecodeBodyOfPart:1; ++ BOOL parserContentTypeOfPart:1; + } delegateRespondsTo; + + +@@ -275,6 +276,9 @@ + - (id)parser:(NGMimePartParser *)_parser + bodyParserForPart:(id)_part; + ++- (NGMimeType *)parser:(id)_parser ++ contentTypeOfPart:(id)_part; ++ + @end /* NSObject(NGMimePartParserDelegate) */ + + @interface NSObject(NGMimePartParser) +Index: sope-mime/NGMime/NGMimePartParser.m +=================================================================== +--- sope-mime/NGMime/NGMimePartParser.m (révision 1551) ++++ sope-mime/NGMime/NGMimePartParser.m (copie de travail) +@@ -227,7 +227,7 @@ + } + + + (NSStringEncoding)defaultHeaderFieldEncoding { +- return NSISOLatin1StringEncoding; ++ return NSUTF8StringEncoding; + } + + - (id)valueOfHeaderField:(NSString *)_name data:(id)_data { +@@ -1091,7 +1091,10 @@ + id bodyParser = nil; + + ctype = [_p contentType]; +- ++ if (!ctype ++ && self->delegateRespondsTo.parserContentTypeOfPart) ++ ctype = [self->delegate parser: self contentTypeOfPart: _p]; ++ + contentType = ([ctype isKindOfClass:[NGMimeType class]]) + ? ctype + : [NGMimeType mimeType:[ctype stringValue]]; +Index: sope-mime/NGMime/NGMimeContentDispositionHeaderFieldGenerator.m +=================================================================== +--- sope-mime/NGMime/NGMimeContentDispositionHeaderFieldGenerator.m (révision 1551) ++++ sope-mime/NGMime/NGMimeContentDispositionHeaderFieldGenerator.m (copie de travail) +@@ -49,80 +49,70 @@ + + // TODO: move the stuff below to some NSString or NSData category? + +- data = [NSMutableData dataWithCapacity:64]; ++ data = [NSMutableData dataWithCapacity: 64]; + tmp = [field type]; + [data appendBytes:[tmp cString] length:[tmp length]]; + tmp = [field filename]; + if (tmp != nil) { + [data appendBytes:"; " length:2]; + [data appendBytes:"filename=\"" length:10]; +- { +- unsigned char *ctmp; +- int cnt, len; +- BOOL doEnc; +- +- // TODO: unicode? +- len = [tmp cStringLength]; +- ctmp = malloc(len + 3); +- [tmp getCString:(char *)ctmp]; ctmp[len] = '\0'; +- cnt = 0; +- doEnc = NO; +- while (cnt < len) { +- if ((unsigned char)ctmp[cnt] > 127) { +- doEnc = YES; +- break; +- } +- cnt++; ++ ++ NSData *d; ++ unsigned char* bytes; ++ unsigned length; ++ int cnt; ++ BOOL doEnc; ++ ++ //d = [tmp dataUsingEncoding: NSUTF8StringEncoding]; ++ //bytes = [d bytes]; ++ //length = [d length]; ++ bytes = [tmp cStringUsingEncoding: NSUTF8StringEncoding]; ++ length = strlen(bytes); ++ ++ cnt = 0; ++ doEnc = NO; ++ while (cnt < length) { ++ if ((unsigned char)bytes[cnt] > 127) { ++ doEnc = YES; ++ break; + } +- if (doEnc) { +- char iso[] = "=?iso-8859-15?q?"; +- unsigned isoLen = 16; +- char isoEnd[] = "?="; +- unsigned isoEndLen = 2; +- unsigned desLen; +- char *des; +- +- if (ctmp) free(ctmp); +- { +- NSData *data; ++ cnt++; ++ } + +-#if APPLE_Foundation_LIBRARY || NeXT_Foundation_LIBRARY +- data = [tmp dataUsingEncoding:NSISOLatin1StringEncoding]; +-#else +- data = [tmp dataUsingEncoding:NSISOLatin9StringEncoding]; +-#endif +- +- len = [data length]; +- ctmp = malloc(len+1); +- [data getBytes:ctmp]; ctmp[len] = '\0'; +- } +- +- desLen = len * 3 + 20; +- des = calloc(desLen + 10, sizeof(char)); +- +- memcpy(des, ctmp, cnt); +- memcpy(des + cnt, iso, isoLen); +- desLen = +- NGEncodeQuotedPrintableMime((unsigned char *)ctmp + cnt, len - cnt, +- (unsigned char *)des + cnt + isoLen, +- desLen - cnt - isoLen); +- if ((int)desLen != -1) { +- memcpy(des + cnt + isoLen + desLen, isoEnd, isoEndLen); +- [data appendBytes:des length:(cnt + isoLen + desLen + isoEndLen)]; +- } +- else { ++ if (doEnc) ++ { ++ char iso[] = "=?utf-8?q?"; ++ unsigned isoLen = 10; ++ char isoEnd[] = "?="; ++ unsigned isoEndLen = 2; ++ int desLen; ++ char *des; ++ ++ desLen = length * 3 + 20; ++ ++ des = calloc(desLen + 2, sizeof(char)); ++ ++ memcpy(des, iso, isoLen); ++ desLen = NGEncodeQuotedPrintableMime((unsigned char *)bytes, length, ++ (unsigned char *)(des + isoLen), ++ desLen - isoLen); ++ if (desLen != -1) { ++ memcpy(des + isoLen + desLen, isoEnd, isoEndLen); ++ [data appendBytes:des length:(isoLen + desLen + isoEndLen)]; ++ } ++ else { + [self logWithFormat:@"WARNING(%s:%i): An error occour during " + @"quoted-printable decoding", + __PRETTY_FUNCTION__, __LINE__]; +- } +- if (des) free(des); ++ if (des != NULL) free(des); ++ } + } +- else { +- [data appendBytes:ctmp length:len]; ++ else ++ { ++ [data appendBytes:[tmp cString] length:[tmp length]]; + } +- } +- // [data appendBytes:[tmp cString] length:[tmp length]]; +- [data appendBytes:"\"" length:1]; ++ ++ [data appendBytes:"\"" length:1]; + } + return data; + } +Index: sope-core/NGExtensions/FdExt.subproj/NSString+Encoding.m +=================================================================== +--- sope-core/NGExtensions/FdExt.subproj/NSString+Encoding.m (révision 1551) ++++ sope-core/NGExtensions/FdExt.subproj/NSString+Encoding.m (copie de travail) +@@ -140,8 +140,12 @@ + + + #ifdef __linux__ ++#if __BYTE_ORDER == __LITTLE_ENDIAN + static NSString *unicharEncoding = @"UCS-2LE"; + #else ++static NSString *unicharEncoding = @"UCS-2BE"; ++#endif /* __BYTE_ORDER */ ++#else + static NSString *unicharEncoding = @"UCS-2-INTERNAL"; + #endif + static int IconvLogEnabled = -1; +@@ -149,21 +153,12 @@ + static void checkDefaults(void) { + NSUserDefaults *ud; + +- if (IconvLogEnabled != -1) +- return; +- ud = [NSUserDefaults standardUserDefaults]; +- IconvLogEnabled = [ud boolForKey:@"IconvLogEnabled"]?1:0; ++ if (IconvLogEnabled == -1) { ++ ud = [NSUserDefaults standardUserDefaults]; ++ IconvLogEnabled = [ud boolForKey:@"IconvLogEnabled"]?1:0; + +-#ifdef __linux__ +- if (NSHostByteOrder() == NS_BigEndian) { +- NSLog(@"Note: using UCS-2 big endian on Linux."); +- unicharEncoding = @"UCS-2BE"; ++ NSLog(@"Note: using '%@' on Linux.", unicharEncoding); + } +- else { +- NSLog(@"Note: using UCS-2 little endian on Linux."); +- unicharEncoding = @"UCS-2LE"; +- } +-#endif + } + + static char *iconv_wrapper(id self, char *_src, unsigned _srcLen, +Index: sope-xml/libxmlSAXDriver/libxmlHTMLSAXDriver.h +=================================================================== +--- sope-xml/libxmlSAXDriver/libxmlHTMLSAXDriver.h (révision 1551) ++++ sope-xml/libxmlSAXDriver/libxmlHTMLSAXDriver.h (copie de travail) +@@ -19,6 +19,8 @@ + 02111-1307, USA. + */ + ++#include ++ + #include + #include + #include +@@ -34,7 +36,7 @@ + + @interface libxmlHTMLSAXDriver : NSObject < SaxXMLReader > + { +- id contentHandler; ++ NSObject *contentHandler; + id dtdHandler; + id errorHandler; + id entityResolver; +Index: sope-xml/libxmlSAXDriver/libxmlHTMLSAXDriver.m +=================================================================== +--- sope-xml/libxmlSAXDriver/libxmlHTMLSAXDriver.m (révision 1551) ++++ sope-xml/libxmlSAXDriver/libxmlHTMLSAXDriver.m (copie de travail) +@@ -30,6 +30,12 @@ + #include + #include + ++@interface NSObject (contentHandlerExtensions) ++ ++- (xmlCharEncoding) contentEncoding; ++ ++@end ++ + @interface libxmlHTMLSAXDriver(PrivateMethods) + + - (void)tearDownParser; +@@ -194,10 +200,10 @@ + return self->entityResolver; + } + +-- (void)setContentHandler:(id)_handler { ++- (void)setContentHandler:(NSObject *)_handler { + ASSIGN(self->contentHandler, _handler); + } +-- (id)contentHandler { ++- (NSObject *)contentHandler { + return self->contentHandler; + } + +@@ -205,6 +211,7 @@ + + - (void)setupParserWithDocumentPath:(NSString *)_path { + xmlSAXHandler sax; ++ xmlCharEncoding charEncoding; + + if (self->ctxt != NULL) { + NSLog(@"WARNING(%s): HTML parser context already setup !", +@@ -223,14 +230,18 @@ + __PRETTY_FUNCTION__, self, activeDriver); + } + activeDriver = self; +- ++ ++ if ([self->contentHandler respondsToSelector: @selector (contentEncoding)]) ++ charEncoding = [self->contentHandler contentEncoding]; ++ else ++ charEncoding = XML_CHAR_ENCODING_8859_1; ++ + self->ctxt = htmlCreatePushParserCtxt(&sax /* sax */, + NULL /*self*/ /* userdata */, + NULL /* chunk */, + 0 /* chunklen */, + [_path cString] /* filename */, +- XML_CHAR_ENCODING_8859_1 +- /* encoding */); ++ charEncoding /* encoding */); + self->doc = NULL; + } + - (void)tearDownParser { +Index: sope-appserver/NGObjWeb/GNUmakefile.postamble +=================================================================== +--- sope-appserver/NGObjWeb/GNUmakefile.postamble (révision 1551) ++++ sope-appserver/NGObjWeb/GNUmakefile.postamble (copie de travail) +@@ -23,14 +23,20 @@ + + # install makefiles + +-after-install :: ++after-install :: $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/Additional/ngobjweb.make ++ ++ifneq ($(GNUSTEP_MAKE_VERSION),1.3.0) ++after-install :: $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/woapp.make $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/wobundle.make ++endif ++ ++$(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/Additional/ngobjweb.make: ngobjweb.make + $(MKDIRS) $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/Additional/ + $(INSTALL_DATA) ngobjweb.make $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/Additional/ngobjweb.make + +-ifneq ($(GNUSTEP_MAKE_VERSION),1.3.0) +-after-install :: ++$(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/woapp.make: woapp-gs.make + $(INSTALL_DATA) woapp-gs.make \ + $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/woapp.make ++ ++$(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/wobundle.make: wobundle-gs.make + $(INSTALL_DATA) wobundle-gs.make \ + $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/wobundle.make +-endif +Index: sope-appserver/NGObjWeb/WOContext.m +=================================================================== +--- sope-appserver/NGObjWeb/WOContext.m (révision 1551) ++++ sope-appserver/NGObjWeb/WOContext.m (copie de travail) +@@ -64,11 +64,13 @@ + static BOOL testNSURLs = NO; + static BOOL newCURLStyle = NO; + static NSString *WOApplicationSuffix = nil; ++static NSURL *redirectURL = nil; + + + (void)initialize { + static BOOL didInit = NO; + NSUserDefaults *ud; + NSString *cn; ++ NSString *url; + + if (didInit) return; + +@@ -91,6 +93,9 @@ + debugCursor = [ud boolForKey:@"WODebugCursor"] ? 1 : 0; + debugComponentAwake = [ud boolForKey:@"WODebugComponentAwake"]; + WOApplicationSuffix = [[ud stringForKey:@"WOApplicationSuffix"] copy]; ++ url = [ud stringForKey:@"WOApplicationRedirectURL"]; ++ if (url != nil) ++ redirectURL = [NSURL URLWithString: url]; + } + + + (id)contextWithRequest:(WORequest *)_r { +@@ -503,6 +508,11 @@ + return nil; + } + ++ if (redirectURL) { ++ // Use URL from user defaults (WOApplicationRedirectURL) ++ return redirectURL; ++ } ++ + if ((serverURL = [rq headerForKey:@"x-webobjects-server-url"]) == nil) { + if ((host = [rq headerForKey:@"host"])) + serverURL = [@"http://" stringByAppendingString:host]; +Index: sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.m +=================================================================== +--- sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.m (révision 1551) ++++ sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.m (copie de travail) +@@ -216,6 +216,12 @@ + assocCount++; + } + } ++ if (count > 0) { ++ if ((self->isAbsolute = OWGetProperty(_config, @"absolute"))) { ++ count--; ++ assocCount++; ++ } ++ } + + self->rest = _config; + +Index: sope-appserver/NGObjWeb/DynamicElements/_WOComplexHyperlink.m +=================================================================== +--- sope-appserver/NGObjWeb/DynamicElements/_WOComplexHyperlink.m (révision 1551) ++++ sope-appserver/NGObjWeb/DynamicElements/_WOComplexHyperlink.m (copie de travail) +@@ -40,6 +40,7 @@ + WOAssociation *string; + WOAssociation *target; + WOAssociation *disabled; ++ WOAssociation *isAbsolute; + WOElement *template; + + /* new in WO4: */ +@@ -359,6 +360,7 @@ + { + if ((self = [super initWithName:_name hyperlinkInfo:_info template:_t])) { + self->href = _info->href; ++ self->isAbsolute = _info->isAbsolute; + } + return self; + } +@@ -374,6 +376,9 @@ + // TODO: we need a binding to disable rewriting! + NSRange r; + ++ if ([[self->isAbsolute valueInContext:_ctx] boolValue] == YES) ++ return NO; ++ + r = [_s rangeOfString:@":"]; + if (r.length == 0) + return YES; +Index: sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.h +=================================================================== +--- sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.h (révision 1551) ++++ sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.h (copie de travail) +@@ -41,7 +41,8 @@ + WOAssociation *pageName; + WOAssociation *actionClass; + WOAssociation *directActionName; +- ++ WOAssociation *isAbsolute; ++ + BOOL sidInUrl; + + /* 'ivar' associations */ +Index: sope-appserver/NGObjWeb/SoObjects/SoObject.m +=================================================================== +--- sope-appserver/NGObjWeb/SoObjects/SoObject.m (révision 1551) ++++ sope-appserver/NGObjWeb/SoObjects/SoObject.m (copie de travail) +@@ -39,22 +39,34 @@ + static int debugLookup = -1; + static int debugBaseURL = -1; + static int useRelativeURLs = -1; ++static int redirectInitted = -1; ++static NSURL *redirectURL = nil; ++ + static void _initialize(void) { ++ NSString *url; ++ NSUserDefaults *ud; ++ ++ ud = [NSUserDefaults standardUserDefaults]; ++ + if (debugLookup == -1) { +- debugLookup = [[NSUserDefaults standardUserDefaults] +- boolForKey:@"SoDebugKeyLookup"] ? 1 : 0; ++ debugLookup = [ud boolForKey:@"SoDebugKeyLookup"] ? 1 : 0; + NSLog(@"Note(SoObject): SoDebugKeyLookup is enabled!"); + } + if (debugBaseURL == -1) { +- debugBaseURL = [[NSUserDefaults standardUserDefaults] +- boolForKey:@"SoDebugBaseURL"] ? 1 : 0; ++ debugBaseURL = [ud boolForKey:@"SoDebugBaseURL"] ? 1 : 0; + NSLog(@"Note(SoObject): SoDebugBaseURL is enabled!"); + } + if (useRelativeURLs == -1) { +- useRelativeURLs = [[NSUserDefaults standardUserDefaults] +- boolForKey:@"WOUseRelativeURLs"] ?1:0; ++ useRelativeURLs = [ud boolForKey:@"WOUseRelativeURLs"] ?1:0; + NSLog(@"Note(SoObject): relative base URLs are enabled."); + } ++ if (redirectInitted == -1) { ++ url = [ud stringForKey:@"WOApplicationRedirectURL"]; ++ if ([url length]) { ++ redirectURL = [[NSURL alloc] initWithString: url]; ++ } ++ redirectInitted = 1; ++ } + } + + /* classes */ +@@ -318,56 +330,61 @@ + + rq = [_ctx request]; + ms = [[NSMutableString alloc] initWithCapacity:128]; ++ ++ if (redirectURL) { ++ [ms appendString: [redirectURL absoluteString]]; ++ } ++ else { ++ if (!useRelativeURLs) { ++ port = [[rq headerForKey:@"x-webobjects-server-port"] intValue]; + +- if (!useRelativeURLs) { +- port = [[rq headerForKey:@"x-webobjects-server-port"] intValue]; +- +- /* this is actually a bug in Apache */ +- if (port == 0) { +- static BOOL didWarn = NO; +- if (!didWarn) { +- [self warnWithFormat:@"(%s:%i): got an empty port from Apache!", +- __PRETTY_FUNCTION__, __LINE__]; +- didWarn = YES; ++ /* this is actually a bug in Apache */ ++ if (port == 0) { ++ static BOOL didWarn = NO; ++ if (!didWarn) { ++ [self warnWithFormat:@"(%s:%i): got an empty port from Apache!", ++ __PRETTY_FUNCTION__, __LINE__]; ++ didWarn = YES; ++ } ++ port = 80; + } +- port = 80; +- } + +- if ((tmp = [rq headerForKey:@"host"]) != nil) { +- /* check whether we have a host header with port */ +- if ([tmp rangeOfString:@":"].length == 0) +- tmp = nil; +- } +- if (tmp != nil) { /* we have a host header with port */ +- isHTTPS = +- [[rq headerForKey:@"x-webobjects-server-url"] hasPrefix:@"https"]; +- [ms appendString:isHTTPS ? @"https://" : @"http://"]; +- [ms appendString:tmp]; +- } +- else if ((tmp = [rq headerForKey:@"x-webobjects-server-url"]) != nil) { +- /* sometimes the URL is just wrong! (suggests port 80) */ +- if ([tmp hasSuffix:@":0"] && [tmp length] > 2) { // TODO: bad bad bad +- [self warnWithFormat:@"%s: got incorrect URL from Apache: '%@'", +- __PRETTY_FUNCTION__, tmp]; +- tmp = [tmp substringToIndex:([tmp length] - 2)]; ++ if ((tmp = [rq headerForKey:@"host"]) != nil) { ++ /* check whether we have a host header with port */ ++ if ([tmp rangeOfString:@":"].length == 0) ++ tmp = nil; + } +- else if ([tmp hasSuffix:@":443"] && [tmp hasPrefix:@"http://"]) { +- /* see OGo bug #1435, Debian Apache hack */ +- [self warnWithFormat:@"%s: got 'http' protocol but 443 port, " +- @"assuming Debian/Apache bug (OGo #1435): '%@'", +- __PRETTY_FUNCTION__, tmp]; +- tmp = [tmp substringWithRange:NSMakeRange(4, [tmp length] - 4 - 4)]; +- tmp = [@"https" stringByAppendingString:tmp]; ++ if (tmp != nil) { /* we have a host header with port */ ++ isHTTPS = ++ [[rq headerForKey:@"x-webobjects-server-url"] hasPrefix:@"https"]; ++ [ms appendString:isHTTPS ? @"https://" : @"http://"]; ++ [ms appendString:tmp]; + } +- [ms appendString:tmp]; +- } +- else { +- // TODO: isHTTPS always no in this case? +- [ms appendString:isHTTPS ? @"https://" : @"http://"]; ++ else if ((tmp = [rq headerForKey:@"x-webobjects-server-url"]) != nil) { ++ /* sometimes the URL is just wrong! (suggests port 80) */ ++ if ([tmp hasSuffix:@":0"] && [tmp length] > 2) { // TODO: bad bad bad ++ [self warnWithFormat:@"%s: got incorrect URL from Apache: '%@'", ++ __PRETTY_FUNCTION__, tmp]; ++ tmp = [tmp substringToIndex:([tmp length] - 2)]; ++ } ++ else if ([tmp hasSuffix:@":443"] && [tmp hasPrefix:@"http://"]) { ++ /* see OGo bug #1435, Debian Apache hack */ ++ [self warnWithFormat:@"%s: got 'http' protocol but 443 port, " ++ @"assuming Debian/Apache bug (OGo #1435): '%@'", ++ __PRETTY_FUNCTION__, tmp]; ++ tmp = [tmp substringWithRange:NSMakeRange(4, [tmp length] - 4 - 4)]; ++ tmp = [@"https" stringByAppendingString:tmp]; ++ } ++ [ms appendString:tmp]; ++ } ++ else { ++ // TODO: isHTTPS always no in this case? ++ [ms appendString:isHTTPS ? @"https://" : @"http://"]; + +- [ms appendString:[rq headerForKey:@"x-webobjects-server-name"]]; +- if ((isHTTPS ? (port != 443) : (port != 80)) && port != 0) +- [ms appendFormat:@":%i", port]; ++ [ms appendString:[rq headerForKey:@"x-webobjects-server-name"]]; ++ if ((isHTTPS ? (port != 443) : (port != 80)) && port != 0) ++ [ms appendFormat:@":%i", port]; ++ } + } + } + +Index: sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpTransaction.m +=================================================================== +--- sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpTransaction.m (révision 1551) ++++ sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpTransaction.m (copie de travail) +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + #include "common.h" + + #include +@@ -1016,6 +1017,12 @@ + - (void)parser:(NGMimePartParser *)_parser didParseHeader:(NGHashMap *)_header { + } + ++- (NGMimeType *)parser:(id)_parser ++ contentTypeOfPart:(id)_part ++{ ++ return [NGMimeType mimeType: @"text/plain; charset=utf-8"]; ++} ++ + @end /* WOHttpAdaptor */ + + @implementation WOCoreApplication(SimpleParserSelection)