diff --git a/SOPE/sope-debian.diff b/SOPE/sope-debian.diff index 6903b03fb..14355227c 100644 --- a/SOPE/sope-debian.diff +++ b/SOPE/sope-debian.diff @@ -1,17 +1,24 @@ ---- sope-4.9.r1660.orig/debian/sope_SOPEVER_-gdl1-postgresql.install -+++ sope-4.9.r1660/debian/sope_SOPEVER_-gdl1-postgresql.install +--- sope-4.9.r1664.orig/debian/sope_SOPEVER_-gdl1-postgresql.install ++++ sope-4.9.r1664/debian/sope_SOPEVER_-gdl1-postgresql.install @@ -1 +1 @@ -usr/lib/sope-*/dbadaptors/PostgreSQL.gdladaptor +usr/lib/GNUstep/GDLAdaptors-*/PostgreSQL.gdladaptor ---- sope-4.9.r1660.orig/debian/libsope-ical_SOPEVER_-dev.install -+++ sope-4.9.r1660/debian/libsope-ical_SOPEVER_-dev.install +--- sope-4.9.r1664.orig/debian/libsope-ical_SOPEVER_-dev.install ++++ sope-4.9.r1664/debian/libsope-ical_SOPEVER_-dev.install @@ -1,2 +1,2 @@ -usr/include/NGiCal +usr/include/GNUstep/NGiCal usr/lib/libNGiCal.so ---- sope-4.9.r1660.orig/debian/changelog -+++ sope-4.9.r1660/debian/changelog -@@ -1,3 +1,21 @@ +--- sope-4.9.r1664.orig/debian/changelog ++++ sope-4.9.r1664/debian/changelog +@@ -1,3 +1,28 @@ ++sope (4.9.r1664-1) unstable; urgency=low ++ ++ * updated SOPE patchset for SOGo 1.2.0. ++ * updated upstream SOPE revision to 1664. ++ ++ -- Inverse Support Wed, 06 Jan 2010 14:49:27 -0500 ++ +sope (4.9.r1660-2) unstable; urgency=low + + * debian/control: @@ -33,8 +40,8 @@ sope (4.7.0-0) UNRELEASED; urgency=low * New upstream release. ---- sope-4.9.r1660.orig/debian/rules -+++ sope-4.9.r1660/debian/rules +--- sope-4.9.r1664.orig/debian/rules ++++ sope-4.9.r1664/debian/rules @@ -7,13 +7,16 @@ #export DH_VERBOSE=1 @@ -191,17 +198,17 @@ + binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install configure ---- sope-4.9.r1660.orig/debian/sope_SOPEVER_-libxmlsaxdriver.install -+++ sope-4.9.r1660/debian/sope_SOPEVER_-libxmlsaxdriver.install +--- sope-4.9.r1664.orig/debian/sope_SOPEVER_-libxmlsaxdriver.install ++++ sope-4.9.r1664/debian/sope_SOPEVER_-libxmlsaxdriver.install @@ -1 +1 @@ -usr/lib/sope-*/saxdrivers/libxmlSAXDriver.sax +usr/lib/GNUstep/SaxDrivers-*/libxmlSAXDriver.sax ---- sope-4.9.r1660.orig/debian/sope_SOPEVER_-gdl1-mysql.install -+++ sope-4.9.r1660/debian/sope_SOPEVER_-gdl1-mysql.install +--- sope-4.9.r1664.orig/debian/sope_SOPEVER_-gdl1-mysql.install ++++ sope-4.9.r1664/debian/sope_SOPEVER_-gdl1-mysql.install @@ -0,0 +1 @@ +usr/lib/GNUstep/GDLAdaptors-*/MySQL.gdladaptor ---- sope-4.9.r1660.orig/debian/control.in -+++ sope-4.9.r1660/debian/control.in +--- sope-4.9.r1664.orig/debian/control.in ++++ sope-4.9.r1664/debian/control.in @@ -1,8 +1,9 @@ Source: sope +Section: web @@ -330,14 +337,14 @@ -Description: Apache2 module for the SOPE application server - This package contains an apache module which enables the apache2 - webserver to deliver pages generated by the SOPE application server. ---- sope-4.9.r1660.orig/debian/libsope-ical_SOPEVER_.install -+++ sope-4.9.r1660/debian/libsope-ical_SOPEVER_.install +--- sope-4.9.r1664.orig/debian/libsope-ical_SOPEVER_.install ++++ sope-4.9.r1664/debian/libsope-ical_SOPEVER_.install @@ -1,2 +1,2 @@ usr/lib/libNGiCal.so.* -usr/share/sope-*/saxmappings +usr/lib/GNUstep/SaxMappings/* ---- sope-4.9.r1660.orig/debian/libsope-core_SOPEVER_-dev.install -+++ sope-4.9.r1660/debian/libsope-core_SOPEVER_-dev.install +--- sope-4.9.r1664.orig/debian/libsope-core_SOPEVER_-dev.install ++++ sope-4.9.r1664/debian/libsope-core_SOPEVER_-dev.install @@ -1,6 +1,6 @@ -usr/include/NGExtensions -usr/include/EOControl @@ -348,18 +355,18 @@ usr/lib/libNGExtensions.so usr/lib/libEOControl.so usr/lib/libNGStreams.so ---- sope-4.9.r1660.orig/debian/sope_SOPEVER_-stxsaxdriver.install -+++ sope-4.9.r1660/debian/sope_SOPEVER_-stxsaxdriver.install +--- sope-4.9.r1664.orig/debian/sope_SOPEVER_-stxsaxdriver.install ++++ sope-4.9.r1664/debian/sope_SOPEVER_-stxsaxdriver.install @@ -1 +1 @@ -usr/lib/sope-*/saxdrivers/STXSaxDriver.sax +usr/lib/GNUstep/SaxDrivers-*/STXSaxDriver.sax ---- sope-4.9.r1660.orig/debian/compat -+++ sope-4.9.r1660/debian/compat +--- sope-4.9.r1664.orig/debian/compat ++++ sope-4.9.r1664/debian/compat @@ -1 +1 @@ -4 +5 ---- sope-4.9.r1660.orig/debian/libsope-mime_SOPEVER_-dev.install -+++ sope-4.9.r1660/debian/libsope-mime_SOPEVER_-dev.install +--- sope-4.9.r1664.orig/debian/libsope-mime_SOPEVER_-dev.install ++++ sope-4.9.r1664/debian/libsope-mime_SOPEVER_-dev.install @@ -1,4 +1,4 @@ -usr/include/NGMime -usr/include/NGImap4 @@ -368,8 +375,8 @@ +usr/include/GNUstep/NGImap4 +usr/include/GNUstep/NGMail usr/lib/libNGMime.so ---- sope-4.9.r1660.orig/debian/copyright -+++ sope-4.9.r1660/debian/copyright +--- sope-4.9.r1664.orig/debian/copyright ++++ sope-4.9.r1664/debian/copyright @@ -8,19 +8,19 @@ License: @@ -397,8 +404,8 @@ On Debian systems, the complete text of the GNU Lesser General Public License can be found in `/usr/share/common-licenses/LGPL'. ---- sope-4.9.r1660.orig/debian/libsope-appserver_SOPEVER_-dev.install -+++ sope-4.9.r1660/debian/libsope-appserver_SOPEVER_-dev.install +--- sope-4.9.r1664.orig/debian/libsope-appserver_SOPEVER_-dev.install ++++ sope-4.9.r1664/debian/libsope-appserver_SOPEVER_-dev.install @@ -1,11 +1,10 @@ -usr/include/NGHttp -usr/include/NGObjWeb @@ -423,8 +430,8 @@ usr/lib/libSoOFS.so usr/lib/libWEPrototype.so +usr/share/GNUstep/Makefiles/* ---- sope-4.9.r1660.orig/debian/control -+++ sope-4.9.r1660/debian/control +--- sope-4.9.r1664.orig/debian/control ++++ sope-4.9.r1664/debian/control @@ -1,8 +1,9 @@ Source: sope +Section: web @@ -553,8 +560,8 @@ -Description: Apache2 module for the SOPE application server - This package contains an apache module which enables the apache2 - webserver to deliver pages generated by the SOPE application server. ---- sope-4.9.r1660.orig/debian/libsope-xml_SOPEVER_-dev.install -+++ sope-4.9.r1660/debian/libsope-xml_SOPEVER_-dev.install +--- sope-4.9.r1664.orig/debian/libsope-xml_SOPEVER_-dev.install ++++ sope-4.9.r1664/debian/libsope-xml_SOPEVER_-dev.install @@ -1,6 +1,6 @@ -usr/include/DOM -usr/include/SaxObjC @@ -565,25 +572,25 @@ usr/lib/libDOM.so usr/lib/libSaxObjC.so usr/lib/libXmlRpc.so ---- sope-4.9.r1660.orig/debian/sope_SOPEVER_-versitsaxdriver.install -+++ sope-4.9.r1660/debian/sope_SOPEVER_-versitsaxdriver.install +--- sope-4.9.r1664.orig/debian/sope_SOPEVER_-versitsaxdriver.install ++++ sope-4.9.r1664/debian/sope_SOPEVER_-versitsaxdriver.install @@ -1 +1 @@ -usr/lib/sope-*/saxdrivers/versitSaxDriver.sax +usr/lib/GNUstep/SaxDrivers-*/versitSaxDriver.sax ---- sope-4.9.r1660.orig/debian/libsope-gdl1-_SOPEVER_-dev.install -+++ sope-4.9.r1660/debian/libsope-gdl1-_SOPEVER_-dev.install +--- sope-4.9.r1664.orig/debian/libsope-gdl1-_SOPEVER_-dev.install ++++ sope-4.9.r1664/debian/libsope-gdl1-_SOPEVER_-dev.install @@ -1,2 +1,2 @@ -usr/include/GDLAccess +usr/include/GNUstep/GDLAccess usr/lib/libGDLAccess.so ---- sope-4.9.r1660.orig/debian/libsope-ldap_SOPEVER_-dev.install -+++ sope-4.9.r1660/debian/libsope-ldap_SOPEVER_-dev.install +--- sope-4.9.r1664.orig/debian/libsope-ldap_SOPEVER_-dev.install ++++ sope-4.9.r1664/debian/libsope-ldap_SOPEVER_-dev.install @@ -1,2 +1,2 @@ -usr/include/NGLdap +usr/include/GNUstep/NGLdap usr/lib/libNGLdap.so ---- sope-4.9.r1660.orig/debian/libsope-appserver_SOPEVER_.install -+++ sope-4.9.r1660/debian/libsope-appserver_SOPEVER_.install +--- sope-4.9.r1664.orig/debian/libsope-appserver_SOPEVER_.install ++++ sope-4.9.r1664/debian/libsope-appserver_SOPEVER_.install @@ -5,6 +5,6 @@ usr/lib/libNGXmlRpc.so.* usr/lib/libSoOFS.so.* @@ -594,8 +601,7737 @@ +usr/lib/GNUstep/Libraries/Resources/NGObjWeb/* +usr/lib/GNUstep/SoProducts-*/* +usr/lib/GNUstep/WOxElemBuilders-*/* ---- sope-4.9.r1660.orig/debian/patches/sope-gsmake2.diff -+++ sope-4.9.r1660/debian/patches/sope-gsmake2.diff +--- sope-4.9.r1664.orig/debian/patches/sope-patchset-r1664.diff ++++ sope-4.9.r1664/debian/patches/sope-patchset-r1664.diff +@@ -0,0 +1,7726 @@ ++Index: sope-ldap/NGLdap/NGLdapEntry.m ++=================================================================== ++--- sope-ldap/NGLdap/NGLdapEntry.m (revision 1664) +++++ sope-ldap/NGLdap/NGLdapEntry.m (working copy) ++@@ -105,14 +105,16 @@ ++ - (NGLdapAttribute *)attributeWithName:(NSString *)_name { ++ NSEnumerator *e; ++ NGLdapAttribute *a; ++- +++ NSString *upperName; +++ ++ if (_name == nil) ++ return nil; ++ +++ upperName = [_name uppercaseString]; ++ e = [self->attributes objectEnumerator]; ++ ++ while ((a = [e nextObject])) { ++- if ([[a attributeName] isEqualToString:_name]) +++ if ([[[a attributeName] uppercaseString] isEqualToString:upperName]) ++ return a; ++ } ++ return nil; ++Index: sope-ldap/NGLdap/ChangeLog ++=================================================================== ++--- sope-ldap/NGLdap/ChangeLog (revision 1664) +++++ sope-ldap/NGLdap/ChangeLog (working copy) ++@@ -1,3 +1,8 @@ +++2009-08-13 Wolfgang Sourdeau +++ +++ * NGLdapEntry.m (-attributeWithName:): attribute names are now +++ accessed in a case-insensitive way. +++ ++ 2009-04-02 Wolfgang Sourdeau ++ ++ * NGLdapConnection.m (useSSL,startTLS): new method enabling ++Index: sope-gdl1/PostgreSQL/PostgreSQL72Channel.m ++=================================================================== ++--- sope-gdl1/PostgreSQL/PostgreSQL72Channel.m (revision 1664) +++++ sope-gdl1/PostgreSQL/PostgreSQL72Channel.m (working copy) ++@@ -713,6 +713,39 @@ ++ return ms; ++ } ++ +++/* GCSEOAdaptorChannel protocol */ +++static NSString *sqlFolderFormat = (@"CREATE TABLE %@ (\n" \ +++ @" c_name VARCHAR (256) NOT NULL PRIMARY KEY,\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-gdl1/MySQL/MySQL4Channel.m ++=================================================================== ++--- sope-gdl1/MySQL/MySQL4Channel.m (revision 1664) +++++ sope-gdl1/MySQL/MySQL4Channel.m (working copy) ++@@ -755,6 +755,39 @@ ++ return pkey; ++ } ++ +++/* GCSEOAdaptorChannel protocol */ +++static NSString *sqlFolderFormat = (@"CREATE TABLE %@ (\n" \ +++ @" c_name VARCHAR (256) NOT NULL PRIMARY KEY,\n" +++ @" c_content VARCHAR (100000) NOT NULL,\n" +++ @" c_creationdate INT NOT NULL,\n" +++ @" c_lastmodified INT NOT NULL,\n" +++ @" c_version INT NOT NULL,\n" +++ @" c_deleted INT 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 /* MySQL4Channel */ ++ ++ void __link_MySQL4Channel() { ++Index: sope-gdl1/Oracle8/OracleAdaptorChannel.m ++=================================================================== ++--- sope-gdl1/Oracle8/OracleAdaptorChannel.m (revision 1664) +++++ sope-gdl1/Oracle8/OracleAdaptorChannel.m (working copy) ++@@ -1,7 +1,7 @@ ++ /* ++ ** OracleAdaptorChannel.m ++ ** ++-** Copyright (c) 2007 Inverse groupe conseil inc. and Ludovic Marcotte +++** Copyright (c) 2007-2009 Inverse inc. and Ludovic Marcotte ++ ** ++ ** Author: Ludovic Marcotte ++ ** ++@@ -30,6 +30,11 @@ ++ ++ #import ++ +++#include +++ +++static BOOL debugOn = NO; +++static int maxTry = 3; +++static int maxSleep = 500; ++ // ++ // ++ // ++@@ -41,10 +46,11 @@ ++ ++ @implementation OracleAdaptorChannel (Private) ++ ++-- (void) _cleanup +++- (void) _cleanup ++ { ++ column_info *info; ++ int c; +++ sword result; ++ ++ [_resultSetProperties removeAllObjects]; ++ ++@@ -58,11 +64,29 @@ ++ // so we just free the value instead. ++ if (info->value) ++ { ++- if (OCIDescriptorFree((dvoid *)info->value, (ub4)OCI_DTYPE_LOB) != OCI_SUCCESS) +++ if (info->type == SQLT_CLOB +++ || info->type == SQLT_BLOB +++ || info->type == SQLT_BFILEE +++ || info->type == SQLT_CFILEE) +++ { +++ result = OCIDescriptorFree((dvoid *)info->value, (ub4) OCI_DTYPE_LOB); +++ if (result != OCI_SUCCESS) +++ { +++ NSLog (@"value was not a LOB descriptor"); +++ abort(); +++ } +++ } +++ else ++ free(info->value); ++ info->value = NULL; ++ } ++- free(info); +++ else +++ { +++ NSLog (@"trying to free an already freed value!"); +++ abort(); +++ } +++ free(info); +++ ++ [_row_buffer removeObjectAtIndex: c]; ++ } ++ ++@@ -78,8 +102,7 @@ ++ // ++ @implementation OracleAdaptorChannel ++ ++-static void ++-DBTerminate() +++static void DBTerminate() ++ { ++ if (OCITerminate(OCI_DEFAULT)) ++ NSLog(@"FAILED: OCITerminate()"); ++@@ -89,6 +112,11 @@ ++ ++ + (void) initialize ++ { +++ NSUserDefaults *ud; +++ +++ ud = [NSUserDefaults standardUserDefaults]; +++ debugOn = [ud boolForKey: @"OracleAdaptorDebug"]; +++ ++ // We Initialize the OCI process environment. ++ if (OCIInitialize((ub4)OCI_DEFAULT, (dvoid *)0, ++ (dvoid * (*)(dvoid *, size_t)) 0, ++@@ -156,14 +184,17 @@ ++ [super closeChannel]; ++ ++ // We logoff from the database. ++- if (OCILogoff(_oci_ctx, _oci_err)) +++ if (!_oci_ctx || !_oci_err || OCILogoff(_oci_ctx, _oci_err)) ++ { ++ NSLog(@"FAILED: OCILogoff()"); ++ } ++ +++ if (_oci_ctx) +++ OCIHandleFree(_oci_ctx, OCI_HTYPE_SVCCTX); ++ ++- OCIHandleFree(_oci_ctx, OCI_HTYPE_SVCCTX); ++- OCIHandleFree(_oci_err, OCI_HTYPE_ERROR); +++ if (_oci_err) +++ OCIHandleFree(_oci_err, OCI_HTYPE_ERROR); +++ ++ // OCIHandleFree(_oci_env, OCI_HTYPE_ENV); ++ ++ _oci_ctx = (OCISvcCtx *)0; ++@@ -177,7 +208,8 @@ ++ // ++ - (void) dealloc ++ { ++- //NSLog(@"OracleAdaptorChannel: -dealloc"); +++ if (debugOn) +++ NSLog(@"OracleAdaptorChannel: -dealloc"); ++ ++ [self _cleanup]; ++ ++@@ -222,7 +254,7 @@ ++ { ++ EOAttribute *attribute; ++ OCIParam *param; ++- +++ int rCount; ++ column_info *info; ++ ub4 i, clen, count; ++ text *sql, *cname; ++@@ -231,6 +263,9 @@ ++ ++ [self _cleanup]; ++ +++ if (debugOn) +++ [self logWithFormat: @"expression: %@", theExpression]; +++ ++ if (!theExpression || ![theExpression length]) ++ { ++ [NSException raise: @"OracleInvalidExpressionException" ++@@ -244,7 +279,9 @@ ++ } ++ ++ sql = (text *)[theExpression UTF8String]; ++- +++ +++ rCount = 0; +++ retry: ++ // We alloc our statement handle ++ if ((status = OCIHandleAlloc((dvoid *)_oci_env, (dvoid **)&_current_stm, (ub4)OCI_HTYPE_STMT, (CONST size_t) 0, (dvoid **) 0))) ++ { ++@@ -264,13 +301,39 @@ ++ // We check if we're doing a SELECT and if so, we're fetching data! ++ OCIAttrGet(_current_stm, OCI_HTYPE_STMT, &type, 0, OCI_ATTR_STMT_TYPE, _oci_err); ++ self->isFetchInProgress = (type == OCI_STMT_SELECT ? YES : NO); ++- +++ ++ // We execute our statement. Not that we _MUST_ set iter to 0 for non-SELECT statements. ++ if ((status = OCIStmtExecute(_oci_ctx, _current_stm, _oci_err, (self->isFetchInProgress ? (ub4)0 : (ub4)1), (ub4)0, (CONST OCISnapshot *)NULL, (OCISnapshot *)NULL, ++ ([(OracleAdaptorContext *)[self adaptorContext] autoCommit] ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT)))) ++ { +++ ub4 serverStatus; +++ ++ checkerr(_oci_err, status); ++ NSLog(@"Statement execute failed (OCI_ERROR): %@", theExpression); +++ +++ // We check to see if we lost connection and need to reconnect. +++ serverStatus = 0; +++ OCIAttrGet((dvoid *)_oci_env, OCI_HTYPE_SERVER, (dvoid *)&serverStatus, (ub4 *)0, OCI_ATTR_SERVER_STATUS, _oci_err); +++ +++ if (serverStatus == OCI_SERVER_NOT_CONNECTED) +++ { +++ // We cleanup our previous handles +++ [self cancelFetch]; +++ [self closeChannel]; +++ +++ // We try to reconnect a couple of times before giving up... +++ while (rCount < maxTry) +++ { +++ usleep(maxSleep); +++ rCount++; +++ +++ if ([self openChannel]) +++ { +++ NSLog(@"Connection re-established to Oracle - retrying to process the statement."); +++ goto retry; +++ } +++ } +++ } ++ return NO; ++ } ++ ++@@ -302,7 +365,9 @@ ++ // We read the maximum width of a column ++ info->max_width = 0; ++ status = OCIAttrGet((dvoid*)param, (ub4)OCI_DTYPE_PARAM, (dvoid*)&(info->max_width), (ub4 *)0, (ub4)OCI_ATTR_DATA_SIZE, (OCIError *)_oci_err); ++- +++ +++ if (debugOn) +++ NSLog(@"name: %s, type: %d", cname, info->type); ++ attribute = [EOAttribute attributeWithOracleType: info->type name: cname length: clen width: info->max_width]; ++ [_resultSetProperties addObject: attribute]; ++ ++@@ -394,16 +459,17 @@ ++ return NO; ++ } ++ ++- ++ if (OCIEnvInit((OCIEnv **)&_oci_env, (ub4)OCI_DEFAULT, (size_t)0, (dvoid **)0)) ++ { ++ NSLog(@"FAILED: OCIEnvInit()"); +++ [self closeChannel]; ++ return NO; ++ } ++ ++ if (OCIHandleAlloc((dvoid *)_oci_env, (dvoid *)&_oci_err, (ub4)OCI_HTYPE_ERROR, (size_t)0, (dvoid **)0)) ++ { ++ NSLog(@"FAILED: OCIHandleAlloc() on errhp"); +++ [self closeChannel]; ++ return NO; ++ } ++ ++@@ -414,7 +480,10 @@ ++ // Under Oracle 10g, the third parameter of OCILogon() has the form: [//]host[:port][/service_name] ++ // See http://download-west.oracle.com/docs/cd/B12037_01/network.101/b10775/naming.htm#i498306 for ++ // all juicy details. ++- database = [[NSString stringWithFormat:@"%@:%@", [o serverName], [o port]] UTF8String]; +++ if ([o serverName] && [o port]) +++ database = [[NSString stringWithFormat:@"%@:%@/%@", [o serverName], [o port], [o databaseName]] UTF8String]; +++ else +++ database = [[o databaseName] UTF8String]; ++ ++ // We logon to the database. ++ if (OCILogon(_oci_env, _oci_err, &_oci_ctx, (const OraText*)username, strlen(username), ++@@ -422,6 +491,7 @@ ++ { ++ NSLog(@"FAILED: OCILogon(). username = %s password = %s" ++ @" database = %s", username, password, database); +++ [self closeChannel]; ++ return NO; ++ } ++ ++@@ -438,6 +508,11 @@ ++ { ++ sword status; ++ +++ // We check if our connection is open prior to trying to fetch any data. OCIStmtFetch2() returns +++ // NO error code if the OCI environment is set up but the OCILogon() has failed. +++ if (![self isOpen]) +++ return nil; +++ ++ status = OCIStmtFetch2(_current_stm, _oci_err, (ub4)1, (ub4)OCI_FETCH_NEXT, (sb4)0, (ub4)OCI_DEFAULT); ++ ++ if (status == OCI_NO_DATA) ++@@ -609,7 +684,7 @@ ++ ++ /* GCSEOAdaptorChannel protocol */ ++ static NSString *sqlFolderFormat = (@"CREATE TABLE %@ (\n" \ ++- @" c_name VARCHAR2 (256) NOT NULL,\n" +++ @" c_name VARCHAR2 (256) NOT NULL PRIMARY KEY,\n" ++ @" c_content CLOB NOT NULL,\n" ++ @" c_creationdate INTEGER NOT NULL,\n" ++ @" c_lastmodified INTEGER NOT NULL,\n" ++Index: sope-gdl1/Oracle8/OracleAdaptorChannelController.m ++=================================================================== ++--- sope-gdl1/Oracle8/OracleAdaptorChannelController.m (revision 1664) +++++ sope-gdl1/Oracle8/OracleAdaptorChannelController.m (working copy) ++@@ -31,6 +31,8 @@ ++ #import ++ #import ++ +++static BOOL debugOn = NO; +++ ++ // ++ // ++ // ++@@ -48,6 +50,14 @@ ++ // ++ @implementation OracleAdaptorChannelController ++ ++++ (void) initialize +++{ +++ NSUserDefaults *ud; +++ +++ ud = [NSUserDefaults standardUserDefaults]; +++ debugOn = [ud boolForKey: @"OracleAdaptorDebug"]; +++} +++ ++ - (EODelegateResponse) adaptorChannel: (id) theChannel ++ willInsertRow: (NSMutableDictionary *) theRow ++ forEntity: (EOEntity *) theEntity ++@@ -56,7 +66,8 @@ ++ NSArray *keys; ++ int i, c; ++ ++- NSLog(@"willInsertRow: %@ %@", [theRow description], [theEntity description]); +++ if (debugOn) +++ NSLog(@"willInsertRow: %@ %@", [theRow description], [theEntity description]); ++ ++ s = AUTORELEASE([[NSMutableString alloc] init]); ++ ++@@ -101,7 +112,8 @@ ++ NSArray *keys; ++ int i, c; ++ ++- NSLog(@"willUpdatetRow: %@ %@", [theRow description], [theQualifier description]); +++ if (debugOn) +++ NSLog(@"willUpdateRow: %@ %@", [theRow description], [theQualifier description]); ++ ++ s = AUTORELEASE([[NSMutableString alloc] init]); ++ ++Index: sope-mime/NGImap4/NGImap4Functions.m ++=================================================================== ++--- sope-mime/NGImap4/NGImap4Functions.m (revision 1664) +++++ sope-mime/NGImap4/NGImap4Functions.m (working copy) ++@@ -367,3 +367,16 @@ ++ } ++ ++ @end /* NGImap4FolderHandler */ +++ +++NSString * +++SaneFolderName(NSString *folderName) +++{ +++ NSString *saneFName; +++ +++ saneFName = [[folderName stringByReplacingString: @"\\" +++ withString: @"\\\\"] +++ stringByReplacingString: @"\"" +++ withString: @"\\\""]; +++ +++ return saneFName; +++} ++Index: sope-mime/NGImap4/NGImap4Client.h ++=================================================================== ++--- sope-mime/NGImap4/NGImap4Client.h (revision 1664) +++++ sope-mime/NGImap4/NGImap4Client.h (working copy) ++@@ -62,6 +62,8 @@ ++ NGImap4ResponseNormalizer *normer; ++ NSMutableArray *responseReceiver; ++ +++ BOOL loggedIn; +++ ++ BOOL isLogin; ++ unsigned tagId; ++ ++@@ -117,9 +119,11 @@ ++ - (NSDictionary *)noop; ++ ++ - (NSDictionary *)capability; +++- (NSDictionary *)namespace; ++ - (NSDictionary *)list:(NSString *)_folder pattern:(NSString *)_pattern; ++ - (NSDictionary *)lsub:(NSString *)_folder pattern:(NSString *)_pattern; ++ - (NSDictionary *)select:(NSString *)_folder; +++- (NSDictionary *)unselect; ++ - (NSDictionary *)status:(NSString *)_folder flags:(NSArray *)_flags; ++ - (NSDictionary *)rename:(NSString *)_folder to:(NSString *)_newName; ++ - (NSDictionary *)delete:(NSString *)_folder; ++@@ -138,7 +142,7 @@ ++ flags:(NSArray *)_flags; ++ - (NSDictionary *)storeFrom:(unsigned)_from to:(unsigned)_to ++ add:(NSNumber *)_add flags:(NSArray *)_flags; ++-- (NSDictionary *)storeFlags:(NSArray *)_flags forMSNs:(id)_msns +++- (NSDictionary *)storeFlags:(NSArray *)_flags forUIDs:(id)_uids ++ addOrRemove:(BOOL)_flag; ++ ++ - (NSDictionary *)copyUid:(unsigned)_uid toFolder:(NSString *)_folder; ++Index: sope-mime/NGImap4/NGImap4Client.m ++=================================================================== ++--- sope-mime/NGImap4/NGImap4Client.m (revision 1664) +++++ sope-mime/NGImap4/NGImap4Client.m (working copy) ++@@ -24,6 +24,8 @@ ++ #include "NGImap4Client.h" ++ #include "NGImap4Context.h" ++ #include "NGImap4Support.h" +++#include "NGImap4Envelope.h" +++#include "NGImap4EnvelopeAddress.h" ++ #include "NGImap4Functions.h" ++ #include "NGImap4ResponseParser.h" ++ #include "NGImap4ResponseNormalizer.h" ++@@ -53,17 +55,17 @@ ++ ++ @end /* NGImap4Client(ConnectionRegistration); */ ++ ++-#if GNUSTEP_BASE_LIBRARY ++-/* FIXME: TODO: move someplace better (hh: NGExtensions...) */ ++-@implementation NSException(setUserInfo) +++// #if GNUSTEP_BASE_LIBRARY +++// /* FIXME: TODO: move someplace better (hh: NGExtensions...) */ +++// @implementation NSException(setUserInfo) ++ ++-- (id)setUserInfo:(NSDictionary *)_userInfo { ++- ASSIGN(self->_e_info, _userInfo); ++- return self; ++-} +++// - (id)setUserInfo:(NSDictionary *)_userInfo { +++// ASSIGN(self->_e_info, _userInfo); +++// return self; +++// } ++ ++-@end /* NSException(setUserInfo) */ ++-#endif +++// @end /* NSException(setUserInfo) */ +++// #endif ++ ++ @interface NGImap4Client(Private) ++ ++@@ -84,6 +86,8 @@ ++ ++ - (NSDictionary *)login; ++ +++- (NSDictionary *) _sopeSORT: (id)_sortSpec qualifier:(EOQualifier *)_qual encoding:(NSString *)_encoding; +++ ++ @end ++ ++ /* ++@@ -110,6 +114,9 @@ ++ static BOOL ImapDebugEnabled = NO; ++ static NSArray *Imap4SystemFlags = nil; ++ +++static NSMutableDictionary *capabilities; +++static NSMutableDictionary *namespaces; +++ ++ - (BOOL)useSSL { ++ return self->useSSL; ++ } ++@@ -140,6 +147,9 @@ ++ ++ Imap4SystemFlags = [[NSArray alloc] initWithObjects: @"seen", @"answered", ++ @"deleted", @"draft", nil]; +++ +++ capabilities = [[NSMutableDictionary alloc] init]; +++ namespaces = [[NSMutableDictionary alloc] init]; ++ } ++ ++ /* constructors */ ++@@ -195,11 +205,14 @@ ++ self->debug = ImapDebugEnabled; ++ self->responseReceiver = [[NSMutableArray alloc] initWithCapacity:128]; ++ self->normer = [[NGImap4ResponseNormalizer alloc] initWithClient:self]; +++ self->loggedIn = NO; +++ self->context = nil; ++ } ++ return self; ++ } ++ ++ - (void)dealloc { +++ if (self->loggedIn) [self logout]; ++ [self removeFromConnectionRegister]; ++ [self->normer release]; ++ [self->text release]; ++@@ -457,8 +470,8 @@ ++ - (void)reconnect { ++ if ([self->context lastException] != nil) ++ return; ++- ++- [self closeConnection]; +++ +++ [self closeConnection]; ++ self->tagId = 0; ++ [self openConnection]; ++ ++@@ -481,6 +494,7 @@ ++ */ ++ NGHashMap *map; ++ NSString *s, *log; +++ NSDictionary *response; ++ ++ if (self->isLogin ) ++ return nil; ++@@ -499,7 +513,11 @@ ++ ++ self->isLogin = NO; ++ ++- return [self->normer normalizeResponse:map]; +++ response = [self->normer normalizeResponse:map]; +++ +++ self->loggedIn = [[response valueForKey:@"result"] boolValue]; +++ +++ return response; ++ } ++ ++ - (NSDictionary *)logout { ++@@ -508,6 +526,8 @@ ++ ++ map = [self processCommand:@"logout"]; ++ [self closeConnection]; +++ [self->selectedFolder release]; self->selectedFolder = nil; +++ self->loggedIn = NO; ++ ++ return [self->normer normalizeResponse:map]; ++ } ++@@ -530,7 +550,7 @@ ++ NSAutoreleasePool *pool; ++ NGHashMap *map; ++ NSDictionary *result; ++- NSString *s; +++ NSString *s, *prefix; ++ ++ pool = [[NSAutoreleasePool alloc] init]; ++ ++@@ -547,7 +567,11 @@ ++ if (!(_pattern = [self _folder2ImapFolder:_pattern])) ++ return nil; ++ ++- s = [NSString stringWithFormat:@"list \"%@\" \"%@\"", _folder, _pattern]; +++ if ([_folder length] > 0) +++ prefix = [NSString stringWithFormat: @"%@%@", SaneFolderName(_folder), self->delimiter]; +++ else +++ prefix = @""; +++ s = [NSString stringWithFormat:@"list \"%@\" \"%@\"", prefix, _pattern]; ++ map = [self processCommand:s]; ++ ++ if (self->delimiter == nil) { ++@@ -563,18 +587,49 @@ ++ } ++ ++ - (NSDictionary *)capability { +++ NSDictionary *result; ++ id capres; ++- capres = [self processCommand:@"capability"]; ++- return [self->normer normalizeCapabilityRespone:capres]; +++ +++ result = [capabilities objectForKey: [self->address description]]; +++ +++ if (!result) +++ { +++ capres = [self processCommand:@"capability"]; +++ result = [self->normer normalizeCapabilityResponse:capres]; +++ +++ if (result) +++ [capabilities setObject: result forKey: [self->address description]]; +++ } +++ return result; ++ } ++ +++- (NSDictionary *)namespace { +++ NSArray *capabilities; +++ NGHashMap *namesres; +++ id namespace; +++ +++ namespace = [namespaces objectForKey: [self->address description]]; +++ if (!namespace) { +++ capabilities = [[self capability] objectForKey: @"capability"]; +++ if ([capabilities containsObject: @"namespace"]) { +++ namesres = [self processCommand: @"namespace"]; +++ namespace = [self->normer normalizeNamespaceResponse:namesres]; +++ } +++ else +++ namespace = [NSNull null]; +++ [namespaces setObject: namespace forKey: [self->address description]]; +++ } +++ +++ return ([namespace isKindOfClass: [NSNull class]] ? nil : namespace); +++} +++ ++ - (NSDictionary *)lsub:(NSString *)_folder pattern:(NSString *)_pattern { ++ /* ++ The method build statements like 'LSUB "_folder" "_pattern"'. ++ The returnvalue is the same like the list:pattern: method ++ */ ++ NGHashMap *map; ++- NSString *s; +++ NSString *s, *prefix; ++ ++ if (_folder == nil) ++ _folder = @""; ++@@ -591,7 +646,11 @@ ++ return nil; ++ } ++ ++- s = [NSString stringWithFormat:@"lsub \"%@\" \"%@\"", _folder, _pattern]; +++ if ([_folder length] > 0) +++ prefix = [NSString stringWithFormat: @"%@%@", SaneFolderName(_folder), self->delimiter]; +++ else +++ prefix = @""; +++ s = [NSString stringWithFormat:@"lsub \"%@\" \"%@\"", prefix, _pattern]; ++ map = [self processCommand:s]; ++ ++ if (self->delimiter == nil) { ++@@ -617,24 +676,25 @@ ++ 'flags' - array of strings (eg (answered,flagged,draft,seen); ++ 'RawResponse' - the raw IMAP4 response ++ */ ++- NSString *s; ++- id tmp; ++- ++- tmp = self->selectedFolder; // remember ptr to old folder name ++- +++ NSString *s, *newFolder; +++ ++ if (![_folder isNotEmpty]) ++ return nil; ++ if ((_folder = [self _folder2ImapFolder:_folder]) == nil) ++ return nil; ++ ++- self->selectedFolder = [_folder copy]; ++- ++- [tmp release]; tmp = nil; // release old folder name +++ newFolder = [NSString stringWithString: _folder]; +++ ASSIGN (self->selectedFolder, newFolder); ++ ++- s = [NSString stringWithFormat:@"select \"%@\"", self->selectedFolder]; +++ s = [NSString stringWithFormat:@"select \"%@\"", SaneFolderName(self->selectedFolder)]; ++ return [self->normer normalizeSelectResponse:[self processCommand:s]]; ++ } ++ +++- (NSDictionary *)unselect { +++ [self->selectedFolder release]; self->selectedFolder = nil; +++ return [self->normer normalizeResponse:[self processCommand:@"unselect"]]; +++} +++ ++ - (NSDictionary *)status:(NSString *)_folder flags:(NSArray *)_flags { ++ NSString *cmd; ++ ++@@ -646,7 +706,7 @@ ++ return nil; ++ ++ cmd = [NSString stringWithFormat:@"status \"%@\" (%@)", ++- _folder, [_flags componentsJoinedByString:@" "]]; +++ SaneFolderName(_folder), [_flags componentsJoinedByString:@" "]]; ++ return [self->normer normalizeStatusResponse:[self processCommand:cmd]]; ++ } ++ ++@@ -663,24 +723,28 @@ ++ if ((_newName = [self _folder2ImapFolder:_newName]) == nil) ++ return nil; ++ ++- cmd = [NSString stringWithFormat:@"rename \"%@\" \"%@\"", _folder, _newName]; +++ cmd = [NSString stringWithFormat:@"rename \"%@\" \"%@\"", +++ SaneFolderName(_folder), SaneFolderName(_newName)]; ++ ++ return [self->normer normalizeResponse:[self processCommand:cmd]]; ++ } ++ ++ - (NSDictionary *)_performCommand:(NSString *)_op onFolder:(NSString *)_fname { ++ NSString *command; ++- +++ ++ if ((_fname = [self _folder2ImapFolder:_fname]) == nil) ++ return nil; ++- +++ ++ // eg: 'delete "blah"' ++- command = [NSString stringWithFormat:@"%@ \"%@\"", _op, _fname]; ++- +++ command = [NSString stringWithFormat:@"%@ \"%@\"", _op, SaneFolderName(_fname)]; +++ ++ return [self->normer normalizeResponse:[self processCommand:command]]; ++ } ++ ++ - (NSDictionary *)delete:(NSString *)_name { +++ if ([self->selectedFolder isEqualToString:_name]) { +++ [self unselect]; +++ } ++ return [self _performCommand:@"delete" onFolder:_name]; ++ } ++ - (NSDictionary *)create:(NSString *)_name { ++@@ -820,23 +884,23 @@ ++ return [self->normer normalizeResponse:[self processCommand:cmd]]; ++ } ++ ++-- (NSDictionary *)storeFlags:(NSArray *)_flags forMSNs:(id)_msns +++- (NSDictionary *)storeFlags:(NSArray *)_flags forUIDs:(id)_uids ++ addOrRemove:(BOOL)_flag ++ { ++ NSString *cmd; ++ NSString *flagstr; ++ NSString *seqstr; ++ ++- if ([_msns isKindOfClass:[NSArray class]]) { +++ if ([_uids isKindOfClass:[NSArray class]]) { ++ // TODO: improve by using ranges, eg 1:5 instead of 1,2,3,4,5 ++- _msns = [_msns valueForKey:@"stringValue"]; ++- seqstr = [_msns componentsJoinedByString:@","]; +++ _uids = [_uids valueForKey:@"stringValue"]; +++ seqstr = [_uids componentsJoinedByString:@","]; ++ } ++ else ++- seqstr = [_msns stringValue]; +++ seqstr = [_uids stringValue]; ++ ++ flagstr = [_flags2ImapFlags(self, _flags) componentsJoinedByString:@" "]; ++- cmd = [NSString stringWithFormat:@"store %@ %cFLAGS (%@)", +++ cmd = [NSString stringWithFormat:@"UID STORE %@ %cFLAGS (%@)", ++ seqstr, _flag ? '+' : '-', flagstr]; ++ ++ return [self->normer normalizeResponse:[self processCommand:cmd]]; ++@@ -967,11 +1031,12 @@ ++ descr = @"Could not process qualifier for imap search "; ++ descr = [descr stringByAppendingString:reason]; ++ ++- exception = [[NGImap4SearchException alloc] initWithFormat:@"%@", descr]; ++ ui = [NSDictionary dictionaryWithObject:_q forKey:@"qualifier"]; ++- [exception setUserInfo:ui]; +++ exception +++ = [NGImap4SearchException exceptionWithName: @"NGImap4SearchException" +++ reason: descr +++ userInfo: ui]; ++ [self->context setLastException:exception]; ++- [exception release]; ++ } ++ ++ - (NSString *)_searchExprForQual:(EOQualifier *)_qualifier { ++@@ -1093,7 +1158,18 @@ ++ Eg: UID SORT ( DATE REVERSE SUBJECT ) UTF-8 TODO ++ */ ++ NSString *tmp; +++ NSArray *capa; ++ +++ // We first check to see if our server supports IMAP SORT. If not +++ // we'll sort ourself the results. +++ capa = [[self capability] objectForKey: @"capability"]; +++ +++ if ([capa indexOfObject: @"sort"] == NSNotFound) +++ { +++ return [self _sopeSORT: _sortSpec qualifier: _qual encoding: _encoding]; +++ } +++ +++ ++ if ([_sortSpec isKindOfClass:[NSArray class]]) ++ tmp = [self _generateIMAP4SortOrderings:_sortSpec]; ++ else if ([_sortSpec isKindOfClass:[EOSortOrdering class]]) ++@@ -1107,9 +1183,10 @@ ++ tmp = @"DATE"; ++ } ++ +++ ++ return [self primarySort:tmp ++- qualifierString:[self _searchExprForQual:_qual] ++- encoding:_encoding]; +++ qualifierString:[self _searchExprForQual:_qual] +++ encoding:_encoding]; ++ } ++ - (NSDictionary *)sort:(NSArray *)_sortOrderings ++ qualifier:(EOQualifier *)_qual ++@@ -1130,7 +1207,7 @@ ++ return nil; ++ } ++ ++- s = [@"search" stringByAppendingString:s]; +++ s = [@"UID SEARCH" stringByAppendingString:s]; ++ return [self->normer normalizeSearchResponse:[self processCommand:s]]; ++ } ++ ++@@ -1142,7 +1219,7 @@ ++ if ((_folder = [self _folder2ImapFolder:_folder]) == nil) ++ return nil; ++ ++- cmd = [NSString stringWithFormat:@"getacl \"%@\"", _folder]; +++ cmd = [NSString stringWithFormat:@"getacl \"%@\"", SaneFolderName(_folder)]; ++ return [self->normer normalizeGetACLResponse:[self processCommand:cmd]]; ++ } ++ ++@@ -1155,7 +1232,7 @@ ++ return nil; ++ ++ cmd = [NSString stringWithFormat:@"setacl \"%@\" \"%@\" \"%@\"", ++- _folder, _uid, _r]; +++ SaneFolderName(_folder), _uid, _r]; ++ return [self->normer normalizeResponse:[self processCommand:cmd]]; ++ } ++ ++@@ -1166,7 +1243,7 @@ ++ return nil; ++ ++ cmd = [NSString stringWithFormat:@"deleteacl \"%@\" \"%@\"", ++- _folder, _uid]; +++ SaneFolderName(_folder), _uid]; ++ return [self->normer normalizeResponse:[self processCommand:cmd]]; ++ } ++ ++@@ -1177,7 +1254,7 @@ ++ return nil; ++ ++ cmd = [NSString stringWithFormat:@"listrights \"%@\" \"%@\"", ++- _folder, _uid]; +++ SaneFolderName(_folder), _uid]; ++ return [self->normer normalizeListRightsResponse:[self processCommand:cmd]]; ++ } ++ ++@@ -1187,12 +1264,94 @@ ++ if ((_folder = [self _folder2ImapFolder:_folder]) == nil) ++ return nil; ++ ++- cmd = [NSString stringWithFormat:@"myrights \"%@\"", _folder]; +++ cmd = [NSString stringWithFormat:@"myrights \"%@\"", SaneFolderName(_folder)]; ++ return [self->normer normalizeMyRightsResponse:[self processCommand:cmd]]; ++ } ++ ++ /* Private Methods */ ++ +++- (NSDictionary *) _sopeSORT: (id)_sortSpec qualifier:(EOQualifier *)_qual encoding:(NSString *)_encoding { +++ NSMutableDictionary *result; +++ NSDictionary *d; +++ NSCalendarDate *envDate; +++ +++ result = [NSMutableDictionary dictionary]; +++ [result setObject: [NSNumber numberWithBool: NO] forKey: @"result"]; +++ +++ // _sortSpec: [REVERSE] {DATE,FROM,SUBJECT} +++ d = [self searchWithQualifier: _qual]; +++ +++ if ((d = [d objectForKey: @"RawResponse"])) { +++ NSMutableDictionary *dict; +++ NSArray *a, *s_a; +++ BOOL b; +++ int i; +++ +++ a = [d objectForKey: @"search"]; +++ if ([a isNotEmpty]) { +++ d = [self fetchUids: a +++ parts: [NSArray arrayWithObjects: @"ENVELOPE", +++ @"RFC822.SIZE", nil]]; +++ a = [d objectForKey: @"fetch"]; +++ +++ dict = [NSMutableDictionary dictionary]; +++ b = YES; +++ +++ for (i = 0; i < [a count]; i++) { +++ NGImap4Envelope *env; +++ id o, uid, s; +++ +++ o = [a objectAtIndex: i]; +++ env = [o objectForKey: @"envelope"]; +++ uid = [o objectForKey: @"uid"]; +++ +++ if ([_sortSpec rangeOfString: @"SUBJECT"].length) { +++ s = [env subject]; +++ if ([s isKindOfClass: [NSData class]]) +++ s = [[[NSString alloc] initWithData: s encoding: NSUTF8StringEncoding] autorelease]; +++ +++ [dict setObject: (s != nil ? s : (id)@"") forKey: uid]; +++ } +++ else if ([_sortSpec rangeOfString: @"FROM"].length) { +++ s = [[[env from] lastObject] email]; +++ [dict setObject: (s != nil ? s : (id)@"") forKey: uid]; +++ } +++ else if ([_sortSpec rangeOfString: @"SIZE"].length) { +++ s = [o objectForKey: @"size"]; +++ [dict setObject: (s != nil ? s : [NSNumber numberWithInt: 0]) +++ forKey: uid]; +++ b = NO; +++ } +++ else { +++ envDate = [env date]; +++ if (!envDate) +++ envDate = [NSCalendarDate date]; +++ [dict setObject: envDate forKey: uid]; +++ b = NO; +++ } +++ } +++ +++ if (b) +++ s_a = [dict keysSortedByValueUsingSelector: @selector(caseInsensitiveCompare:)]; +++ else +++ s_a = [dict keysSortedByValueUsingSelector: @selector(compare:)]; +++ +++ if ([_sortSpec rangeOfString: @"REVERSE"].length) { +++ s_a = [[s_a reverseObjectEnumerator] allObjects]; +++ } +++ +++ } +++ else { +++ s_a = [NSArray array]; +++ } +++ [result setObject: [NSNumber numberWithBool: YES] forKey: @"result"]; +++ [result setObject: s_a forKey: @"sort"]; +++ } +++ +++ return result; +++} +++ +++ ++ - (NSException *)_processCommandParserException:(NSException *)_exception { ++ [self logWithFormat:@"ERROR(%s): catched IMAP4 parser exception %@: %@", ++ __PRETTY_FUNCTION__, [_exception name], [_exception reason]]; ++@@ -1412,21 +1571,24 @@ ++ return nil; ++ } ++ ++- array = [_folder pathComponents]; +++// array = [_folder pathComponents]; +++ array = [_folder componentsSeparatedByString:@"/"]; ++ ++- if ([array isNotEmpty]) { +++ if ([array count]) { ++ NSString *o; ++ ++ o = [array objectAtIndex:0]; ++- if (([o isEqualToString:@"/"]) || ([o length] == 0)) +++ if ([o length] == 0) ++ array = [array subarrayWithRange:NSMakeRange(1, [array count] - 1)]; ++- ++- o = [array lastObject]; ++- if (([o length] == 0) || ([o isEqualToString:@"/"])) ++- array = [array subarrayWithRange:NSMakeRange(0, [array count] - 1)]; +++ +++ if ([array count]) { +++ o = [array lastObject]; +++ if ([o length] == 0) +++ array = [array subarrayWithRange:NSMakeRange(0, [array count] - 1)]; +++ } ++ } ++ return [[array componentsJoinedByString:self->delimiter] ++- stringByEncodingImap4FolderName]; +++ stringByEncodingImap4FolderName]; ++ } ++ ++ - (NSString *)_imapFolder2Folder:(NSString *)_folder { ++@@ -1442,10 +1604,16 @@ ++ return nil; ++ } ++ +++ if ([_folder hasPrefix: self->delimiter]) +++ _folder = [_folder substringFromIndex: 1]; +++ if ([_folder hasSuffix: self->delimiter]) +++ _folder = [_folder substringToIndex: [_folder length] - 1]; +++ ++ array = [array arrayByAddingObjectsFromArray: ++ [_folder componentsSeparatedByString:[self delimiter]]]; ++- ++- return [[NSString pathWithComponents:array] stringByDecodingImap4FolderName]; +++ +++ return [[array componentsJoinedByString: @"/"] +++ stringByDecodingImap4FolderName]; ++ } ++ ++ - (void)setContext:(NGImap4Context *)_ctx { ++Index: sope-mime/NGImap4/NGSieveClient.m ++=================================================================== ++--- sope-mime/NGImap4/NGSieveClient.m (revision 1664) +++++ sope-mime/NGImap4/NGSieveClient.m (working copy) ++@@ -294,8 +294,8 @@ ++ return con; ++ } ++ ++- logLen = [self->login cStringLength]; ++- bufLen = (logLen * 2) + [self->password cStringLength] +2; +++ logLen = [self->login lengthOfBytesUsingEncoding: NSUTF8StringEncoding]; +++ bufLen = (logLen * 2) + [self->password lengthOfBytesUsingEncoding: NSUTF8StringEncoding] +2; ++ ++ buf = calloc(bufLen + 2, sizeof(char)); ++ ++@@ -306,8 +306,9 @@ ++ password ++ */ ++ sprintf(buf, "%s %s %s", ++- [self->login cString], [self->login cString], ++- [self->password cString]); +++ [self->login cStringUsingEncoding:NSUTF8StringEncoding], +++ [self->login cStringUsingEncoding:NSUTF8StringEncoding], +++ [self->password cStringUsingEncoding:NSUTF8StringEncoding]); ++ ++ buf[logLen] = '\0'; ++ buf[logLen * 2 + 1] = '\0'; ++@@ -656,7 +657,7 @@ ++ fputc('\n', stderr); ++ } ++ else ++- fprintf(stderr, "C: %s\n", [_txt cString]); +++ fprintf(stderr, "C: %s\n", [_txt cStringUsingEncoding:NSUTF8StringEncoding]); ++ } ++ ++ /* write */ ++Index: sope-mime/NGImap4/NGImap4Connection.h ++=================================================================== ++--- sope-mime/NGImap4/NGImap4Connection.h (revision 1664) +++++ sope-mime/NGImap4/NGImap4Connection.h (working copy) ++@@ -52,7 +52,7 @@ ++ NSString *separator; ++ ++ /* hierarchy cache */ ++- NSDictionary *subfolders; +++ NSMutableDictionary *subfolders; ++ ++ /* permission cache */ ++ NSMutableDictionary *urlToRights; ++@@ -72,8 +72,9 @@ ++ ++ - (NSDate *)creationTime; ++ ++-- (void)cacheHierarchyResults:(NSDictionary *)_hierarchy; ++-- (NSDictionary *)cachedHierarchyResults; +++- (void)cacheHierarchyResults:(NSDictionary *)_hierarchy +++ forURL:(NSURL *)_url; +++- (NSDictionary *)cachedHierarchyResultsForURL:(NSURL *)_url; ++ - (void)flushFolderHierarchyCache; ++ ++ - (id)cachedUIDsForURL:(NSURL *)_url qualifier:(id)_q sortOrdering:(id)_so; ++@@ -88,7 +89,12 @@ ++ /* folder operations */ ++ ++ - (NSArray *)subfoldersForURL:(NSURL *)_url; +++- (NSArray *)subfoldersForURL:(NSURL *)_url +++ onlySubscribedFolders: (BOOL) subscribedFoldersOnly; ++ - (NSArray *)allFoldersForURL:(NSURL *)_url; +++- (NSArray *)allFoldersForURL:(NSURL *)_url +++ onlySubscribedFolders: (BOOL) subscribedFoldersOnly; +++- (BOOL)selectFolder:(id)_url; ++ ++ /* message operations */ ++ ++Index: sope-mime/NGImap4/NGImap4ResponseNormalizer.h ++=================================================================== ++--- sope-mime/NGImap4/NGImap4ResponseNormalizer.h (revision 1664) +++++ sope-mime/NGImap4/NGImap4ResponseNormalizer.h (working copy) ++@@ -49,7 +49,8 @@ ++ - (NSDictionary *)normalizeSearchResponse:(NGHashMap *)_map; ++ - (NSDictionary *)normalizeSortResponse:(NGHashMap *)_map; ++ - (NSDictionary *)normalizeThreadResponse:(NGHashMap *)_map; ++-- (NSDictionary *)normalizeCapabilityRespone:(NGHashMap *)_map; +++- (NSDictionary *)normalizeCapabilityResponse:(NGHashMap *)_map; +++- (NSDictionary *)normalizeNamespaceResponse:(NGHashMap *)_map; ++ - (NSDictionary *)normalizeQuotaResponse:(NGHashMap *)_map; ++ ++ /* ACL */ ++Index: sope-mime/NGImap4/NGImap4Connection.m ++=================================================================== ++--- sope-mime/NGImap4/NGImap4Connection.m (revision 1664) +++++ sope-mime/NGImap4/NGImap4Connection.m (working copy) ++@@ -22,6 +22,7 @@ ++ #include "NGImap4Connection.h" ++ #include "NGImap4MailboxInfo.h" ++ #include "NGImap4Client.h" +++#include "NGImap4Functions.h" ++ #include "imCommon.h" ++ ++ @implementation NGImap4Connection ++@@ -66,7 +67,8 @@ ++ self->creationTime = [[NSDate alloc] init]; ++ ++ // TODO: retrieve from IMAP4 instead of using a default ++- self->separator = imap4Separator; +++ self->separator = [imap4Separator copy]; +++ self->subfolders = [NSMutableDictionary new]; ++ } ++ return self; ++ } ++@@ -100,11 +102,13 @@ ++ return self->creationTime; ++ } ++ ++-- (void)cacheHierarchyResults:(NSDictionary *)_hierarchy { ++- ASSIGNCOPY(self->subfolders, _hierarchy); +++- (void)cacheHierarchyResults:(NSDictionary *)_hierarchy +++ forURL:(NSURL *)_url +++{ +++ [self->subfolders setObject:_hierarchy forKey:[_url absoluteString]]; ++ } ++-- (NSDictionary *)cachedHierarchyResults { ++- return self->subfolders; +++- (NSDictionary *)cachedHierarchyResultsForURL:(NSURL *)_url { +++ return [self->subfolders objectForKey:[_url absoluteString]]; ++ } ++ - (void)flushFolderHierarchyCache { ++ [self->subfolders release]; self->subfolders = nil; ++@@ -152,7 +156,6 @@ ++ ASSIGN(self->cachedUIDs, nil); ++ } ++ ++- ++ /* errors */ ++ ++ - (NSException *)errorCouldNotSelectURL:(NSURL *)_url { ++@@ -215,18 +218,13 @@ ++ NSMutableArray *ma; ++ unsigned i, count, prefixlen; ++ ++- if ((count = [_array count]) < 2) { ++- /* one entry is the folder itself, so we need at least two */ ++- return [NSArray array]; ++- } +++ count = [_array count]; ++ ++ // TODO: somehow results are different on OSX ++ // we should investigate and test all Foundation libraries and document the ++ // differences ++ #if __APPLE__ ++ prefixlen = [_fn isEqualToString:@""] ? 0 : [_fn length] + 1; ++-#elif GNUSTEP_BASE_LIBRARY ++- prefixlen = [_fn isEqualToString:@"/"] ? 1 : [_fn length]; ++ #else ++ prefixlen = [_fn isEqualToString:@"/"] ? 1 : [_fn length] + 1; ++ #endif ++@@ -321,13 +319,15 @@ ++ return nil; ++ if ([folderName characterAtIndex:0] == '/') ++ folderName = [folderName substringFromIndex:1]; +++ if ([folderName hasSuffix: @"/"]) +++ folderName = [folderName substringToIndex:[folderName length] - 1]; ++ ++ if (_delfn) folderName = [folderName stringByDeletingLastPathComponent]; ++ ++ if ([[self imap4Separator] isEqualToString:@"/"]) ++ return folderName; ++ ++- names = [folderName pathComponents]; +++ names = [folderName componentsSeparatedByString: @"/"]; ++ return [names componentsJoinedByString:[self imap4Separator]]; ++ } ++ - (NSString *)imap4FolderNameForURL:(NSURL *)_url { ++@@ -373,16 +373,26 @@ ++ ++ /* folder operations */ ++ ++-- (NSDictionary *)primaryFetchMailboxHierarchyForURL:(NSURL *)_url { +++- (NSDictionary *)primaryFetchMailboxHierarchyForURL:(NSURL *)_url +++ onlySubscribedFolders:(BOOL) subscribedFoldersOnly +++{ ++ NSDictionary *result; +++ NSString *prefix; ++ ++- if ((result = [self cachedHierarchyResults]) != nil) +++ if ((result = [self cachedHierarchyResultsForURL:_url]) != nil) ++ return [result isNotNull] ? result : (NSDictionary *)nil; ++ ++ if (debugCache) [self logWithFormat:@" no folders cached yet .."]; ++- ++- result = [[self client] list:(onlyFetchInbox ? @"INBOX" : @"*") ++- pattern:@"*"]; +++ +++ prefix = [_url path]; +++ if ([prefix hasPrefix: @"/"]) +++ prefix = [prefix substringFromIndex:1]; +++ if (subscribedFoldersOnly) +++ result = [[self client] lsub:(onlyFetchInbox ? @"INBOX" : prefix) +++ pattern:@"*"]; +++ else +++ result = [[self client] list:(onlyFetchInbox ? @"INBOX" : prefix) +++ pattern:@"*"]; ++ if (![[result valueForKey:@"result"] boolValue]) { ++ [self errorWithFormat:@"Could not list mailbox hierarchy!"]; ++ return nil; ++@@ -391,7 +401,7 @@ ++ /* cache results */ ++ ++ if ([result isNotNull]) { ++- [self cacheHierarchyResults:result]; +++ [self cacheHierarchyResults:result forURL:_url]; ++ if (debugCache) { ++ [self logWithFormat:@"cached results: 0x%p(%d)", ++ result, [result count]]; ++@@ -400,32 +410,55 @@ ++ return result; ++ } ++ ++-- (NSArray *)subfoldersForURL:(NSURL *)_url { +++- (NSDictionary *)primaryFetchMailboxHierarchyForURL:(NSURL *)_url +++{ +++ return [self primaryFetchMailboxHierarchyForURL: _url onlySubscribedFolders: NO]; +++} +++ +++- (NSArray *)allFoldersForURL:(NSURL *)_url +++ onlySubscribedFolders:(BOOL)_subscribedFoldersOnly +++{ ++ NSDictionary *result; ++ ++- if ((result = [self primaryFetchMailboxHierarchyForURL:_url]) == nil) +++ if ((result = [self primaryFetchMailboxHierarchyForURL:_url +++ onlySubscribedFolders:_subscribedFoldersOnly]) == nil) ++ return nil; ++ if ([result isKindOfClass:[NSException class]]) { ++ [self errorWithFormat:@"failed to retrieve hierarchy: %@", result]; ++ return nil; ++ } ++ ++- return [self extractSubfoldersForURL:_url fromResultSet:result]; +++ return [self extractFoldersFromResultSet:result]; ++ } ++ ++-- (NSArray *)allFoldersForURL:(NSURL *)_url { +++- (NSArray *)allFoldersForURL:(NSURL *)_url +++{ +++ return [self allFoldersForURL: _url onlySubscribedFolders: NO]; +++} +++ +++- (NSArray *)subfoldersForURL:(NSURL *)_url +++ onlySubscribedFolders:(BOOL)_subscribedFoldersOnly +++{ ++ NSDictionary *result; +++ NSString *baseFolder; ++ ++- if ((result = [self primaryFetchMailboxHierarchyForURL:_url]) == nil) +++ baseFolder = [self imap4FolderNameForURL:_url removeFileName:NO]; +++ if (_subscribedFoldersOnly) +++ result = [[self client] lsub:baseFolder pattern:@"%"]; +++ else +++ result = [[self client] list:baseFolder pattern:@"%"]; +++ if (![[result valueForKey:@"result"] boolValue]) { +++ [self errorWithFormat:@"Could not list mailbox hierarchy!"]; ++ return nil; ++- if ([result isKindOfClass:[NSException class]]) { ++- [self errorWithFormat:@"failed to retrieve hierarchy: %@", result]; ++- return nil; ++ } ++- ++- return [self extractFoldersFromResultSet:result]; +++ +++ return [self extractSubfoldersForURL:_url fromResultSet: result]; ++ } ++ +++- (NSArray *)subfoldersForURL:(NSURL *)_url { +++ return [self subfoldersForURL:_url onlySubscribedFolders: NO]; +++} +++ ++ /* message operations */ ++ ++ - (NSArray *)fetchUIDsInURL:(NSURL *)_url qualifier:(id)_qualifier ++@@ -646,7 +679,7 @@ ++ ++ /* store flags */ ++ ++- result = [[self client] storeFlags:_f forMSNs:result addOrRemove:YES]; +++ result = [[self client] storeFlags:_f forUIDs:result addOrRemove:YES]; ++ if (![[result valueForKey:@"result"] boolValue]) { ++ return [self errorForResult:result ++ text:@"Failed to change flags of IMAP4 message"]; ++@@ -737,14 +770,17 @@ ++ ++ - (BOOL)doesMailboxExistAtURL:(NSURL *)_url { ++ NSString *folderName; +++ NSArray *caches; ++ id result; +++ int count, max; ++ ++ /* check in hierarchy cache */ ++- ++- if ((result = [self cachedHierarchyResults]) != nil) { +++ caches = [self->subfolders allValues]; +++ max = [caches count]; +++ for (count = 0; count < max; count++) { ++ NSString *p; ++- ++- result = [(NSDictionary *)result objectForKey:@"list"]; +++ +++ result = [[caches objectAtIndex: count] objectForKey:@"list"]; ++ p = [_url path]; ++ #if __APPLE__ ++ /* normalized results already have the / in front on libFoundation?! */ ++@@ -760,11 +796,11 @@ ++ // TODO: we should probably just fetch the whole hierarchy? ++ ++ folderName = [self imap4FolderNameForURL:_url]; ++- result = [[self client] select:folderName]; ++- if (![[result valueForKey:@"result"] boolValue]) ++- return NO; ++- ++- return YES; +++ +++ result = [self->client status: folderName +++ flags: [NSArray arrayWithObject: @"UIDVALIDITY"]]; +++ +++ return ([[result valueForKey: @"result"] boolValue]); ++ } ++ ++ - (id)infoForMailboxAtURL:(NSURL *)_url { ++@@ -789,7 +825,8 @@ ++ /* construct path */ ++ ++ newPath = [self imap4FolderNameForURL:_url]; ++- newPath = [newPath stringByAppendingString:[self imap4Separator]]; +++ if ([newPath length]) +++ newPath = [newPath stringByAppendingString:[self imap4Separator]]; ++ newPath = [newPath stringByAppendingString:_mailbox]; ++ ++ /* create */ ++Index: sope-mime/NGImap4/NGImap4ResponseNormalizer.m ++=================================================================== ++--- sope-mime/NGImap4/NGImap4ResponseNormalizer.m (revision 1664) +++++ sope-mime/NGImap4/NGImap4ResponseNormalizer.m (working copy) ++@@ -76,22 +76,6 @@ ++ return self; ++ } ++ ++-/* client callbacks */ ++- ++-- (void)closeConnection { ++- [(id)self->client closeConnection]; ++-} ++- ++-- (NSString *)delimiter { ++- return [self->client delimiter]; ++-} ++- ++-/* folder handling */ ++- ++-- (NSString *)_imapFolder2Folder:(NSString *)_folder { ++- return [self->client _imapFolder2Folder:_folder]; ++-} ++- ++ /* primary */ ++ ++ - (NSMutableDictionary *)normalizeResponse:(NGHashMap *)_map { ++@@ -117,7 +101,7 @@ ++ if ((obj = [_map objectForKey:@"bye"])) { ++ [result setObject:NoNumber forKey:@"result"]; ++ [result setObject:obj forKey:@"reason"]; ++- [self closeConnection]; +++ [self->client closeConnection]; ++ return result; ++ } ++ ++@@ -157,7 +141,7 @@ ++ return result; ++ } ++ ++-- (NSDictionary *)normalizeCapabilityRespone:(NGHashMap *)_map { +++- (NSDictionary *)normalizeCapabilityResponse:(NGHashMap *)_map { ++ /* filter for capability response: capability : NSArray */ ++ id obj; ++ NSMutableDictionary *result; ++@@ -170,6 +154,51 @@ ++ return result; ++ } ++ +++- (NSArray *)_normalizeNamespace:(NSArray *)_namespace { +++ NSMutableArray *result; +++ NSDictionary *currentNS; +++ NSMutableDictionary *newNS; +++ NSString *newPrefix; +++ int count, max; +++ +++ max = [_namespace count]; +++ result = [NSMutableArray arrayWithCapacity: max]; +++ for (count = 0; count < max; count++) { +++ currentNS = [_namespace objectAtIndex: count]; +++ newNS = [currentNS mutableCopy]; +++ newPrefix = [self->client +++ _imapFolder2Folder: [currentNS objectForKey: @"prefix"]]; +++ [newNS setObject: newPrefix forKey: @"prefix"]; +++ [result addObject: newNS]; +++ [newNS release]; +++ } +++ +++ return result; +++} +++ +++- (NSDictionary *)normalizeNamespaceResponse:(NGHashMap *)_map { +++ NSMutableDictionary *result; +++ NSDictionary *rawResponse; +++ NSArray *namespace; +++ +++ result = [self normalizeResponse:_map]; +++ rawResponse = [result objectForKey: @"RawResponse"]; +++ namespace = [rawResponse objectForKey: @"personal"]; +++ if (namespace) +++ [result setObject: [self _normalizeNamespace: namespace] +++ forKey: @"personal"]; +++ namespace = [rawResponse objectForKey: @"other users"]; +++ if (namespace) +++ [result setObject: [self _normalizeNamespace: namespace] +++ forKey: @"other users"]; +++ namespace = [rawResponse objectForKey: @"shared"]; +++ if (namespace) +++ [result setObject: [self _normalizeNamespace: namespace] +++ forKey: @"shared"]; +++ +++ return result; +++} +++ ++ - (NSDictionary *)normalizeThreadResponse:(NGHashMap *)_map { ++ /* filter for thread response: thread : NSArray (msn) */ ++ id obj; ++@@ -292,7 +321,7 @@ ++ /* ++ filter for fetch response ++ fetch : NSArray (fetch responses) ++- 'header' - RFC822.HEADER +++ 'header' - RFC822.HEADER and BODY[HEADER.FIELDS (...)] ++ 'text' - RFC822.TEXT ++ 'size' - SIZE ++ 'flags' - FLAGS ++@@ -336,7 +365,12 @@ ++ switch (c) { ++ case 'b': ++ /* Note: we check for _prefix_! eg body[1] is valid too */ ++- if (klen > 3 && [key hasPrefix:@"body"]) { +++ if (klen > 17 && [key hasPrefix:@"body[header.fields"]) { +++ keys[count] = @"header"; +++ values[count] = objForKey(obj, @selector(objectForKey:), key); +++ count++; +++ } +++ else if (klen > 3 && [key hasPrefix:@"body"]) { ++ keys[count] = @"body"; ++ values[count] = objForKey(obj, @selector(objectForKey:), key); ++ count++; ++@@ -516,7 +550,7 @@ ++ } ++ continue; ++ } ++- [tmp setObject:qDesc forKey:[self _imapFolder2Folder:obj]]; +++ [tmp setObject:qDesc forKey:[self->client _imapFolder2Folder:obj]]; ++ } ++ [result setObject:tmp forKey:@"quotas"]; ++ return [[result copy] autorelease]; ++@@ -615,7 +649,7 @@ ++ ++ while ((o = [enumerator nextObject])) { ++ [folder setObject:_imapFlags2Flags(self, [o objectForKey:@"flags"]) ++- forKey:[self _imapFolder2Folder:[o objectForKey:@"folderName"]]]; +++ forKey:[self->client _imapFolder2Folder:[o objectForKey:@"folderName"]]]; ++ } ++ ++ { ++@@ -648,14 +682,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/EOQualifier+IMAPAdditions.m ++=================================================================== ++--- sope-mime/NGImap4/EOQualifier+IMAPAdditions.m (revision 1664) +++++ sope-mime/NGImap4/EOQualifier+IMAPAdditions.m (working copy) ++@@ -53,13 +53,13 @@ ++ if (FlagKeyWords) return; ++ ++ ud = [NSUserDefaults standardUserDefaults]; ++- FlagKeyWords = [[NSArray alloc] initWithObjects: @"answered", @"deleted", ++- @"draft", @"flagged", @"new", @"old", @"recent", ++- @"seen", @"unanswered", @"undeleted", @"undraft", ++- @"unflagged", @"unseen", nil]; ++- OtherKeyWords = [[NSArray alloc] initWithObjects: ++- @"bcc", @"body", @"cc", @"from", @"subject", ++- @"text", @"to", @"keyword", @"unkeyword", nil]; +++ FlagKeyWords = [[NSArray alloc] initWithObjects: @"ANSWERED", @"DELETED", +++ @"DRAFT", @"FLAGGED", @"NEW", @"OLD", @"RECENT", +++ @"SEEN", @"UNANSWERED", @"UNDELETED", @"UNDRAFT", +++ @"UNFLAGGED", @"UNSEEN", nil]; +++ OtherKeyWords = [[NSArray alloc] initWithObjects: @"ALL", @"BCC", @"BODY", +++ @"CC", @"FROM", @"SUBJECT", @"TEXT", @"TO", +++ @"KEYWORD", @"UID", @"UNKEYWORD", nil]; ++ ++ debugOn = [ud boolForKey:@"ImapDebugQualifierGeneration"]; ++ } ++@@ -266,10 +266,10 @@ ++ ++ enumerator = [lvalue objectEnumerator]; ++ while ((lvalue = [enumerator nextObject]) != nil) { ++- lvalue = [lvalue lowercaseString]; +++ lvalue = [lvalue uppercaseString]; ++ ++ if ([FlagKeyWords containsObject:lvalue]) { ++- if (insertNot) [search appendString:@"not "]; +++ if (insertNot) [search appendString:@"NOT "]; ++ [search appendString:lvalue]; ++ } ++ else { ++@@ -280,15 +280,31 @@ ++ return nil; ++ } ++ ++-- (NSString *)imap4OperatorForDateComparisonSelector:(SEL)lselector { +++- (NSString *)imap4OperatorForDateKeyword:(NSString *)dkey +++andComparisonSelector:(SEL)lselector { +++ NSString *operatorPrefix, *dateOperator, *imap4Operator; +++ ++ if (sel_eq(lselector, EOQualifierOperatorEqual)) ++- return @" senton "; ++- if (sel_eq(lselector, EOQualifierOperatorGreaterThan)) ++- return @" sentsince "; ++- if (sel_eq(lselector, EOQualifierOperatorLessThan)) ++- return @" sentbefore "; ++- ++- return nil; +++ dateOperator = @"ON"; +++ else if (sel_eq(lselector, EOQualifierOperatorGreaterThan)) +++ dateOperator = @"SINCE"; +++ else if (sel_eq(lselector, EOQualifierOperatorLessThan)) +++ dateOperator = @"BEFORE"; +++ else +++ dateOperator = nil; +++ +++ if (dateOperator) { +++ if ([dkey isEqualToString: @"DATE"]) +++ operatorPrefix = @"SENT"; +++ else +++ operatorPrefix = @""; +++ imap4Operator = [NSString stringWithFormat: @"%@%@ ", +++ operatorPrefix, dateOperator]; +++ } +++ else +++ imap4Operator = nil; +++ +++ return imap4Operator; ++ } ++ ++ - (NSException *)appendToImap4SearchString:(NSMutableString *)search ++@@ -300,11 +316,11 @@ ++ id lvalue; ++ SEL lselector; ++ ++- lkey = [[self key] lowercaseString]; +++ lkey = [[self key] uppercaseString]; ++ lvalue = [self value]; ++ lselector = [self selector]; ++ ++- if ([lkey isEqualToString:@"flags"]) { +++ if ([lkey isEqualToString:@"FLAGS"]) { ++ /* NOTE: special "not" processing! */ ++ return [self appendFlagsCheckToImap4SearchString:search ++ insertNot:insertNot]; ++@@ -312,9 +328,9 @@ ++ ++ /* not a flag */ ++ if (insertNot) ++- [search appendString:@"not "]; +++ [search appendString:@"NOT "]; ++ ++- if ([lkey isEqualToString:@"date"]) { +++ if ([lkey isEqualToString:@"DATE"] || [lkey isEqualToString:@"RECEIVE-DATE"]) { ++ NSString *s; ++ ++ if (![lvalue isKindOfClass:[NSCalendarDate class]]) { ++@@ -322,35 +338,38 @@ ++ @"expected a NSDate as value"]; ++ } ++ ++- if ((s = [self imap4OperatorForDateComparisonSelector:lselector]) == nil) +++ if ((s = [self imap4OperatorForDateKeyword:lkey +++ andComparisonSelector:lselector]) == nil) ++ return [self invalidImap4SearchQualifier:@"unexpected selector"]; ++ ++- // TODO: operator created but NOT added? +++ [search appendString:s]; ++ ++ // TODO: much faster without descriptionWithCalendarFormat:?! ++- s = [lvalue descriptionWithCalendarFormat:@"%d-%b-%Y"]; +++ s = [lvalue descriptionWithCalendarFormat:@"\"%d-%b-%Y\""]; ++ [search appendString:s]; ++ return nil; ++ } ++ ++- if ([lkey isEqualToString:@"uid"]) { ++- if (!sel_eq(lselector, EOQualifierOperatorEqual)) +++ if ([lkey isEqualToString:@"UID"]) { +++ if (!sel_eq(lselector, EOQualifierOperatorEqual)) { ++ return [self invalidImap4SearchQualifier:@"unexpected qualifier 2"]; +++ } ++ ++- [search appendString:@"uid "]; +++ [search appendString:@"UID "]; ++ [search appendString:[lvalue stringValue]]; ++ return nil; ++ } ++ ++- if ([lkey isEqualToString:@"size"]) { +++ if ([lkey isEqualToString:@"SIZE"]) { ++ if (sel_eq(lselector, EOQualifierOperatorGreaterThan)) ++- [search appendString:@"larger "]; +++ [search appendString:@"LARGER "]; ++ else if (sel_eq(lselector, EOQualifierOperatorLessThan)) ++- [search appendString:@"smaller "]; +++ [search appendString:@"SMALLER "]; ++ else ++ return [self invalidImap4SearchQualifier:@"unexpected qualifier 3"]; ++ ++ [search appendString:[lvalue stringValue]]; +++ ++ return nil; ++ } ++ ++@@ -386,7 +405,7 @@ ++ if (!sel_eq(lselector, EOQualifierOperatorEqual)) ++ return [self invalidImap4SearchQualifier:@"unexpected qualifier 5"]; ++ ++- [search appendString:@"header "]; +++ [search appendString:@"HEADER "]; ++ [search appendString:lkey]; ++ [search appendString:@" \""]; ++ [search appendString:[lvalue stringValue]]; ++Index: sope-mime/NGImap4/NGImap4ResponseParser.m ++=================================================================== ++--- sope-mime/NGImap4/NGImap4ResponseParser.m (revision 1664) +++++ sope-mime/NGImap4/NGImap4ResponseParser.m (working copy) ++@@ -31,6 +31,7 @@ ++ @interface NGImap4ResponseParser(ParsingPrivates) ++ - (BOOL)_parseNumberUntaggedResponse:(NGMutableHashMap *)result_; ++ - (NSDictionary *)_parseBodyContent; +++- (NSData *) _parseBodyHeaderFields; ++ ++ - (NSData *)_parseData; ++ ++@@ -38,6 +39,7 @@ ++ - (void)_parseContinuationResponseIntoHashMap:(NGMutableHashMap *)result_; ++ - (BOOL)_parseListOrLSubResponseIntoHashMap:(NGMutableHashMap *)result_; ++ - (BOOL)_parseCapabilityResponseIntoHashMap:(NGMutableHashMap *)result_; +++- (BOOL)_parseNamespaceResponseIntoHashMap:(NGMutableHashMap *)result_; ++ - (BOOL)_parseSearchResponseIntoHashMap:(NGMutableHashMap *)result_; ++ - (BOOL)_parseSortResponseIntoHashMap:(NGMutableHashMap *)result_; ++ - (BOOL)_parseQuotaRootResponseIntoHashMap:(NGMutableHashMap *)result_; ++@@ -84,6 +86,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 +115,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); ++@@ -488,6 +493,50 @@ ++ return [self _parseDataIntoRAM:size]; ++ } ++ +++/* +++ Similair to _parseData but used to parse something like this : +++ +++ BODY[HEADER.FIELDS (X-PRIORITY)] {17} +++ X-Priority: 1 +++ +++ ) +++ +++ Headers are returned as data, as is. +++*/ +++- (NSData *) _parseBodyHeaderFields +++{ +++ NSData *result; +++ unsigned size; +++ NSNumber *sizeNum; +++ +++ /* we skip until we're ready to parse {length} */ +++ _parseUntil(self, '{'); +++ +++ result = nil; +++ +++ if ((sizeNum = _parseUnsigned(self)) == nil) { +++ NSException *e; +++ +++ e = [[NGImap4ParserException alloc] +++ initWithFormat:@"expect a number between {}"]; +++ [self setLastException:[e autorelease]]; +++ return nil; +++ } +++ _consumeIfMatch(self, '}'); +++ _consumeIfMatch(self, '\n'); +++ +++ if ((size = [sizeNum intValue]) == 0) { +++ [self logWithFormat:@"ERROR(%s): got content size '0'!", +++ __PRETTY_FUNCTION__]; +++ return nil; +++ } +++ +++ if (UseMemoryMappedData && (size > Imap4MMDataBoundary)) +++ return [self _parseDataToFile:size]; +++ +++ return [self _parseDataIntoRAM:size]; +++} +++ ++ static int _parseTaggedResponse(NGImap4ResponseParser *self, ++ NGMutableHashMap *result_) ++ { ++@@ -584,6 +633,10 @@ ++ break; ++ ++ case 'N': +++ if (_matchesString(self, "NAMESPACE")) { +++ if ([self _parseNamespaceResponseIntoHashMap:result_]) +++ return; +++ } ++ if (_parseNoUntaggedResponse(self, result_)) // la: 2 ++ return; ++ break; ++@@ -648,14 +701,171 @@ ++ [result_ addObject:_parseUntil(self, '\n') forKey:@"description"]; ++ } ++ +++static inline void +++_purifyQuotedString(NSMutableString *quotedString) { +++ unichar *currentChar, *qString, *maxC, *startC; +++ unsigned int max, questionMarks; +++ BOOL possiblyQuoted, skipSpaces; +++ NSMutableString *newString; +++ +++ newString = [NSMutableString string]; +++ +++ max = [quotedString length]; +++ qString = malloc (sizeof (unichar) * max); +++ [quotedString getCharacters: qString]; +++ currentChar = qString; +++ startC = qString; +++ maxC = qString + max; +++ +++ possiblyQuoted = NO; +++ skipSpaces = NO; +++ +++ questionMarks = 0; +++ +++ while (currentChar < maxC) { +++ if (possiblyQuoted) { +++ if (questionMarks == 2) { +++ if ((*currentChar == 'Q' || *currentChar == 'q' +++ || *currentChar == 'B' || *currentChar == 'b') +++ && ((currentChar + 1) < maxC +++ && (*(currentChar + 1) == '?'))) { +++ currentChar++; +++ questionMarks = 3; +++ } +++ else { +++ possiblyQuoted = NO; +++ } +++ } +++ else if (questionMarks == 4) { +++ if (*currentChar == '=') { +++ skipSpaces = YES; +++ possiblyQuoted = NO; +++ currentChar++; +++ [newString appendString: [NSString stringWithCharacters: startC +++ length: (currentChar - startC)]]; +++ startC = currentChar; +++ } +++ else { +++ possiblyQuoted = NO; +++ } +++ } +++ else { +++ if (*currentChar == '?') { +++ questionMarks++; +++ } +++ else if (*currentChar == ' ' && questionMarks != 3) { +++ possiblyQuoted = NO; +++ } +++ } +++ } +++ else if (*currentChar == '=' +++ && ((currentChar + 1) < maxC +++ && (*(currentChar + 1) == '?'))) { +++ [newString appendString: [NSString stringWithCharacters: startC +++ length: (currentChar - startC)]]; +++ startC = currentChar; +++ possiblyQuoted = YES; +++ skipSpaces = NO; +++ currentChar++; +++ questionMarks = 1; +++ } +++ +++ currentChar++; +++ +++ if (skipSpaces) { +++ while (currentChar < maxC +++ && (*currentChar == ' ' +++ || *currentChar == '\t')) +++ currentChar++; +++ skipSpaces = NO; +++ startC = currentChar; +++ } +++ } +++ +++ if (startC < maxC) +++ [newString appendString: [NSString stringWithCharacters: startC +++ length: (currentChar - startC)]]; +++ +++ [quotedString setString: newString]; +++ free (qString); +++} +++ ++ - (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; +++ } +++ } ++ } +++ else { +++ quotedString = nil; +++ } +++ +++ _purifyQuotedString(quotedString); +++ +++ return quotedString; +++} +++- (NSString *)_parseQuotedStringOrNIL { +++ unsigned char c0; +++ +++ if ((c0 = _la(self, 0)) == '"') +++ return [self _parseQuotedString]; +++ +++ if (c0 == '{') { +++ /* a size indicator, eg '{112}\nkasdjfkja sdj fhj hasdfj hjasdf' */ +++ NSData *data; +++ NSString *s; +++ +++ if ((data = [self _parseData]) == nil) +++ return nil; +++ if (![data isNotEmpty]) +++ return @""; +++ +++ s = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; +++ if (s == nil) +++ s = [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding]; +++ if (s == nil) { +++ [self logWithFormat: +++ @"ERROR(%s): could not convert data (%d bytes) into string.", +++ __PRETTY_FUNCTION__, [data length]]; +++ return @"[ERROR: NGImap4 could not parse IMAP4 data string]"; +++ } +++ return [s autorelease]; +++ } +++ +++ if (c0 == 'N' && _matchesString(self, "NIL")) { +++ _consume(self, 3); +++ return (id)null; +++ } ++ return nil; ++ } +++- (id)_parseQuotedStringOrDataOrNIL { +++ if (_la(self, 0) == '"') +++ return [self _parseQuotedString]; +++ if (_la(self, 0) == '{') +++ return [self _parseData]; +++ +++ if (_matchesString(self, "NIL")) { +++ _consume(self, 3); +++ return null; +++ } +++ return nil; +++} ++ - (void)_consumeOptionalSpace { ++ if (_la(self, 0) == ' ') _consume(self, 1); ++ } ++@@ -685,6 +895,10 @@ ++ name = [self _parseQuotedString]; ++ _parseUntil(self, '\n'); ++ } +++ else if (_la(self, 0) == '{') { +++ name = [self _parseQuotedStringOrNIL]; +++ _parseUntil(self, '\n'); +++ } ++ else ++ name = _parseUntil(self, '\n'); ++ ++@@ -723,6 +937,85 @@ ++ return YES; ++ } ++ +++/* support for NAMESPACE extension - RFC2342 */ +++ +++- (NSDictionary *)_parseNamespacePart { +++ NSDictionary *namespacePart; +++ NSString *prefix, *key, *delimiter; +++ NSMutableDictionary *parameters; +++ NSMutableArray *values; +++ +++ _consume(self, 1); /* ( */ +++ prefix = [self _parseQuotedStringOrNIL]; /* "prefix" */ +++ _consume(self, 1); /* */ +++ delimiter = [self _parseQuotedStringOrNIL]; /* "delimiter" */ +++ parameters = [NSMutableDictionary dictionary]; +++ while (_la(self, 0) == ' ') { +++ _consume(self, 1); /* */ +++ key = [self _parseQuotedString]; +++ _consume(self, 1); /* */ +++ values = [NSMutableArray new]; +++ while (_la(self, 0) != ')') { +++ _consume(self, 1); /* ( or */ +++ [values addObject: [self _parseQuotedString]]; +++ } +++ _consume(self, 1); /* ) */ +++ [parameters setObject: values forKey: key]; +++ [values release]; +++ } +++ _consume(self, 1); /* ) */ +++ +++ namespacePart = [NSDictionary dictionaryWithObjectsAndKeys: +++ prefix, @"prefix", +++ delimiter, @"delimiter", +++ parameters, @"parameters", +++ nil]; +++ +++ return namespacePart; +++} +++ +++- (NSArray *)_parseNamespace { +++ NSMutableArray *namespace; +++ +++ namespace = [[NSMutableArray alloc] initWithCapacity: 3]; +++ if (_la(self, 0) == 'N') { +++ namespace = nil; +++ _consume(self, 3); +++ } else { +++ _consume(self, 1); /* ( */ +++ while (_la(self, 0) == '(') { +++ [namespace addObject: [self _parseNamespacePart]]; +++ } +++ _consume(self, 1); /* ) */ +++ } +++ +++ return namespace; +++} +++ +++- (BOOL)_parseNamespaceResponseIntoHashMap:(NGMutableHashMap *)result_ { +++ NSArray *namespace; +++ +++ if (!_matchesString(self, "NAMESPACE ")) +++ return NO; +++ +++ _parseUntil(self, ' '); +++ +++ namespace = [self _parseNamespace]; +++ if (namespace) +++ [result_ addObject:namespace forKey:@"personal"]; +++ _consume(self, 1); +++ namespace = [self _parseNamespace]; +++ if (namespace) +++ [result_ addObject:namespace forKey:@"other users"]; +++ _consume(self, 1); +++ namespace = [self _parseNamespace]; +++ if (namespace) +++ [result_ addObject:namespace forKey:@"shared"]; +++ _consume(self, 1); /* \n */ +++ +++ return YES; +++} +++ ++ - (BOOL)_parseACLResponseIntoHashMap:(NGMutableHashMap *)result_ { ++ /* ++ 21 GETACL INBOX ++@@ -1030,10 +1323,15 @@ ++ _consume(self, 7); ++ ++ if (_la(self, 0) == '"') { ++- _consume(self, 1); ++- name = _parseUntil(self, '"'); +++ name = [self _parseQuotedString]; +++// _consume(self, 1); +++// name = _parseUntil(self, '"'); ++ _consumeIfMatch(self, ' '); ++ } +++ else if (_la(self, 0) == '{') { +++ name = [self _parseQuotedStringOrNIL]; +++ _consumeIfMatch(self, ' '); +++ } ++ else { ++ name = _parseUntil(self, ' '); ++ } ++@@ -1073,51 +1371,6 @@ ++ return YES; ++ } ++ ++-- (NSString *)_parseQuotedStringOrNIL { ++- unsigned char c0; ++- ++- if ((c0 = _la(self, 0)) == '"') ++- return [self _parseQuotedString]; ++- ++- if (c0 == '{') { ++- /* a size indicator, eg '{112}\nkasdjfkja sdj fhj hasdfj hjasdf' */ ++- NSData *data; ++- NSString *s; ++- ++- if ((data = [self _parseData]) == nil) ++- return nil; ++- if (![data isNotEmpty]) ++- return @""; ++- ++- s = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; ++- if (s == nil) { ++- [self logWithFormat: ++- @"ERROR(%s): could not convert data (%d bytes) into string.", ++- __PRETTY_FUNCTION__, [data length]]; ++- return @"[ERROR: NGImap4 could not parse IMAP4 data string]"; ++- } ++- return [s autorelease]; ++- } ++- ++- if (c0 == 'N' && _matchesString(self, "NIL")) { ++- _consume(self, 3); ++- return (id)null; ++- } ++- return nil; ++-} ++-- (id)_parseQuotedStringOrDataOrNIL { ++- if (_la(self, 0) == '"') ++- return [self _parseQuotedString]; ++- if (_la(self, 0) == '{') ++- return [self _parseData]; ++- ++- if (_matchesString(self, "NIL")) { ++- _consume(self, 3); ++- return null; ++- } ++- return nil; ++-} ++- ++ - (id)_decodeQP:(id)_string headerField:(NSString *)_field { ++ if (![_string isNotNull]) ++ return _string; ++@@ -1185,7 +1438,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 +1450,7 @@ ++ address = [[NGImap4EnvelopeAddress alloc] initWithPersonalName:pname ++ sourceRoute:route mailbox:mailbox ++ host:host]; +++ ++ return address; ++ } ++ ++@@ -1382,7 +1636,15 @@ ++ #if 0 ++ [self logWithFormat:@"PARSE KEY: %@", key]; ++ #endif ++- if ([key hasPrefix:@"body["]) { +++ if ([key hasPrefix:@"body[header.fields"]) { +++ NSData *content; +++ +++ if ((content = [self _parseBodyHeaderFields]) != nil) +++ [fetch setObject:content forKey:key]; +++ else +++ [self logWithFormat:@"ERROR: got no body content for key: '%@'",key]; +++ } +++ else if ([key hasPrefix:@"body["]) { ++ NSDictionary *content; ++ ++ if ((content = [self _parseBodyContent]) != nil) ++@@ -1594,8 +1856,11 @@ ++ if (_decode) ++ data = [data decodeQuotedPrintableValueOfMIMEHeaderField:nil]; ++ ++- return [[[StrClass alloc] initWithData:data encoding:encoding] ++- autorelease]; +++ if ([data isKindOfClass: [NSString class]]) +++ return (NSString *) data; +++ else +++ return [[[StrClass alloc] initWithData:data encoding:encoding] +++ autorelease]; ++ } ++ else { ++ str = _parseUntil2(self, ' ', ')'); ++@@ -1620,13 +1885,35 @@ ++ return str; ++ } ++ ++- ++ static NSString *_parseBodyString(NGImap4ResponseParser *self, ++ BOOL _convertString) ++ { ++ 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; ++@@ -1646,7 +1933,7 @@ ++ _consumeIfMatch(self, ' '); ++ value = _parseBodyDecodeString(self, YES, YES); ++ ++- [list setObject:value forKey:[key lowercaseString]]; +++ if (value) [list setObject:value forKey:[key lowercaseString]]; ++ } ++ _consumeIfMatch(self, ')'); ++ } ++@@ -1731,13 +2018,14 @@ ++ static NSDictionary *_parseSingleBody(NGImap4ResponseParser *self, ++ BOOL isBodyStructure) { ++ NSString *type, *subtype, *bodyId, *description, ++- *encoding, *bodysize; +++ *result, *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,13 +2050,18 @@ ++ _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, '('); ++- [dict setObject:_parseBodyString(self, YES) forKey:@"date"]; +++ result = _parseBodyString(self, YES); +++ if (result == nil) result = @""; +++ [dict setObject:result forKey:@"date"]; ++ _consumeIfMatch(self, ' '); ++- [dict setObject:_parseBodyString(self, YES) forKey:@"subject"]; +++ result = _parseBodyString(self, YES); +++ if (result == nil) result = @""; +++ [dict setObject:result forKey:@"subject"]; ++ _consumeIfMatch(self, ' '); ++ [dict setObject:_parseParenthesizedAddressList(self) forKey:@"from"]; ++ _consumeIfMatch(self, ' '); ++@@ -1783,14 +2076,20 @@ ++ _consumeIfMatch(self, ' '); ++ [dict setObject:_parseParenthesizedAddressList(self) forKey:@"bcc"]; ++ _consumeIfMatch(self, ' '); ++- [dict setObject:_parseBodyString(self, YES) forKey:@"in-reply-to"]; +++ result = _parseBodyString(self, YES); +++ if (result == nil) result = @""; +++ [dict setObject:result forKey:@"in-reply-to"]; ++ _consumeIfMatch(self, ' '); ++- [dict setObject:_parseBodyString(self, YES) forKey:@"messageId"]; +++ result = _parseBodyString(self, YES); +++ if (result == nil) result = @""; +++ [dict setObject:result forKey:@"messageId"]; ++ _consumeIfMatch(self, ')'); ++ _consumeIfMatch(self, ' '); ++ [dict setObject:_parseBody(self, isBodyStructure) forKey:@"body"]; ++ _consumeIfMatch(self, ' '); ++- [dict setObject:_parseBodyString(self, YES) forKey:@"bodyLines"]; +++ result = _parseBodyString(self, YES); +++ if (result == nil) result = @""; +++ [dict setObject:result forKey:@"bodyLines"]; ++ } ++ } ++ ++@@ -1805,14 +2104,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 +2123,7 @@ ++ static NSDictionary *_parseMultipartBody(NGImap4ResponseParser *self, ++ BOOL isBodyStructure) { ++ NSMutableArray *parts; +++ NSArray *languages; ++ NSString *kind; ++ NSMutableDictionary *dict; ++ ++@@ -1854,14 +2149,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 +2460,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 ++ { ++@@ -2225,9 +2530,9 @@ ++ [s release]; ++ ++ if (c == '\n') { ++- if ([self->serverResponseDebug cStringLength] > 2) { +++ if ([self->serverResponseDebug lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding] > 2) { ++ fprintf(stderr, "S[%p]: %s", self, ++- [self->serverResponseDebug cString]); +++ [self->serverResponseDebug cStringUsingEncoding:NSISOLatin1StringEncoding]); ++ } ++ [self->serverResponseDebug release]; ++ self->serverResponseDebug = ++Index: sope-mime/NGImap4/ChangeLog ++=================================================================== ++--- sope-mime/NGImap4/ChangeLog (revision 1664) +++++ sope-mime/NGImap4/ChangeLog (working copy) ++@@ -1,3 +1,92 @@ +++2010-01-05 Wolfgang Sourdeau +++ +++ * NGImap4ResponseParser.m (_parseUntaggedResponse): now accepts +++ the "NAMESPACE" response and parse accordingly by making use of +++ the appropriate new method. +++ +++ * NGImap4ResponseNormalizer.m (-normalizeNamespaceResponse): +++ self-explicit new method. +++ +++ * NGImap4Connection.m (-subfolderForURL:, -allFoldersForURL:): +++ differenciate both methods by having "subfolder..." use the "%" +++ wild card and "allFolders" the "*" wildcard. +++ (-cachedHierarchyResultsForURL): now accepts a url parameter so +++ in order to maintain multiple caches depending on the queried +++ namespace. +++ (SOGoMailGetDirectChildren): if the array count is < 2, we must +++ not return since certain implementations may not return the +++ current folder. +++ +++ * NGImap4Client.m (-namespace): new method implementing the +++ "NAMESPACE" command. +++ (-lsub:pattern:): we now sanitize the "prefix" particle of the +++ LSUB command. +++ +++2009-11-25 Wolfgang Sourdeau +++ +++ * NSString+Imap4.m (_encodeToModifiedUTF7): handle the case where +++ the leftOver is 0 by "chance" after the first 2 cycles. This +++ can happen when coding characters having a bitmask with 6 zeroes +++ in a row. +++ +++2009-10-06 Wolfgang Sourdeau +++ +++ * NGImap4Client.m (-delete:): if the folder we want to delete is +++ the same as self->selectedFolder, we unselect it first to ensure a +++ "SELECT" happens if a new folder with the same name is created. +++ +++ * EOQualifier+IMAPAdditions.m +++ (-imap4OperatorForDateKeyword:andComparisonSelector:): modified +++ operator handler to handle "receive-date" search key as well as +++ "date", prefixing the real filter with "sent" or not. +++ +++2009-09-22 Wolfgang Sourdeau +++ +++ * NGImap4Client.m (_sopeSORT:qualifier:encoding:): added support +++ for sorting by message size. +++ +++2009-07-01 Wolfgang Sourdeau +++ +++ * NGImap4Connection.m (-initWithClient:password:): we need to copy +++ the imap4Separator, otherwise it will be released when the connection +++ is deallocated. +++ +++2009-06-15 Wolfgang Sourdeau +++ +++ * NSString+Imap4.m (-stringByEncodingImap4FolderName, +++ -stringByDecodingImap4FolderName): reimplemented the original +++ methods in a unicode-safe way, thereby simplifying the code at the +++ same time. +++ +++ * NGImap4Functions.m (SaneFolderName): new function designed to +++ sanitize folder names prior to using them in IMAP commands. +++ +++2008-10-23 Wolfgang Sourdeau +++ +++ * NGImap4Client.m ([NGImap -sort:qualifier:encoding:]): message +++ without date that are sorted on servers which do not have the SORT +++ capability are now given the current date as a work-around. +++ +++2008-09-22 Wolfgang Sourdeau +++ +++ * NGImap4Connection.m ([NGImap -doesMailboxExistAtURL:]): restore +++ the previously selected folder state. +++ +++2008-09-19 Wolfgang Sourdeau +++ +++ * NGImap4Client.m ([NGImap -select:]): simplified method by +++ removing the need for storing the previous folder before releasing +++ it. This strangely seems to fix a crash with gnustep 1.14. +++ +++2008-09-01 Ludovic Marcotte +++ +++ * NGImap4ConnectionManager.m: implemented _garbageCollect. +++ +++2008-08-28 Wolfgang Sourdeau +++ +++ * NGImap4Client.m ([NGImap -unselect]): new method to send +++ "UNSELECT" to the imap server. +++ ++ 2007-08-24 Wolfgang Sourdeau ++ ++ * NGImap4Connection.m: some fix for folders ending with a slash (OGo ++Index: sope-mime/NGImap4/NGImap4ConnectionManager.m ++=================================================================== ++--- sope-mime/NGImap4/NGImap4ConnectionManager.m (revision 1664) +++++ sope-mime/NGImap4/NGImap4ConnectionManager.m (working copy) ++@@ -38,6 +38,9 @@ ++ debugCache = [ud boolForKey:@"NGImap4EnableIMAP4CacheDebug"]; ++ poolingOff = [ud boolForKey:@"NGImap4DisableIMAP4Pooling"]; ++ +++ if ([ud objectForKey:@"NGImap4PoolingCleanupInterval"]) +++ PoolScanInterval = [[ud objectForKey:@"NGImap4PoolingCleanupInterval"] doubleValue]; +++ ++ if (debugOn) NSLog(@"Note: NGImap4EnableIMAP4Debug is enabled!"); ++ if (poolingOff) NSLog(@"WARNING: IMAP4 connection pooling is disabled!"); ++ } ++@@ -53,18 +56,17 @@ ++ if ((self = [super init])) { ++ if (!poolingOff) { ++ self->urlToEntry = [[NSMutableDictionary alloc] initWithCapacity:256]; +++ self->gcTimer = [[NSTimer scheduledTimerWithTimeInterval: +++ PoolScanInterval +++ target:self selector:@selector(_garbageCollect:) +++ userInfo:nil repeats:YES] retain]; ++ } ++- ++- self->gcTimer = [[NSTimer scheduledTimerWithTimeInterval: ++- PoolScanInterval ++- target:self selector:@selector(_garbageCollect:) ++- userInfo:nil repeats:YES] retain]; ++ } ++ return self; ++ } ++ ++ - (void)dealloc { ++- if (self->gcTimer) [self->gcTimer invalidate]; +++ [self->gcTimer invalidate]; ++ [self->urlToEntry release]; ++ [self->gcTimer release]; ++ [super dealloc]; ++@@ -91,6 +93,25 @@ ++ ++ - (void)_garbageCollect:(NSTimer *)_timer { ++ // TODO: scan for old IMAP4 channels +++ NGImap4Connection *entry; +++ NSDate *now; +++ NSArray *a; +++ int i; +++ +++ a = [self->urlToEntry allKeys]; +++ now = [NSDate date]; +++ +++ for (i = 0; i < [a count]; i++) +++ { +++ entry = [self->urlToEntry objectForKey: [a objectAtIndex: i]]; +++ +++ if ([now timeIntervalSinceDate: [entry creationTime]] > PoolScanInterval) +++ { +++ [[entry client] logout]; +++ [self->urlToEntry removeObjectForKey: [a objectAtIndex: i]]; +++ } +++ } +++ ++ [self debugWithFormat:@"should collect IMAP4 channels (%d active)", ++ [self->urlToEntry count]]; ++ } ++@@ -105,34 +126,42 @@ ++ NGImap4Connection *entry; ++ NGImap4Client *client; ++ +++ if (poolingOff) { +++ client = [self imap4ClientForURL:_url password:_p]; +++ entry = [[NGImap4Connection alloc] initWithClient:client +++ password:_p]; +++ return [entry autorelease]; +++ } +++ else { ++ /* check cache */ ++ ++- if ((entry = [self entryForURL:_url]) != nil) { ++- if ([entry isValidPassword:_p]) { +++ if ((entry = [self entryForURL:_url]) != nil) { +++ if ([entry isValidPassword:_p]) { +++ if (debugCache) +++ [self logWithFormat:@"valid password, reusing cache entry ..."]; +++ return entry; +++ } +++ +++ /* different password, password could have changed! */ ++ if (debugCache) ++- [self logWithFormat:@"valid password, reusing cache entry ..."]; ++- return entry; +++ [self logWithFormat:@"different password than cached entry: %@", _url]; +++ entry = nil; ++ } ++- ++- /* different password, password could have changed! */ ++- if (debugCache) ++- [self logWithFormat:@"different password than cached entry: %@", _url]; ++- entry = nil; ++- } ++- else ++- [self debugWithFormat:@"no connection cached yet for url: %@", _url]; +++ else +++ [self debugWithFormat:@"no connection cached yet for url: %@", _url]; ++ ++- /* try to login */ +++ /* try to login */ ++ ++- client = [entry isValidPassword:_p] ++- ? [entry client] ++- : [self imap4ClientForURL:_url password:_p]; +++ client = [entry isValidPassword:_p] +++ ? [entry client] +++ : [self imap4ClientForURL:_url password:_p]; +++ +++ if (client == nil) +++ return nil; ++ ++- if (client == nil) ++- return nil; ++- ++ /* sideeffect of -imap4ClientForURL:password: is to create a cache entry */ ++- return [self entryForURL:_url]; +++ return [self entryForURL:_url]; +++ } ++ } ++ ++ /* client object */ ++Index: sope-mime/NGImap4/NSString+Imap4.m ++=================================================================== ++--- sope-mime/NGImap4/NSString+Imap4.m (revision 1664) +++++ sope-mime/NGImap4/NSString+Imap4.m (working copy) ++@@ -20,117 +20,86 @@ ++ 02111-1307, USA. ++ */ ++ +++#import +++ ++ #include ++ #include "imCommon.h" ++ ++-/* TODO: NOT UNICODE SAFE (uses cString) */ ++- ++-static void _encodeToModifiedUTF7(unsigned char *_buf, int encLen, ++- unsigned char **result_, ++- unsigned int *cntRes_); ++-static int _decodeOfModifiedUTF7(unsigned char *_target, unsigned _targetLen, ++- unsigned *usedBytes_ , ++- unsigned char **buffer_, ++- int *bufLen_, int maxBuf); ++- ++ @implementation NSString(Imap4) ++ +++static unsigned int _encodeToModifiedUTF7(unichar *_char, unsigned char *result_, +++ unsigned int *cntRes_); +++static unsigned int _decodeOfModifiedUTF7(unsigned char *_source, unichar *result_, +++ unsigned int *cntRes_ ); +++ ++ - (NSString *)stringByEncodingImap4FolderName { ++- // TBD: this is restricted to Latin1, should be fixed to UTF-8 ++- /* dude.d& --> dude.d&- */ ++- unsigned char *buf = NULL; +++ unichar *buf = NULL; ++ unsigned char *res = NULL; ++ unsigned int len = 0; ++ unsigned int cnt = 0; ++ unsigned int cntRes = 0; ++ NSString *result = nil; ++- NSData *data; ++ ++- len = [self cStringLength]; ++- buf = calloc(len + 3, sizeof(char)); ++- res = calloc((len * 6) + 3, sizeof(char)); ++- buf[len] = '\0'; ++- res[len * 6] = '\0'; ++- [self getCString:(char *)buf]; +++ len = [self length]; +++ buf = NSZoneMalloc(NULL, (len + 1) * sizeof(unichar)); +++ [self getCharacters: buf]; +++ buf[len] = 0; ++ +++ /* 1 * '&', 3 for the max bytes / char, 1 * '-' */ +++ res = NSZoneMalloc(NULL, ((len * 5) + 1) * sizeof(char)); +++ ++ while (cnt < len) { ++- int c = buf[cnt]; +++ unichar c = buf[cnt]; ++ if (((c > 31) && (c < 38)) || ++ ((c > 38) && (c < 127))) { ++ res[cntRes++] = c; ++- cnt++; ++ } ++ else { ++ if (c == '&') { ++ res[cntRes++] = '&'; ++ res[cntRes++] = '-'; ++- cnt++; ++ } ++ else { ++- int start; ++- ++- start = cnt; ++- ++- while (cnt < (len - 1)) { ++- int c = buf[cnt + 1]; ++- if (((c > 31) && (c < 38)) || ++- ((c > 38) && (c < 127)) || ++- (c == '&')) { ++- break; ++- } ++- else { ++- cnt++; ++- } ++- } ++- { ++- unsigned length; ++- ++- res[cntRes++] = '&'; ++- ++- length = cnt - start + 1; ++- ++- _encodeToModifiedUTF7(buf + start, length, &res, &cntRes); ++- ++- res[cntRes] = '-'; ++- cntRes++; ++- cnt++; ++- } +++ res[cntRes++] = '&'; +++ cnt += _encodeToModifiedUTF7(buf + cnt, res + cntRes, &cntRes); +++ res[cntRes++] = '-'; ++ } ++ } +++ cnt++; ++ } ++- if (buf != NULL) free(buf); buf = NULL; +++ if (buf != NULL) NSZoneFree(NULL, buf); ++ ++- data = [[NSData alloc] initWithBytesNoCopy:res length:cntRes ++- freeWhenDone:YES]; ++- result = [[NSString alloc] initWithData:data ++- encoding:NSISOLatin1StringEncoding]; ++- [data release]; data = nil; ++- ++- return [result autorelease]; +++ res[cntRes] = 0; +++ result = [NSString stringWithCString: (char *) res +++ encoding: NSISOLatin1StringEncoding]; +++ +++ return result; ++ } ++ ++ - (NSString *)stringByDecodingImap4FolderName { ++- // TBD: this is restricted to Latin1, should be fixed to UTF-8 ++- /* dude/d&- --> dude/d& */ ++ unsigned char *buf; ++- unsigned char *res; +++ unichar *res; ++ unsigned int len; ++ unsigned int cnt = 0; ++ unsigned int cntRes = 0; ++ NSString *result = nil; ++- NSData *data; +++// NSData *data; ++ ++- if ((len = [self cStringLength]) == 0) +++ if ((len = [self lengthOfBytesUsingEncoding: NSISOLatin1StringEncoding]) == 0) ++ return @""; ++- ++- buf = calloc(len + 3, sizeof(unsigned char)); ++- res = calloc(len + 3, sizeof(unsigned char)); +++ +++ buf = NSZoneMalloc(NULL, (len + 1) * sizeof(unsigned char)); +++ +++ if ([self getCString:(char *)buf maxLength: len + 1 +++ encoding: NSISOLatin1StringEncoding] == NO) { +++ NSZoneFree(NULL, buf); +++ return @""; +++ } ++ buf[len] = '\0'; ++- res[len] = '\0'; ++- ++- [self getCString:(char *)buf]; ++- ++- while (cnt < (len - 1)) { /* &- */ +++ +++ res = NSZoneMalloc(NULL, (len + 1) * sizeof(unichar)); +++ +++ while (cnt < len) { /* &- */ ++ unsigned char c; ++ ++ c = buf[cnt]; ++@@ -141,29 +110,7 @@ ++ cnt += 2; ++ } ++ else { ++- unsigned usedBytes = 0; ++- unsigned char *buffer; ++- int maxBuf, bufLen; ++- ++- cnt++; ++- maxBuf = 511; ++- bufLen = 0; ++- buffer = calloc(maxBuf + 3, sizeof(char)); ++- ++- if (_decodeOfModifiedUTF7(buf + cnt, len - cnt, &usedBytes , &buffer, ++- &bufLen, maxBuf) == 0) { ++- int cnt1; ++- ++- cnt1 = 0; ++- while (cnt1 < bufLen) { ++- res[cntRes++] = buffer[cnt1++]; ++- } ++- cnt += usedBytes; ++- } ++- else { ++- NSCAssert(NO, @"couldn't decode UTF-7 .."); ++- } ++- free(buffer); buffer = NULL; +++ cnt += _decodeOfModifiedUTF7(buf + cnt + 1, res + cntRes, &cntRes) + 1; ++ } ++ } ++ else { ++@@ -171,20 +118,133 @@ ++ cnt++; ++ } ++ } ++- if (cnt < len) ++- res[cntRes++] = buf[cnt++]; ++- ++- if (buf != NULL) free(buf); buf = NULL; ++ ++- data = [[NSData alloc] initWithBytesNoCopy:res length:cntRes ++- freeWhenDone:YES]; ++- result = [[NSString alloc] initWithData:data ++- encoding:NSISOLatin1StringEncoding]; ++- [data release]; data = nil; ++- ++- return [result autorelease]; +++ if (buf != NULL) NSZoneFree(NULL, buf); +++ +++ res[cntRes] = 0; +++ result = [NSString stringWithCharacters: res length: cntRes]; +++ +++ return result; ++ } ++ +++/* check metamail output for correctness */ +++ +++static unsigned char basis_64[] = +++ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +++ +++static char index_64[128] = { +++ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, +++ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, +++ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, +++ 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1, +++ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, +++ 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, +++ -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, +++ 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 +++}; +++ +++#define char64(c) (((c) < 0 || (c) > 127) ? -1 : index_64[(c)]) +++ +++static unsigned int _encodeToModifiedUTF7(unichar *_char, unsigned char *result_, +++ unsigned int *cntRes_) +++{ +++ unsigned int processedSrc, processedDest, cycle; +++ unichar c; +++ char leftover; +++ BOOL hasLeftOver; +++ +++ processedSrc = 0; +++ processedDest = 0; +++ cycle = 0; +++ leftover = 0; +++ +++ c = *_char; +++ while (c > 126 || (c > 0 && c < 32)) { +++ if (cycle == 0) { +++ *(result_ + processedDest) = basis_64[(c >> 10) & 0x3f]; +++ *(result_ + processedDest + 1) = basis_64[(c >> 4) & 0x3f]; +++ leftover = (c << 2); +++ hasLeftOver = YES; +++ processedDest += 2; +++ cycle = 1; +++ } +++ else if (cycle == 1) { +++ *(result_ + processedDest) = basis_64[(leftover | (c >> 14)) & 0x3f]; +++ *(result_ + processedDest + 1) = basis_64[(c >> 8) & 0x3f]; +++ *(result_ + processedDest + 2) = basis_64[(c >> 2) & 0x3f]; +++ leftover = (c << 4); +++ hasLeftOver = YES; +++ processedDest += 3; +++ cycle = 2; +++ } +++ else if (cycle == 2) { +++ *(result_ + processedDest) = basis_64[(leftover | (c >> 12)) & 0x3f]; +++ *(result_ + processedDest + 1) = basis_64[(c >> 6) & 0x3f]; +++ *(result_ + processedDest + 2) = basis_64[c & 0x3f]; +++ leftover = 0; +++ hasLeftOver = NO; +++ processedDest += 3; +++ cycle = 0; +++ } +++ processedSrc++; +++ c = *(_char + processedSrc); +++ } +++ if (hasLeftOver) { +++ *(result_ + processedDest) = basis_64[leftover & 0x3f]; +++ processedDest++; +++ } +++ processedSrc--; +++ *cntRes_ += processedDest; +++ +++ return processedSrc; +++} +++ +++static unsigned int _decodeOfModifiedUTF7(unsigned char *_source, unichar *result_, +++ unsigned int *cntRes_) +++{ +++ unsigned int processedSrc, processedDest; +++ unsigned char c, decoded; +++ unichar currentRes; +++ int shift; +++ +++ processedSrc = 0; +++ processedDest = 0; +++ shift = 10; +++ currentRes = 0; +++ +++ c = *_source; +++ while (c != 0 && c != '-') { +++ decoded = index_64[c]; +++ if (shift < 0) { +++ currentRes |= (decoded >> (shift * -1)); +++ *(result_ + processedDest) = currentRes; +++ processedDest++; +++ shift += 16; +++ currentRes = (decoded << shift); +++ } else { +++ currentRes |= (decoded << shift); +++ if (shift == 0) { +++ *(result_ + processedDest) = currentRes; +++ processedDest++; +++ currentRes = 0; +++ shift = 16; +++ } +++ } +++ shift -= 6; +++ processedSrc++; +++ c = *(_source + processedSrc); +++ } +++ if (shift != 10) { +++ *(result_ + processedDest) = currentRes; +++ } +++ if (c == '-') +++ processedSrc++; +++ +++ *cntRes_ += processedDest; +++ +++ return processedSrc; +++} +++ ++ - (NSString *)stringByEscapingImap4Password { ++ // TODO: perf ++ unichar *buffer; ++@@ -193,12 +253,12 @@ ++ NSString *s; ++ ++ len = [self length]; ++- chars = calloc(len + 2, sizeof(unichar)); +++ chars = NSZoneCalloc(NULL, len + 2, sizeof(unichar)); ++ [self getCharacters:chars]; ++- ++- buffer = calloc(len * 2 + 2, sizeof(unichar)); +++ +++ buffer = NSZoneCalloc(NULL, len * 2 + 2, sizeof(unichar)); ++ buffer[len * 2] = '\0'; ++- +++ ++ for (i = 0, j = 0; i < len; i++, j++) { ++ BOOL conv = NO; ++ ++@@ -224,209 +284,11 @@ ++ } ++ buffer[j] = chars[i]; ++ } ++- if (chars != NULL) free(chars); chars = NULL; +++ if (chars != NULL) NSZoneFree(NULL, chars); ++ ++ s = [NSString stringWithCharacters:buffer length:j]; ++- if (buffer != NULL) free(buffer); buffer = NULL; +++ ++ return s; ++ } ++ ++ @end /* NSString(Imap4) */ ++- ++-static void writeChunk(int _c1, int _c2, int _c3, int _pads, ++- unsigned char **result_, ++- unsigned int *cntRes_); ++- ++-static int getChar(int _cnt, int *cnt_, unsigned char *_buf) { ++- int result; ++- ++- if ((_cnt % 2)) { ++- result = _buf[*cnt_]; ++- (*cnt_)++; ++- } ++- else { ++- result = 0; ++- } ++- return result; ++-} ++-static void _encodeToModifiedUTF7(unsigned char *_buf, int encLen, ++- unsigned char **result_, unsigned int *cntRes_) ++-{ ++- int c1, c2, c3; ++- int cnt, cntAll; ++- ++- cnt = 0; ++- cntAll = 0; ++- ++- while (cnt < encLen) { ++- c1 = getChar(cntAll++, &cnt, _buf); ++- if (cnt == encLen) { ++- writeChunk(c1, 0, 0, 2, result_, cntRes_); ++- } ++- else { ++- c2 = getChar(cntAll++, &cnt, _buf); ++- if (cnt == encLen) { ++- writeChunk(c1, c2, 0, 1, result_, cntRes_); ++- } ++- else { ++- c3 = getChar(cntAll++, &cnt, _buf); ++- writeChunk(c1, c2, c3, 0, result_, cntRes_); ++- } ++- } ++- } ++-} ++- ++-/* check metamail output for correctness */ ++- ++-static unsigned char basis_64[] = ++- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; ++- ++-static void writeChunk(int c1, int c2, int c3, int pads, unsigned char **result_, ++- unsigned int *cntRes_) { ++- unsigned char c; ++- ++- c = basis_64[c1>>2]; ++- (*result_)[*cntRes_] = c; ++- (*cntRes_)++; ++- ++- c = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)]; ++- ++- (*result_)[*cntRes_] = c; ++- (*cntRes_)++; ++- ++- ++- if (pads == 2) { ++- ; ++- } ++- else if (pads) { ++- c = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)]; ++- (*result_)[*cntRes_] = c; ++- (*cntRes_)++; ++- } ++- else { ++- c = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)]; ++- ++- (*result_)[*cntRes_] = c; ++- (*cntRes_)++; ++- ++- c = basis_64[c3 & 0x3F]; ++- (*result_)[*cntRes_] = c; ++- (*cntRes_)++; ++- } ++-} ++- ++-static char index_64[128] = { ++- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, ++- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, ++- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, ++- 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1, ++- -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, ++- 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, ++- -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, ++- 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 ++-}; ++- ++-#define char64(c) (((c) < 0 || (c) > 127) ? -1 : index_64[(c)]) ++- ++-static int _decodeOfModifiedUTF7(unsigned char *_target, unsigned _targetLen, ++- unsigned *usedBytes_ , unsigned char **buffer_, ++- int *bufLen_, int maxBuf) ++-{ ++- int c1, c2, c3, c4; ++- unsigned int cnt; ++- ++- for (cnt = 0; cnt < _targetLen; ) { ++- c1 = '='; ++- c2 = '='; ++- c3 = '='; ++- c4 = '='; ++- ++- c1 = _target[cnt++]; ++- ++- if (c1 == '-') { ++- (*usedBytes_)++; ++- return 0; ++- } ++- if (cnt < _targetLen) ++- c2 = _target[cnt++]; ++- ++- if (c2 == '-') { ++- (*usedBytes_)+=2; ++- return 0; ++- } ++- ++- (*usedBytes_) += 2; ++- ++- if (cnt < _targetLen) { ++- c3 = _target[cnt++]; ++- (*usedBytes_)++; ++- } ++- ++- if (cnt < _targetLen) { ++- c4 = _target[cnt++]; ++- if (c3 != '-') ++- (*usedBytes_)++; ++- } ++- ++- if (c2 == -1 || c3 == -1 || c4 == -1) { ++- fprintf(stderr, "Warning: base64 decoder saw premature EOF!\n"); ++- return 0; ++- } ++- ++- if (c1 == '=' || c2 == '=') { ++- continue; ++- } ++- ++- c1 = char64(c1); ++- c2 = char64(c2); ++- ++- if (*bufLen_ < maxBuf) { ++- unsigned char c; ++- ++- c = ((c1<<2) | ((c2&0x30)>>4)); ++- ++- if (c) { ++- (*buffer_)[*bufLen_] = c; ++- *bufLen_ = *bufLen_ + 1; ++- } ++- } ++- if (c3 == '-') { ++- return 0; ++- } ++- else if (c3 == '=') { ++- continue; ++- } else { ++- ++- c3 = char64(c3); ++- ++- if (*bufLen_ < maxBuf) { ++- unsigned char c; ++- c = (((c2&0XF) << 4) | ((c3&0x3C) >> 2)); ++- if (c) { ++- (*buffer_)[*bufLen_] = c; ++- *bufLen_ = *bufLen_ + 1; ++- } ++- } ++- ++- if (c4 == '-') { ++- return 0; ++- } ++- else if (c4 == '=') { ++- continue; ++- } else { ++- c4 = char64(c4); ++- ++- if (*bufLen_ < maxBuf) { ++- unsigned char c; ++- ++- c = (((c3&0x03) <<6) | c4); ++- if (c) { ++- (*buffer_)[*bufLen_] = c; ++- (*bufLen_) = (*bufLen_) + 1; ++- } ++- } ++- } ++- } ++- } ++- return 0; ++-} ++Index: sope-mime/NGImap4/NGImap4Functions.h ++=================================================================== ++--- sope-mime/NGImap4/NGImap4Functions.h (revision 1664) +++++ sope-mime/NGImap4/NGImap4Functions.h (working copy) ++@@ -58,4 +58,6 @@ ++ id_folder); ++ BOOL _createSubFolderWithName(id self, NSString *_name, BOOL _app); ++ +++NSString *SaneFolderName(NSString *folderName); +++ ++ #endif /* __NGMime_NGImap4_NGImap4Functions_H__ */ ++Index: sope-mime/NGMail/NGMailAddressParser.h ++=================================================================== ++--- sope-mime/NGMail/NGMailAddressParser.h (revision 1664) +++++ sope-mime/NGMail/NGMailAddressParser.h (working copy) ++@@ -24,7 +24,9 @@ ++ ++ #import ++ ++-@class NSData, NSString, NSArray; +++#import +++ +++@class NSData, NSArray; ++ @class NGMailAddressList; ++ ++ /* ++@@ -34,16 +36,16 @@ ++ @interface NGMailAddressParser : NSObject ++ { ++ @private ++- unsigned char *data; ++- int dataPos; ++- int errorPos; ++- int maxLength; +++ unichar *data; +++ int dataPos; +++ int errorPos; +++ int maxLength; ++ } ++ ++ + (id)mailAddressParserWithString:(NSString *)_string; ++ + (id)mailAddressParserWithData:(NSData *)_data; ++-+ (id)mailAddressParserWithCString:(char *)_cString; ++-- (id)initWithCString:(const unsigned char *)_cstr length:(int unsigned)_len; ++++ (id)mailAddressParserWithCString:(const char *)_cString; +++- (id)initWithString:(NSString *)_str; ++ ++ /* parsing */ ++ ++Index: sope-mime/NGMail/NGMimeMessageGenerator.m ++=================================================================== ++--- sope-mime/NGMail/NGMimeMessageGenerator.m (revision 1664) +++++ sope-mime/NGMail/NGMimeMessageGenerator.m (working copy) ++@@ -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/NGMail/NGMailAddressParser.m ++=================================================================== ++--- sope-mime/NGMail/NGMailAddressParser.m (revision 1664) +++++ sope-mime/NGMail/NGMailAddressParser.m (working copy) ++@@ -52,9 +52,9 @@ ++ StrClass = [NSString class]; ++ } ++ ++-static inline NSString *mkStrObj(const unsigned char *s, unsigned int l) { +++static inline NSString *mkStrObj(const unichar *s, unsigned int l) { ++ // TODO: unicode ++- return [(NSString *)[StrClass alloc] initWithCString:(char *)s length:l]; +++ return [(NSString *)[StrClass alloc] initWithCharacters:s length:l]; ++ } ++ ++ static inline id parseWhiteSpaces(NGMailAddressParser *self, BOOL _guessMode) { ++@@ -84,7 +84,7 @@ ++ int keepPos = self->dataPos; // keep reference for backtracking ++ id returnValue = nil; ++ BOOL isAtom = YES; ++- unsigned char text[self->maxLength + 2]; // token text +++ unichar text[self->maxLength + 2]; // token text ++ int length = 0; // token text length ++ BOOL done = NO; ++ ++@@ -94,7 +94,7 @@ ++ done = YES; ++ } ++ else { ++- register unsigned char c = self->data[self->dataPos]; +++ register unichar c = self->data[self->dataPos]; ++ ++ switch (c) { ++ case '(' : case ')': case '<': case '>': ++@@ -162,7 +162,7 @@ ++ int keepPos = self->dataPos; // keep reference for backtracking ++ id returnValue = nil; ++ BOOL isQText = YES; ++- unsigned char text[self->maxLength + 4]; // token text +++ unichar text[self->maxLength + 4]; // token text ++ int length = 0; // token text length ++ BOOL done = YES; ++ ++@@ -172,9 +172,9 @@ ++ done = YES; ++ } ++ else { ++- register char c = self->data[self->dataPos]; +++ register unichar c = self->data[self->dataPos]; ++ ++- switch ((int)c) { +++ switch (c) { ++ case '"' : ++ case '\\': ++ case 13 : ++@@ -215,7 +215,7 @@ ++ int keepPos = self->dataPos; // keep reference for backtracking ++ id returnValue = nil; ++ BOOL isDText = YES; ++- unsigned char text[self->maxLength]; // token text +++ unichar text[self->maxLength]; // token text ++ int length = 0; // token text length ++ BOOL done = YES; ++ ++@@ -225,9 +225,9 @@ ++ done = YES; ++ } ++ else { ++- register char c = self->data[self->dataPos]; +++ register unichar c = self->data[self->dataPos]; ++ ++- switch ((int)c) { +++ switch (c) { ++ case '[': case ']': ++ case '\\': case 13: ++ isDText = (length > 0); ++@@ -320,42 +320,47 @@ ++ /* constructors */ ++ ++ + (id)mailAddressParserWithData:(NSData *)_data { ++- return [[(NGMailAddressParser *)[self alloc] ++- initWithCString:[_data bytes] ++- length:[_data length]] autorelease]; +++ NSString *uniString; +++ +++ uniString = [NSString stringWithCharacters:(unichar *)[_data bytes] +++ length:([_data length] / sizeof(unichar))]; +++ +++ return [(NGMailAddressParser *)self mailAddressParserWithString:uniString]; ++ } +++ ++ + (id)mailAddressParserWithCString:(char *)_cString { ++- return [[(NGMailAddressParser *)[self alloc] ++- initWithCString:(unsigned char *)_cString ++- length:strlen(_cString)] autorelease]; +++ NSString *nsCString; +++ +++ nsCString = [NSString stringWithCString:_cString]; +++ +++ return [(NGMailAddressParser *)self mailAddressParserWithString:nsCString]; ++ } ++-- (id)initWithCString:(const unsigned char *)_cstr length:(int unsigned)_len { +++ ++++ (id)mailAddressParserWithString:(NSString *)_string { +++ return [[(NGMailAddressParser *)[self alloc] initWithString:_string] +++ autorelease]; +++} +++ +++- (id)initWithString:(NSString *)_str { ++ if ((self = [super init])) { ++ // TODO: remember some string encoding? ++- self->data = (unsigned char *)_cstr; ++- self->maxLength = _len; +++ self->maxLength = [_str length]; +++ self->data = malloc(self->maxLength*sizeof(unichar)); +++ [_str getCharacters:self->data]; ++ self->dataPos = 0; ++ self->errorPos = -1; ++ } ++ return self; ++ } ++ ++-- (id)initWithString:(NSString *)_str { ++- // TODO: unicode ++- return [self initWithCString:(unsigned char *)[_str cString] ++- length:[_str cStringLength]]; ++-} ++- ++ - (id)init { ++- return [self initWithCString:NULL length:0]; +++ return [self initWithString:nil]; ++ } ++ ++-+ (id)mailAddressParserWithString:(NSString *)_string { ++- return [[(NGMailAddressParser *)[self alloc] initWithString:_string] ++- autorelease]; ++-} ++- ++ - (void)dealloc { +++ if (self->data != NULL) { +++ free(self->data); +++ } ++ self->data = NULL; ++ self->maxLength = 0; ++ self->dataPos = 0; ++Index: sope-mime/NGMime/NGMimeRFC822DateHeaderFieldParser.m ++=================================================================== ++--- sope-mime/NGMime/NGMimeRFC822DateHeaderFieldParser.m (revision 1664) +++++ sope-mime/NGMime/NGMimeRFC822DateHeaderFieldParser.m (working copy) ++@@ -19,88 +19,45 @@ ++ 02111-1307, USA. ++ */ ++ +++#ifdef HAVE_STRNDUP +++#define _GNU_SOURCE 1 +++#endif +++ +++#include +++ ++ #include "NGMimeHeaderFieldParser.h" ++ #include "NGMimeHeaderFields.h" ++ #include "NGMimeUtilities.h" ++ #include "common.h" ++-#include ++ +++#ifndef HAVE_STRNDUP +++char *strndup(const char *str, size_t len) +++{ +++ char *dup = (char *)malloc(len+1); +++ if (dup) { +++ strncpy(dup,str,len); +++ dup[len]= '\0'; +++ } +++ return dup; +++} +++#endif +++ ++ @implementation NGMimeRFC822DateHeaderFieldParser ++ ++-static Class CalDateClass = Nil; ++-static NSTimeZone *gmt = nil; ++-static NSTimeZone *gmt01 = nil; ++-static NSTimeZone *gmt02 = nil; ++-static NSTimeZone *gmt03 = nil; ++-static NSTimeZone *gmt04 = nil; ++-static NSTimeZone *gmt05 = nil; ++-static NSTimeZone *gmt06 = nil; ++-static NSTimeZone *gmt07 = nil; ++-static NSTimeZone *gmt08 = nil; ++-static NSTimeZone *gmt09 = nil; ++-static NSTimeZone *gmt10 = nil; ++-static NSTimeZone *gmt11 = nil; ++-static NSTimeZone *gmt12 = nil; ++-static NSTimeZone *gmt0530 = nil; ++-static NSTimeZone *gmtM01 = nil; ++-static NSTimeZone *gmtM02 = nil; ++-static NSTimeZone *gmtM03 = nil; ++-static NSTimeZone *gmtM04 = nil; ++-static NSTimeZone *gmtM05 = nil; ++-static NSTimeZone *gmtM06 = nil; ++-static NSTimeZone *gmtM07 = nil; ++-static NSTimeZone *gmtM08 = nil; ++-static NSTimeZone *gmtM09 = nil; ++-static NSTimeZone *gmtM10 = nil; ++-static NSTimeZone *gmtM11 = nil; ++-static NSTimeZone *gmtM12 = nil; ++-static NSTimeZone *gmtM13 = nil; ++-static NSTimeZone *gmtM14 = nil; ++-static NSTimeZone *met = nil; +++static NSTimeZone *gmt = nil; +++static NSTimeZone *met = nil; ++ ++ + (int)version { ++ return 2; ++ } +++ ++ + (void)initialize { ++ static BOOL didInit = NO; ++- Class TzClass; ++ if (didInit) return; ++ didInit = YES; ++ ++- CalDateClass = [NSCalendarDate class]; ++- ++- /* timezones which were actually used in a maillist mailbox */ ++- TzClass = [NSTimeZone class]; ++- gmt = [[TzClass timeZoneWithName:@"GMT"] retain]; ++- met = [[TzClass timeZoneWithName:@"MET"] retain]; ++- gmt01 = [[TzClass timeZoneForSecondsFromGMT: 1 * (60 * 60)] retain]; ++- gmt02 = [[TzClass timeZoneForSecondsFromGMT: 2 * (60 * 60)] retain]; ++- gmt03 = [[TzClass timeZoneForSecondsFromGMT: 3 * (60 * 60)] retain]; ++- gmt04 = [[TzClass timeZoneForSecondsFromGMT: 4 * (60 * 60)] retain]; ++- gmt05 = [[TzClass timeZoneForSecondsFromGMT: 5 * (60 * 60)] retain]; ++- gmt06 = [[TzClass timeZoneForSecondsFromGMT: 6 * (60 * 60)] retain]; ++- gmt07 = [[TzClass timeZoneForSecondsFromGMT: 7 * (60 * 60)] retain]; ++- gmt08 = [[TzClass timeZoneForSecondsFromGMT: 8 * (60 * 60)] retain]; ++- gmt09 = [[TzClass timeZoneForSecondsFromGMT: 9 * (60 * 60)] retain]; ++- gmt10 = [[TzClass timeZoneForSecondsFromGMT: 10 * (60 * 60)] retain]; ++- gmt11 = [[TzClass timeZoneForSecondsFromGMT: 11 * (60 * 60)] retain]; ++- gmt12 = [[TzClass timeZoneForSecondsFromGMT: 12 * (60 * 60)] retain]; ++- gmtM01 = [[TzClass timeZoneForSecondsFromGMT: -1 * (60 * 60)] retain]; ++- gmtM02 = [[TzClass timeZoneForSecondsFromGMT: -2 * (60 * 60)] retain]; ++- gmtM03 = [[TzClass timeZoneForSecondsFromGMT: -3 * (60 * 60)] retain]; ++- gmtM04 = [[TzClass timeZoneForSecondsFromGMT: -4 * (60 * 60)] retain]; ++- gmtM05 = [[TzClass timeZoneForSecondsFromGMT: -5 * (60 * 60)] retain]; ++- gmtM06 = [[TzClass timeZoneForSecondsFromGMT: -6 * (60 * 60)] retain]; ++- gmtM07 = [[TzClass timeZoneForSecondsFromGMT: -7 * (60 * 60)] retain]; ++- gmtM08 = [[TzClass timeZoneForSecondsFromGMT: -8 * (60 * 60)] retain]; ++- gmtM09 = [[TzClass timeZoneForSecondsFromGMT: -9 * (60 * 60)] retain]; ++- gmtM10 = [[TzClass timeZoneForSecondsFromGMT:-10 * (60 * 60)] retain]; ++- gmtM11 = [[TzClass timeZoneForSecondsFromGMT:-11 * (60 * 60)] retain]; ++- gmtM12 = [[TzClass timeZoneForSecondsFromGMT:-12 * (60 * 60)] retain]; ++- gmtM13 = [[TzClass timeZoneForSecondsFromGMT:-13 * (60 * 60)] retain]; ++- gmtM14 = [[TzClass timeZoneForSecondsFromGMT:-14 * (60 * 60)] retain]; ++- ++- gmt0530 = [[TzClass timeZoneForSecondsFromGMT:5 * (60*60) + (30*60)] retain]; +++ gmt = [[NSTimeZone timeZoneWithName:@"GMT"] retain]; +++ met = [[NSTimeZone timeZoneWithName:@"MET"] retain]; ++ } ++ ++ /* ++@@ -111,7 +68,7 @@ ++ TODO: use an own parser for that. ++ */ ++ ++-static int parseMonthOfYear(unsigned char *s, unsigned int len) { +++static int parseMonthOfYear(char *s, unsigned int len) { ++ /* ++ This one is *extremely* forgiving, it only checks what is ++ necessary for the set below. This should work for both, English ++@@ -147,162 +104,110 @@ ++ } ++ } ++ ++-static NSTimeZone *parseTimeZone(unsigned char *s, unsigned int len) { +++static int offsetFromTZAbbreviation(const char **p) { +++ NSString *abbreviation; +++ NSTimeZone *offsetTZ; +++ unsigned int length; +++ +++ length = 0; +++ while (isalpha(*(*p+length))) +++ length++; +++ abbreviation = [[NSString alloc] initWithBytes: *p +++ length: length - 1 +++ encoding: NSISOLatin1StringEncoding]; +++ offsetTZ = [NSTimeZone timeZoneWithAbbreviation: abbreviation]; +++ [abbreviation release]; +++ *p += length; +++ +++ return [offsetTZ secondsFromGMT]; +++} +++ +++static inline char *digitsString(const char *string) { +++ const char *p; +++ unsigned int len; +++ +++ p = string; +++ while (!isdigit(*p)) +++ p++; +++ len = 0; +++ while (isdigit(*(p + len))) +++ len++; +++ +++ return strndup(p, len); +++} +++ +++static NSTimeZone *parseTimeZone(const char *s, unsigned int len) { ++ /* ++ WARNING: failed to parse RFC822 timezone: '+0530' \ ++ (value='Tue, 13 Jul 2004 21:39:28 +0530') ++ TODO: this is because libFoundation doesn't accept 'GMT+0530' as input. ++ */ ++- char *p = (char *)s; +++ char *newString, *digits; +++ const char *p; ++ NSTimeZone *tz; ++- NSString *ts; ++- ++- if (len == 0) ++- return nil; ++- ++- if (*s == '+' || *s == '-') { ++- if (len == 3) { ++- if (p[1] == '0' && p[2] == '0') // '+00' or '-00' ++- return gmt; ++- if (*s == '+') { ++- if (p[1] == '0' && p[2] == '1') // '+01' ++- return gmt01; ++- if (p[1] == '0' && p[2] == '2') // '+02' ++- return gmt02; ++- } ++- } ++- else if (len == 5) { ++- if (p[3] == '0' && p[4] == '0' && p[1] == '0') { // '?0x00' ++- if (p[2] == '0') // '+0000' ++- return gmt; ++- ++- if (*s == '+') { ++- if (p[2] == '1') return gmt01; // '+0100' ++- if (p[2] == '2') return gmt02; // '+0200' ++- if (p[2] == '3') return gmt03; // '+0300' ++- if (p[2] == '4') return gmt04; // '+0400' ++- if (p[2] == '5') return gmt05; // '+0500' ++- if (p[2] == '6') return gmt06; // '+0600' ++- if (p[2] == '7') return gmt07; // '+0700' ++- if (p[2] == '8') return gmt08; // '+0800' ++- if (p[2] == '9') return gmt09; // '+0900' ++- } ++- else if (*s == '-') { ++- if (p[2] == '1') return gmtM01; // '-0100' ++- if (p[2] == '2') return gmtM02; // '-0200' ++- if (p[2] == '3') return gmtM03; // '-0300' ++- if (p[2] == '4') return gmtM04; // '-0400' ++- if (p[2] == '5') return gmtM05; // '-0500' ++- if (p[2] == '6') return gmtM06; // '-0600' ++- if (p[2] == '7') return gmtM07; // '-0700' ++- if (p[2] == '8') return gmtM08; // '-0800' ++- if (p[2] == '9') return gmtM09; // '-0900' ++- } ++- } ++- else if (p[3] == '0' && p[4] == '0' && p[1] == '1') { // "?1x00" ++- if (*s == '+') { ++- if (p[2] == '0') return gmt10; // '+1000' ++- if (p[2] == '1') return gmt11; // '+1100' ++- if (p[2] == '2') return gmt12; // '+1200' ++- } ++- else if (*s == '-') { ++- if (p[2] == '0') return gmtM10; // '-1000' ++- if (p[2] == '1') return gmtM11; // '-1100' ++- if (p[2] == '2') return gmtM12; // '-1200' ++- if (p[2] == '3') return gmtM13; // '-1300' ++- if (p[2] == '4') return gmtM14; // '-1400' ++- } ++- } ++- ++- /* special case for GMT+0530 */ ++- if (strncmp((char *)s, "+0530", 5) == 0) ++- return gmt0530; ++- } ++- else if (len == 7) { ++- /* ++- "MultiMail" submits timezones like this: ++- "Tue, 9 Mar 2004 9:43:00 -05-500", ++- don't know what the "-500" trailer is supposed to mean? Apparently ++- Thunderbird just uses the "-05", so do we. ++- */ ++- ++- if (isdigit(p[1]) && isdigit(p[2]) && (p[3] == '-'||p[3] == '+')) { ++- unsigned char tmp[8]; ++- ++- strncpy((char *)tmp, p, 3); ++- tmp[3] = '0'; ++- tmp[4] = '0'; ++- tmp[5] = '\0'; ++- return parseTimeZone(tmp, 5); ++- } ++- } +++ unsigned int hours, minutes, seconds, remaining; +++ int sign; +++ +++ sign = 1; +++ hours = 0; +++ minutes = 0; +++ seconds = 0; +++ +++ newString = strndup(s, len); +++ p = newString; +++ +++ if (isalpha(*p)) +++ seconds = offsetFromTZAbbreviation(&p); +++ while (isspace(*p)) +++ p++; +++ while (*p == '+' || *p == '-') { +++ if (*p == '-') +++ sign = -sign; +++ p++; ++ } ++- else if (*s == '0') { ++- if (len == 2) { // '00' ++- if (p[1] == '0') return gmt; ++- if (p[1] == '1') return gmt01; ++- if (p[1] == '2') return gmt02; ++- } ++- else if (len == 4) { ++- if (p[2] == '0' && p[3] == '0') { // '0x00' ++- if (p[1] == '0') return gmt; ++- if (p[1] == '1') return gmt01; ++- if (p[1] == '2') return gmt02; ++- } ++- } +++ digits = digitsString(p); +++ p = digits; +++ remaining = strlen(p); +++ switch(remaining) { +++ case 6: /* hhmmss */ +++ seconds += (10 * (*(p + remaining - 2) - 48) +++ + *(p + remaining - 1) - 48); +++ case 4: /* hhmm */ +++ hours += 10 * (*p - 48); +++ p++; +++ case 3: /* hmm */ +++ hours += (*p - 48); +++ p++; +++ minutes += 10 * (*p - 48) + *(p + 1) - 48; +++ break; +++ case 2: /* hh */ +++ hours += 10 * (*p - 48) + *(p + 1) - 48; +++ break; +++ default: +++ NSLog (@"parseTimeZone: cannot parse time notation '%s'", newString); ++ } ++- else if (len == 3) { ++- if (strcasecmp((char *)s, "GMT") == 0) return gmt; ++- if (strcasecmp((char *)s, "UTC") == 0) return gmt; ++- if (strcasecmp((char *)s, "MET") == 0) return met; ++- if (strcasecmp((char *)s, "CET") == 0) return met; ++- } ++- ++- if (isalpha(*s)) { ++- ts = [[NSString alloc] initWithCString:(char *)s length:len]; ++- } ++- else { ++- char buf[len + 5]; ++- ++- buf[0] = 'G'; buf[1] = 'M'; buf[2] = 'T'; ++- if (*s == '+' || *s == '-') { ++- strcpy(&(buf[3]), (char *)s); ++- } ++- else { ++- buf[3] = '+'; ++- strcpy(&(buf[4]), (char *)s); ++- } ++- ts = [[NSString alloc] initWithCString:buf]; ++- } ++-#if 1 ++- NSLog(@"%s: RFC822 TZ Parser: expensive: '%@'", __PRETTY_FUNCTION__, ts); ++-#endif ++- tz = [NSTimeZone timeZoneWithAbbreviation:ts]; ++- [ts release]; +++ free(digits); +++ +++ seconds += sign * (3600 * hours + 60 * minutes); +++ tz = [NSTimeZone timeZoneForSecondsFromGMT: seconds]; +++ free(newString); +++ ++ return tz; ++ } ++ ++ - (id)parseValue:(id)_data ofHeaderField:(NSString *)_field { ++ // TODO: use UNICODE ++ NSCalendarDate *date = nil; ++- unsigned char buf[256]; ++- unsigned char *bytes = buf, *pe; +++ 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 lengthOfBytesUsingEncoding: NSUTF8StringEncoding]; +++ bytes = [_data cStringUsingEncoding: NSUTF8StringEncoding]; +++ ++ /* remove leading chars (skip to first digit, the day of the month) */ ++ while (length > 0 && (!isdigit(*bytes))) { ++ bytes++; ++@@ -312,7 +217,7 @@ ++ if (length == 0) { ++ NSLog(@"WARNING(%s): empty value for header field %@ ..", ++ __PRETTY_FUNCTION__, _field); ++- return [CalDateClass date]; +++ return [NSCalendarDate date]; ++ } ++ ++ // TODO: should be a category on NSCalendarDate ++@@ -435,7 +340,8 @@ ++ for (pe = bytes; isalnum(*pe) || *pe == '-' || *pe == '+'; pe++) ++ ; ++ *pe = '\0'; ++- if ((tz = parseTimeZone(bytes, (pe - bytes))) == nil) { +++ if (pe == bytes +++ || (tz = parseTimeZone((const char *) bytes, (pe - bytes))) == nil) { ++ [self logWithFormat: ++ @"WARNING: failed to parse RFC822 timezone: '%s' (value='%@')", ++ bytes, _data]; ++@@ -444,9 +350,9 @@ ++ ++ /* construct and return */ ++ finished: ++- date = [CalDateClass dateWithYear:year month:monthOfYear day:dayOfMonth ++- hour:hour minute:minute second:second ++- timeZone:tz]; +++ date = [NSCalendarDate dateWithYear:year month:monthOfYear day:dayOfMonth +++ hour:hour minute:minute second:second +++ timeZone:tz]; ++ if (date == nil) goto failed; ++ ++ #if 0 ++Index: sope-mime/NGMime/NGMimeMultipartBodyParser.m ++=================================================================== ++--- sope-mime/NGMime/NGMimeMultipartBodyParser.m (revision 1664) +++++ sope-mime/NGMime/NGMimeMultipartBodyParser.m (working copy) ++@@ -428,6 +428,7 @@ ++ NSString *boundary = nil; ++ NSArray *rawBodyParts = nil; ++ BOOL foundError = NO; +++ NSData *boundaryBytes; ++ ++ contentType = [_part contentType]; ++ boundary = [contentType valueOfParameter:@"boundary"]; ++@@ -437,9 +438,10 @@ ++ ++ *(&foundError) = NO; ++ +++ boundaryBytes = [boundary dataUsingEncoding:NSISOLatin1StringEncoding]; ++ *(&rawBodyParts) = [self _parseBody:_body part:_part data:_data ++- boundary:[boundary cString] ++- length:[boundary cStringLength] +++ boundary:[boundaryBytes bytes] +++ length:[boundary length] ++ delegate:_d]; ++ ++ if (rawBodyParts) { ++Index: sope-mime/NGMime/NGMimeHeaderFieldGeneratorSet.m ++=================================================================== ++--- sope-mime/NGMime/NGMimeHeaderFieldGeneratorSet.m (revision 1664) +++++ sope-mime/NGMime/NGMimeHeaderFieldGeneratorSet.m (working copy) ++@@ -77,6 +77,7 @@ ++ [rfc822Set setGenerator:gen forField:@"bcc"]; ++ [rfc822Set setGenerator:gen forField:Fields->from]; ++ [rfc822Set setGenerator:gen forField:@"reply-to"]; +++ [rfc822Set setGenerator:gen forField:@"in-reply-to"]; ++ [rfc822Set setGenerator:gen forField:@"Disposition-Notification-To"]; ++ } ++ ++Index: sope-mime/NGMime/NGMimeType.m ++=================================================================== ++--- sope-mime/NGMime/NGMimeType.m (revision 1664) +++++ sope-mime/NGMime/NGMimeType.m (working copy) ++@@ -120,30 +120,30 @@ ++ ++ /* some unsupported, but known encoding */ ++ else if ([charset isEqualToString:@"ks_c_5601-1987"]) { ++- encoding = [NSString defaultCStringEncoding]; +++ encoding = NSISOLatin1StringEncoding; ++ foundUnsupported = YES; ++ } ++ else if ([charset isEqualToString:@"euc-kr"]) { ++- encoding = [NSString defaultCStringEncoding]; ++- foundUnsupported = YES; +++ encoding = NSKoreanEUCStringEncoding; ++ } ++ else if ([charset isEqualToString:@"big5"]) { ++- encoding = [NSString defaultCStringEncoding]; ++- foundUnsupported = YES; +++ encoding = NSBIG5StringEncoding; ++ } ++ else if ([charset isEqualToString:@"iso-2022-jp"]) { ++- encoding = [NSString defaultCStringEncoding]; ++- foundUnsupported = YES; +++ encoding = NSISO2022JPStringEncoding; ++ } ++ else if ([charset isEqualToString:@"gb2312"]) { ++- encoding = [NSString defaultCStringEncoding]; ++- foundUnsupported = YES; +++ encoding = NSGB2312StringEncoding; ++ } ++ else if ([charset isEqualToString:@"koi8-r"]) { ++- encoding = [NSString defaultCStringEncoding]; ++- foundUnsupported = YES; +++ encoding = NSKOI8RStringEncoding; ++ } ++- +++ else if ([charset isEqualToString:@"windows-1250"]) { +++ encoding = NSWindowsCP1250StringEncoding; +++ } +++ else if ([charset isEqualToString:@"windows-1251"]) { +++ encoding = NSWindowsCP1251StringEncoding; +++ } ++ else if ([charset isEqualToString:@"windows-1252"]) { ++ encoding = NSWindowsCP1252StringEncoding; ++ } ++@@ -152,7 +152,7 @@ ++ } ++ else if ([charset isEqualToString:@"x-unknown"] || ++ [charset isEqualToString:@"unknown"]) { ++- encoding = NSASCIIStringEncoding; +++ encoding = NSISOLatin1StringEncoding; ++ } ++ /* ISO Latin 9 */ ++ #if !(NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY) ++@@ -166,7 +166,7 @@ ++ else { ++ [self logWithFormat:@"%s: unknown charset '%@'", ++ __PRETTY_FUNCTION__, _s]; ++- encoding = [NSString defaultCStringEncoding]; +++ encoding = NSISOLatin1StringEncoding; ++ } ++ return encoding; ++ } ++@@ -385,23 +385,26 @@ ++ } ++ ++ - (BOOL)valueNeedsQuotes:(NSString *)_parameterValue { ++- unsigned len = [_parameterValue cStringLength]; ++- char buf[len + 15]; ++- char *cstr; +++ NSData *stringData; +++ const char *cstr; +++ unsigned int count, max; +++ BOOL needsQuote; ++ ++- cstr = &(buf[0]); +++ needsQuote = NO; ++ ++- [_parameterValue getCString:cstr]; cstr[len] = '\0'; ++- while (*cstr) { ++- if (isMime_SpecialByte(*cstr)) ++- return YES; +++ stringData = [_parameterValue dataUsingEncoding:NSUTF8StringEncoding]; +++ cstr = [stringData bytes]; +++ max = [stringData length]; +++ count = 0; +++ while (!needsQuote && count < max) { +++ if (isMime_SpecialByte(*(cstr + count)) +++ || *(cstr + count) == 32) +++ needsQuote = YES; +++ else +++ count++; +++ } ++ ++- if (*cstr == 32) ++- return YES; ++- ++- cstr++; ++- } ++- return NO; +++ return needsQuote; ++ } ++ ++ - (NSString *)stringValue { ++Index: sope-mime/NGMime/NGMimeBodyPart.m ++=================================================================== ++--- sope-mime/NGMime/NGMimeBodyPart.m (revision 1664) +++++ sope-mime/NGMime/NGMimeBodyPart.m (working copy) ++@@ -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/ChangeLog ++=================================================================== ++--- sope-mime/NGMime/ChangeLog (revision 1664) +++++ sope-mime/NGMime/ChangeLog (working copy) ++@@ -1,3 +1,25 @@ +++2008-09-08 Wolfgang Sourdeau +++ +++ * NGMimeRFC822DateHeaderFieldParser.m ([NGMimeRFC +++ -parseValue:ofHeaderField:]): don't parse timezone with a length +++ of 0. +++ +++2008-09-01 Wolfgang Sourdeau +++ +++ * NGMimeRFC822DateHeaderFieldParser.m ([NGMimeRFC +++ -parseValue:ofHeaderField:]): use an 8-bit safe encoding when +++ parsing dates. Since we only consider 7-bits characters, we ensure +++ that bad user-agents can be handled more properly. +++ +++ * NGMimeType.m ([NGMimeType +stringEncodingForCharset:]): +++ x-unknown encoding is now translated to an 8-bit safe encoding +++ (NSISOLatin1StringEncoding). +++ +++ * NGMimeAddressHeaderFieldGenerator.m +++ ([NGMimeAddressHeaderFieldGenerator +++ -generateDataForHeaderFieldNamed:value:]): encode resulting string +++ in an 8-bit safe encoding (NSISOLatin1StringEncoding). +++ ++ 2008-01-29 Albrecht Dress ++ ++ * fixes for OGo bug #789 (reply-to QP encoding) ++Index: sope-mime/NGMime/NGMimeContentTypeHeaderFieldGenerator.m ++=================================================================== ++--- sope-mime/NGMime/NGMimeContentTypeHeaderFieldGenerator.m (revision 1664) +++++ sope-mime/NGMime/NGMimeContentTypeHeaderFieldGenerator.m (working copy) ++@@ -36,8 +36,7 @@ ++ NGMimeType *type = nil; // only one content-type field ++ NSString *tmp = nil; ++ NSMutableData *data = nil; ++- unsigned char *ctmp = NULL; ++- unsigned len = 0; +++ NSData *valueData; ++ ++ type = _value; ++ ++@@ -59,21 +58,15 @@ ++ ++ tmp = [type type]; ++ NSAssert(tmp, @"type should not be nil"); ++- len = [tmp length]; ++- ctmp = malloc(len + 4); ++- [tmp getCString:(char *)ctmp]; ctmp[len] = '\0'; ++- [data appendBytes:ctmp length:len]; ++- free(ctmp); +++ valueData = [tmp dataUsingEncoding: NSISOLatin1StringEncoding]; +++ [data appendData: valueData]; +++ +++ [data appendBytes:"/" length:1]; ++ ++- [data appendBytes:"//" length:1]; ++- ++ tmp = [type subType]; ++ if (tmp != nil) { ++- len = [tmp length]; ++- ctmp = malloc(len + 4); ++- [tmp getCString:(char *)ctmp]; ctmp[len] = '\0'; ++- [data appendBytes:ctmp length:len]; ++- free(ctmp); +++ valueData = [tmp dataUsingEncoding: NSISOLatin1StringEncoding]; +++ [data appendData:valueData]; ++ } ++ else ++ [data appendBytes:"*" length:1]; ++@@ -91,12 +84,9 @@ ++ continue; ++ } ++ [data appendBytes:"; " length:2]; ++- ++- len = [name cStringLength]; ++- ctmp = malloc(len + 1); ++- [name getCString:(char *)ctmp]; ctmp[len] = '\0'; ++- [data appendBytes:ctmp length:len]; ++- free(ctmp); +++ +++ valueData = [name dataUsingEncoding: NSUTF8StringEncoding]; +++ [data appendData: valueData]; ++ ++ /* ++ this confuses GroupWise: "= \"" (a space) ++@@ -105,66 +95,30 @@ ++ ++ /* check for encoding */ ++ { ++- unsigned cnt; +++ unsigned cnt, max; +++ const char *dataBytes; ++ BOOL doEnc; ++ ++- len = [value cStringLength]; ++- ctmp = malloc(len + 4); ++- [value getCString:(char *)ctmp]; ctmp[len] = '\0'; ++- cnt = 0; +++ valueData = [value dataUsingEncoding:NSUTF8StringEncoding]; +++ dataBytes = [valueData bytes]; +++ max = [valueData length]; +++ ++ doEnc = NO; ++- while (cnt < len) { ++- if ((unsigned char)ctmp[cnt] > 127) { +++ cnt = 0; +++ while (!doEnc && cnt < max) { +++ if ((unsigned char)dataBytes[cnt] > 127) ++ doEnc = YES; ++- break; ++- } ++- cnt++; +++ else +++ cnt++; ++ } ++ if (doEnc) { ++- unsigned char iso[] = "=?iso-8859-15?q?"; ++- unsigned isoLen = 16; ++- unsigned char isoEnd[] = "?="; ++- unsigned isoEndLen = 2; ++- unsigned desLen; ++- unsigned char *des; ++- ++- if (ctmp) free(ctmp); ++- { ++- NSData *data; ++- ++-#if APPLE_Foundation_LIBRARY || NeXT_Foundation_LIBRARY ++- data = [value dataUsingEncoding:NSISOLatin1StringEncoding]; ++-#else ++- data = [value dataUsingEncoding:NSISOLatin9StringEncoding]; ++-#endif ++- ++- len = [data length]; ++- ctmp = malloc(len + 10); ++- [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(ctmp + cnt, len - cnt, ++- 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 { ++- NSLog(@"WARNING: An error occour during quoted-printable decoding"); ++- } ++- if (des) free(des); +++ [data appendBytes:"=?utf-8?q?" length:10]; +++ [data appendData: [valueData dataByEncodingQuotedPrintable]]; +++ [data appendBytes:"?=" length:2]; ++ } ++ else { ++- [data appendBytes:ctmp length:len]; +++ [data appendData: valueData]; ++ } ++- free(ctmp); ++ } ++ [data appendBytes:"\"" length:1]; ++ } ++Index: sope-mime/NGMime/NGMimePartGenerator.m ++=================================================================== ++--- sope-mime/NGMime/NGMimePartGenerator.m (revision 1664) +++++ sope-mime/NGMime/NGMimePartGenerator.m (working copy) ++@@ -155,8 +155,9 @@ ++ BOOL isMultiValue, isFirst; ++ ++ /* get field name and strip leading spaces */ ++- fcname = (const unsigned char *)[_field cString]; ++- for (len = [_field cStringLength]; len > 0; fcname++, len--) { +++ fcname = (const unsigned char *)[_field cStringUsingEncoding:NSISOLatin1StringEncoding]; +++ for (len = [_field lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding]; +++ len > 0; fcname++, len--) { ++ if (*fcname != ' ') ++ break; ++ } ++@@ -328,7 +329,7 @@ ++ if ([body isKindOfClass:[NSData class]]) ++ data = body; ++ else if ([body isKindOfClass:[NSString class]]) ++- data = [body dataUsingEncoding:[NSString defaultCStringEncoding]]; +++ data = [body dataUsingEncoding: NSISOLatin1StringEncoding]; ++ else ++ data = nil; ++ ++Index: sope-mime/NGMime/NGMimeBodyParser.m ++=================================================================== ++--- sope-mime/NGMime/NGMimeBodyParser.m (revision 1664) +++++ sope-mime/NGMime/NGMimeBodyParser.m (working copy) ++@@ -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]]; ++ ++@@ -88,10 +91,20 @@ ++ NSStringEncoding encoding; ++ ++ encoding = [NGMimeType stringEncodingForCharset:charset]; ++- +++ +++ // If we nave no encoding here, let's not simply return nil. +++ // We SHOULD try at least UTF-8 and after, Latin1. +++ if (!encoding) +++ encoding = NSUTF8StringEncoding; +++ ++ body = [[[NSString alloc] ++- initWithData:_data +++ initWithData:_data ++ encoding:encoding] autorelease]; +++ +++ if (!body) +++ body = [[[NSString alloc] initWithData:_data +++ encoding:NSISOLatin1StringEncoding] +++ autorelease]; ++ } ++ return body; ++ } ++Index: sope-mime/NGMime/NGMimePartParser.h ++=================================================================== ++--- sope-mime/NGMime/NGMimePartParser.h (revision 1664) +++++ sope-mime/NGMime/NGMimePartParser.h (working copy) ++@@ -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 (revision 1664) +++++ sope-mime/NGMime/NGMimePartParser.m (working copy) ++@@ -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/NGMimeAddressHeaderFieldGenerator.m ++=================================================================== ++--- sope-mime/NGMime/NGMimeAddressHeaderFieldGenerator.m (revision 1664) +++++ sope-mime/NGMime/NGMimeAddressHeaderFieldGenerator.m (working copy) ++@@ -105,10 +105,10 @@ ++ } ++ ++ tmp = [obj displayName]; ++- bufLen = [tmp cStringLength]; +++ bufLen = [tmp lengthOfBytesUsingEncoding: NSUTF8StringEncoding]; ++ ++- buffer = calloc(bufLen + 10, sizeof(char)); ++- [tmp getCString:buffer]; +++ buffer = calloc(bufLen, sizeof(char)); +++ [tmp getCString: buffer maxLength: bufLen encoding: NSUTF8StringEncoding]; ++ ++ cnt = 0; ++ doEnc = NO; ++@@ -117,11 +117,11 @@ ++ /* must encode chars outside ASCII 33..60, 62..126 ranges [RFC 2045, Sect. 6.7] ++ * RFC 2047, Sect. 4.2 also requires chars 63 and 95 to be encoded ++ * For spaces, quotation is fine */ ++- if ((unsigned char)buffer[cnt] < 32 || ++- (unsigned char)buffer[cnt] == 61 || ++- (unsigned char)buffer[cnt] == 63 || ++- (unsigned char)buffer[cnt] == 95 || ++- (unsigned char)buffer[cnt] > 126) { +++ if ((unichar)buffer[cnt] < 32 || +++ (unichar)buffer[cnt] == 61 || +++ (unichar)buffer[cnt] == 63 || +++ (unichar)buffer[cnt] == 95 || +++ (unichar)buffer[cnt] > 126) { ++ doEnc = YES; ++ break; ++ } ++@@ -130,8 +130,13 @@ ++ ++ if (doEnc) { ++ /* FIXME - better use UTF8 encoding! */ +++#if NeXT_Foundation_LIBRARY ++ unsigned char iso[] = "=?iso-8859-15?q?"; ++ unsigned isoLen = 16; +++#else +++ unsigned char iso[] = "=?utf-8?q?"; +++ unsigned isoLen = 10; +++#endif ++ unsigned char isoEnd[] = "?="; ++ unsigned isoEndLen = 2; ++ unsigned desLen; ++@@ -141,10 +146,10 @@ ++ { ++ NSData *data; ++ ++-#if APPLE_Foundation_LIBRARY || NeXT_Foundation_LIBRARY +++#if NeXT_Foundation_LIBRARY ++ data = [tmp dataUsingEncoding:NSISOLatin1StringEncoding]; ++ #else ++- data = [tmp dataUsingEncoding:NSISOLatin9StringEncoding]; +++ data = [tmp dataUsingEncoding:NSUTF8StringEncoding]; ++ #endif ++ ++ bufLen = [data length]; ++@@ -162,8 +167,9 @@ ++ des + isoLen, desLen - isoLen); ++ if ((int)desLen != -1) { ++ memcpy(des + isoLen + desLen, isoEnd, isoEndLen); ++- tmp = [NSString stringWithCString:(char *)des ++- length:(isoLen + desLen + isoEndLen)]; +++ tmp = [[NSString alloc] initWithData: [NSData dataWithBytes:(char *)des length:(isoLen + desLen + isoEndLen)] +++ encoding: NSISOLatin1StringEncoding]; +++ [tmp autorelease]; ++ } ++ else { ++ [self warnWithFormat: ++@@ -190,11 +196,7 @@ ++ } ++ } ++ ++-#if APPLE_Foundation_LIBRARY || NeXT_Foundation_LIBRARY ++ data = [result dataUsingEncoding:NSISOLatin1StringEncoding]; ++-#else ++- data = [result dataUsingEncoding:NSISOLatin9StringEncoding]; ++-#endif ++ [result release]; ++ ++ return data; ++Index: sope-mime/NGMime/NGMimeContentDispositionHeaderFieldGenerator.m ++=================================================================== ++--- sope-mime/NGMime/NGMimeContentDispositionHeaderFieldGenerator.m (revision 1664) +++++ sope-mime/NGMime/NGMimeContentDispositionHeaderFieldGenerator.m (working copy) ++@@ -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/NGExtensions/NSString+Ext.h ++=================================================================== ++--- sope-core/NGExtensions/NGExtensions/NSString+Ext.h (revision 1664) +++++ sope-core/NGExtensions/NGExtensions/NSString+Ext.h (working copy) ++@@ -30,6 +30,7 @@ ++ ++ @interface NSString(GSAdditions) ++ +++#if !GNUSTEP ++ - (NSString *)stringWithoutPrefix:(NSString *)_prefix; ++ - (NSString *)stringWithoutSuffix:(NSString *)_suffix; ++ ++@@ -39,6 +40,7 @@ ++ - (NSString *)stringByTrimmingLeadSpaces; ++ - (NSString *)stringByTrimmingTailSpaces; ++ - (NSString *)stringByTrimmingSpaces; +++#endif /* !GNUSTEP */ ++ ++ /* the following are not available in gstep-base 1.6 ? */ ++ - (NSString *)stringByTrimmingLeadWhiteSpaces; ++@@ -47,6 +49,8 @@ ++ ++ @end /* NSString(GSAdditions) */ ++ +++#if !GNUSTEP +++ ++ @interface NSMutableString(GNUstepCompatibility) ++ ++ - (void)trimLeadSpaces; ++@@ -55,6 +59,8 @@ ++ ++ @end /* NSMutableString(GNUstepCompatibility) */ ++ +++#endif /* !GNUSTEP */ +++ ++ #endif ++ ++ /* specific to libFoundation */ ++Index: sope-core/NGExtensions/FdExt.subproj/NSString+Ext.m ++=================================================================== ++--- sope-core/NGExtensions/FdExt.subproj/NSString+Ext.m (revision 1664) +++++ sope-core/NGExtensions/FdExt.subproj/NSString+Ext.m (working copy) ++@@ -39,18 +39,6 @@ ++ : (NSString *)[[self copy] autorelease]; ++ } ++ ++-- (NSString *)stringByReplacingString:(NSString *)_orignal ++- withString:(NSString *)_replacement ++-{ ++- /* very slow solution .. */ ++- ++- if ([self rangeOfString:_orignal].length == 0) ++- return [[self copy] autorelease]; ++- ++- return [[self componentsSeparatedByString:_orignal] ++- componentsJoinedByString:_replacement]; ++-} ++- ++ - (NSString *)stringByTrimmingLeadWhiteSpaces ++ { ++ // should check 'whitespaceAndNewlineCharacterSet' .. ++@@ -96,6 +84,25 @@ ++ return [[self copy] autorelease]; ++ } ++ +++- (NSString *)stringByTrimmingWhiteSpaces +++{ +++ return [[self stringByTrimmingTailWhiteSpaces] +++ stringByTrimmingLeadWhiteSpaces]; +++} +++ +++#ifndef GNUSTEP +++- (NSString *)stringByReplacingString:(NSString *)_orignal +++ withString:(NSString *)_replacement +++{ +++ /* very slow solution .. */ +++ +++ if ([self rangeOfString:_orignal].length == 0) +++ return [[self copy] autorelease]; +++ +++ return [[self componentsSeparatedByString:_orignal] +++ componentsJoinedByString:_replacement]; +++} +++ ++ - (NSString *)stringByTrimmingLeadSpaces ++ { ++ unsigned len; ++@@ -117,6 +124,7 @@ ++ else ++ return [[self copy] autorelease]; ++ } +++ ++ - (NSString *)stringByTrimmingTailSpaces ++ { ++ unsigned len; ++@@ -139,19 +147,17 @@ ++ return [[self copy] autorelease]; ++ } ++ ++-- (NSString *)stringByTrimmingWhiteSpaces ++-{ ++- return [[self stringByTrimmingTailWhiteSpaces] ++- stringByTrimmingLeadWhiteSpaces]; ++-} ++ - (NSString *)stringByTrimmingSpaces ++ { ++ return [[self stringByTrimmingTailSpaces] ++ stringByTrimmingLeadSpaces]; ++ } +++#endif ++ ++ @end /* NSString(GSAdditions) */ ++ +++#if !GNUSTEP +++ ++ @implementation NSMutableString(GNUstepCompatibility) ++ ++ - (void)trimLeadSpaces ++@@ -169,6 +175,8 @@ ++ ++ @end /* NSMutableString(GNUstepCompatibility) */ ++ +++#endif /* !GNUSTEP */ +++ ++ @implementation NSString(lfNSURLUtilities) ++ ++ - (BOOL)isAbsoluteURL ++Index: sope-core/NGExtensions/FdExt.subproj/NSString+Encoding.m ++=================================================================== ++--- sope-core/NGExtensions/FdExt.subproj/NSString+Encoding.m (revision 1664) +++++ sope-core/NGExtensions/FdExt.subproj/NSString+Encoding.m (working copy) ++@@ -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-core/NGExtensions/EOExt.subproj/EOGlobalID+Ext.m ++=================================================================== ++--- sope-core/NGExtensions/EOExt.subproj/EOGlobalID+Ext.m (revision 1664) +++++ sope-core/NGExtensions/EOExt.subproj/EOGlobalID+Ext.m (working copy) ++@@ -19,6 +19,7 @@ ++ 02111-1307, USA. ++ */ ++ +++#import ++ #import ++ #import ++ ++Index: sope-core/NGStreams/NGStream+serialization.m ++=================================================================== ++--- sope-core/NGStreams/NGStream+serialization.m (revision 1664) +++++ sope-core/NGStreams/NGStream+serialization.m (working copy) ++@@ -282,10 +282,10 @@ ++ else { ++ char *result = NULL; ++ ++-#if NeXT_Foundation_LIBRARY +++#if LIB_FOUNDATION_LIBRARY +++ result = NSZoneMallocAtomic(NULL, len + 1); +++#else ++ result = NSZoneMalloc(NULL, len + 1); ++-#else ++- result = NSZoneMallocAtomic(NULL, len + 1); ++ #endif ++ result[len] = '\0'; ++ ++Index: sope-core/NGStreams/NGStreams/NGDatagramSocket.h ++=================================================================== ++--- sope-core/NGStreams/NGStreams/NGDatagramSocket.h (revision 1664) +++++ sope-core/NGStreams/NGStreams/NGDatagramSocket.h (working copy) ++@@ -69,7 +69,7 @@ ++ + (id)socketBoundToAddress:(id)_address; ++ ++ #if !defined(WIN32) ++-+ (BOOL)socketPair:(id[2])_pair inDomain:(id)_domain; ++++ (BOOL)socketPair:(id[2])_pair; ++ #endif ++ ++ // accessors ++Index: sope-core/NGStreams/NGStreams/NGActiveSocket.h ++=================================================================== ++--- sope-core/NGStreams/NGStreams/NGActiveSocket.h (revision 1664) +++++ sope-core/NGStreams/NGStreams/NGActiveSocket.h (working copy) ++@@ -60,7 +60,7 @@ ++ - (id)initWithDomain:(id)_domain; // designated initializer ++ ++ #if !defined(WIN32) ++-+ (BOOL)socketPair:(id[2])_pair inDomain:(id)_domain; ++++ (BOOL)socketPair:(id[2])_pair; ++ #endif ++ ++ // ******************** operations ******************** ++Index: sope-core/NGStreams/NGGZipStream.m ++=================================================================== ++--- sope-core/NGStreams/NGGZipStream.m (revision 1664) +++++ sope-core/NGStreams/NGGZipStream.m (working copy) ++@@ -52,7 +52,7 @@ ++ @"invalid compression level %i (0-9)", _level); ++ ++ self->outBufLen = 2048; ++-#if GNU_RUNTIME +++#if LIB_FOUNDATION_LIBRARY ++ self->outBuf = NSZoneMallocAtomic([self zone], self->outBufLen); ++ self->outp = NSZoneMallocAtomic([self zone], sizeof(z_stream)); ++ #else ++Index: sope-core/NGStreams/NGDatagramSocket.m ++=================================================================== ++--- sope-core/NGStreams/NGDatagramSocket.m (revision 1664) +++++ sope-core/NGStreams/NGDatagramSocket.m (working copy) ++@@ -25,6 +25,8 @@ ++ #endif ++ ++ #include +++#include +++#include ++ #include "NGDatagramSocket.h" ++ #include "NGDatagramPacket.h" ++ #include "NGSocketExceptions.h" ++@@ -55,19 +57,21 @@ ++ ++ #if !defined(WIN32) || defined(__CYGWIN32__) ++ ++-+ (BOOL)socketPair:(id[2])_pair inDomain:(id)_domain { ++++ (BOOL)socketPair:(id[2])_pair { ++ int fds[2]; +++ NGLocalSocketDomain *domain; ++ ++ _pair[0] = nil; ++ _pair[1] = nil; ++ ++- if (socketpair([_domain socketDomain], SOCK_DGRAM, [_domain protocol], +++ domain = [NGLocalSocketDomain domain]; +++ if (socketpair([domain socketDomain], SOCK_DGRAM, [domain protocol], ++ fds) == 0) { ++ NGDatagramSocket *s1 = nil; ++ NGDatagramSocket *s2 = nil; ++ ++- s1 = [[self alloc] _initWithDomain:_domain descriptor:fds[0]]; ++- s2 = [[self alloc] _initWithDomain:_domain descriptor:fds[1]]; +++ s1 = [[self alloc] _initWithDomain:domain descriptor:fds[0]]; +++ s2 = [[self alloc] _initWithDomain:domain descriptor:fds[1]]; ++ s1 = AUTORELEASE(s1); ++ s2 = AUTORELEASE(s2); ++ ++@@ -112,7 +116,7 @@ ++ break; ++ } ++ [[[NGCouldNotCreateSocketException alloc] ++- initWithReason:reason domain:_domain] raise]; +++ initWithReason:reason domain:domain] raise]; ++ return NO; ++ } ++ } ++Index: sope-core/NGStreams/GNUmakefile.preamble ++=================================================================== ++--- sope-core/NGStreams/GNUmakefile.preamble (revision 1664) +++++ sope-core/NGStreams/GNUmakefile.preamble (working copy) ++@@ -1,6 +1,7 @@ ++ # compilation settings ++ ++-MACHCPU = $(shell echo $$MACHTYPE | cut -f 1 -d '-') +++# MACHCPU = $(shell echo $$MACHTYPE | cut -f 1 -d '-') +++MACHCPU = $(shell uname -m) ++ ++ libNGStreams_INCLUDE_DIRS += \ ++ -I$(GNUSTEP_TARGET_CPU)/$(GNUSTEP_TARGET_OS) \ ++Index: sope-core/NGStreams/ChangeLog ++=================================================================== ++--- sope-core/NGStreams/ChangeLog (revision 1664) +++++ sope-core/NGStreams/ChangeLog (working copy) ++@@ -1,3 +1,14 @@ +++2009-11-11 Wolfgang Sourdeau +++ +++ * NGActiveSocket.m (+socketPair): removed the domain: parameter as +++ only AF_LOCAL is permitted. Assign a default instance of +++ NGLocalSocketAddress to the sockets to "declare" the sockets as +++ connected. +++ +++ * NGActiveSocket.m (-setSendTimeout, -setReceiveTimeout): make use +++ of those methods to actually configure the timeouts on the socket +++ objects (via setsockopt and SO_RCVTIMEO and SO_SNDTIMEO). +++ ++ 2009-03-24 Wolfgang Sourdeau ++ ++ * GNUmakefile.preamble: add machine-type to include path (eg i386) (v4.7.57) ++Index: sope-core/NGStreams/NGCharBuffer.m ++=================================================================== ++--- sope-core/NGStreams/NGCharBuffer.m (revision 1664) +++++ sope-core/NGStreams/NGCharBuffer.m (working copy) ++@@ -46,11 +46,11 @@ ++ // Find first power of 2 >= to requested size ++ for (size = 2; size < _la; size *=2); ++ ++-#if NeXT_Foundation_LIBRARY ++- self->la = NSZoneMalloc([self zone], sizeof(LA_NGCharBuffer) * size); ++-#else +++#if LIB_FOUNDATION_LIBRARY ++ self->la = NSZoneMallocAtomic([self zone], ++ sizeof(LA_NGCharBuffer) * size); +++#else +++ self->la = NSZoneMalloc([self zone], sizeof(LA_NGCharBuffer) * size); ++ #endif ++ memset(self->la, 0, sizeof(LA_NGCharBuffer) * size); ++ ++Index: sope-core/NGStreams/NGActiveSocket.m ++=================================================================== ++--- sope-core/NGStreams/NGActiveSocket.m (revision 1664) +++++ sope-core/NGStreams/NGActiveSocket.m (working copy) ++@@ -62,6 +62,8 @@ ++ #include "common.h" ++ ++ #include +++#include +++#include ++ #include "NGActiveSocket.h" ++ #include "NGSocketExceptions.h" ++ #include "NGSocket+private.h" ++@@ -83,29 +85,35 @@ ++ ++ #if !defined(WIN32) || defined(__CYGWIN32__) ++ ++-+ (BOOL)socketPair:(id[2])_pair inDomain:(id)_domain { ++++ (BOOL)socketPair:(id[2])_pair { ++ int fds[2]; +++ NGLocalSocketDomain *domain; ++ ++ _pair[0] = nil; ++ _pair[1] = nil; ++ ++- if (socketpair([_domain socketDomain], SOCK_STREAM, [_domain protocol], +++ domain = [NGLocalSocketDomain domain]; +++ if (socketpair([domain socketDomain], SOCK_STREAM, [domain protocol], ++ fds) == 0) { ++ NGActiveSocket *s1 = nil; ++ NGActiveSocket *s2 = nil; +++ NGLocalSocketAddress *address; ++ ++- s1 = [[self alloc] _initWithDomain:_domain descriptor:fds[0]]; ++- s2 = [[self alloc] _initWithDomain:_domain descriptor:fds[1]]; +++ s1 = [[self alloc] _initWithDomain:domain descriptor:fds[0]]; +++ s2 = [[self alloc] _initWithDomain:domain descriptor:fds[1]]; ++ s1 = [s1 autorelease]; ++ s2 = [s2 autorelease]; ++ +++ address = [NGLocalSocketAddress address]; ++ if ((s1 != nil) && (s2 != nil)) { ++ s1->mode = NGStreamMode_readWrite; ++ s1->receiveTimeout = 0.0; ++ s1->sendTimeout = 0.0; +++ ASSIGN(s1->remoteAddress, address); ++ s2->mode = NGStreamMode_readWrite; ++ s2->receiveTimeout = 0.0; ++ s2->sendTimeout = 0.0; +++ ASSIGN(s2->remoteAddress, address); ++ ++ _pair[0] = s1; ++ _pair[1] = s2; ++@@ -152,7 +160,7 @@ ++ break; ++ } ++ [[[NGCouldNotCreateSocketException alloc] ++- initWithReason:reason domain:_domain] raise]; +++ initWithReason:reason domain:domain] raise]; ++ return NO; ++ } ++ } ++@@ -507,6 +515,13 @@ ++ } ++ ++ - (void)setSendTimeout:(NSTimeInterval)_timeout { +++ struct timeval tv; +++ +++ if ([self isConnected]) { +++ tv.tv_sec = (int) _timeout; +++ tv.tv_usec = 0; +++ setsockopt(self->fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof (struct timeval)); +++ } ++ self->sendTimeout = _timeout; ++ } ++ - (NSTimeInterval)sendTimeout { ++@@ -514,6 +529,13 @@ ++ } ++ ++ - (void)setReceiveTimeout:(NSTimeInterval)_timeout { +++ struct timeval tv; +++ +++ if ([self isConnected]) { +++ tv.tv_sec = (int) _timeout; +++ tv.tv_usec = 0; +++ setsockopt(self->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof (struct timeval)); +++ } ++ self->receiveTimeout = _timeout; ++ } ++ - (NSTimeInterval)receiveTimeout { ++Index: sope-xml/libxmlSAXDriver/libxmlHTMLSAXDriver.h ++=================================================================== ++--- sope-xml/libxmlSAXDriver/libxmlHTMLSAXDriver.h (revision 1664) +++++ sope-xml/libxmlSAXDriver/libxmlHTMLSAXDriver.h (working copy) ++@@ -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 (revision 1664) +++++ sope-xml/libxmlSAXDriver/libxmlHTMLSAXDriver.m (working copy) ++@@ -200,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; ++ } ++ ++Index: sope-appserver/mod_ngobjweb/GNUmakefile ++=================================================================== ++--- sope-appserver/mod_ngobjweb/GNUmakefile (revision 1664) +++++ sope-appserver/mod_ngobjweb/GNUmakefile (working copy) ++@@ -82,7 +82,7 @@ ++ ++ CFLAGS = -Wall -I. -fPIC \ ++ $(APXS_CFLAGS) $(APR_CFLAGS) \ ++- $(APXS_INCLUDE_DIRS) $(APR_INCLUDE_DIRS) +++ $(APXS_INCLUDE_DIRS) $(APR_INCLUDE_DIRS) -O0 -ggdb ++ ++ LDFLAGS = $(APXS_LDFLAGS) $(APR_LDFLAGS) -shared -fPIC ++ LDLIBS = $(APXS_LIBS) $(APR_LIBS) ++@@ -111,8 +111,7 @@ ++ apache-dir : ++ $(MKDIRS) $(GNUSTEP_INSTALLATION_DIR) ++ ++-install :: apache-dir all ++- $(INSTALL_PROGRAM) $(product) $(GNUSTEP_INSTALLATION_DIR) +++install :: ++ ++ install-usr-libexec :: all ++ $(INSTALL_PROGRAM) $(product) /usr/libexec/httpd/ ++Index: sope-appserver/NGObjWeb/WOWatchDogApplicationMain.m ++=================================================================== ++--- sope-appserver/NGObjWeb/WOWatchDogApplicationMain.m (revision 1664) +++++ sope-appserver/NGObjWeb/WOWatchDogApplicationMain.m (working copy) ++@@ -1,5 +1,6 @@ ++ /* ++ Copyright (C) 2000-2005 SKYRIX Software AG +++ Copyright (C) 2009 Inverse inc. ++ ++ This file is part of SOPE. ++ ++@@ -19,9 +20,29 @@ ++ 02111-1307, USA. ++ */ ++ ++-#import ++-#include +++#import +++#import +++#import +++#import +++#import +++#import +++#import +++#import +++#import +++#import +++#import ++ +++#import +++#import +++#import +++#import +++#import +++#import +++#import +++#import +++ +++#import "UnixSignalHandler.h" +++ ++ #if defined(__CYGWIN32__) || defined(__MINGW32__) ++ ++ int WOWatchDogApplicationMain ++@@ -39,201 +60,911 @@ ++ #include ++ #include ++ ++-static pid_t child = -1; ++-static NSString *pidFile = nil; ++-static time_t lastFailExit = 0; ++-static unsigned failExitCount = 0; ++-static BOOL killedChild = NO; +++static NSTimeInterval respawnDelay; /* seconds */ +++static const char *pidFile = NULL; ++ ++-static void killChild(void) { ++- if (child > 0) { ++- int status; ++- ++- fprintf(stderr, "watchdog[%i]: terminating child %i ..\n", getpid(), child); ++- ++- if (kill(child, SIGTERM) == 0) { ++- waitpid(child, &status, 0); ++- killedChild = YES; ++- ++- fprintf(stderr, " terminated child %i", child); ++- ++- if (WIFEXITED(status)) ++- fprintf(stderr, " exit=%i", WEXITSTATUS(status)); ++- if (WIFSIGNALED(status)) ++- fprintf(stderr, " signal=%i", WTERMSIG(status)); ++- ++- fprintf(stderr, ".\n"); ++- fflush(stderr); ++- ++- child = -1; ++- return; +++typedef enum { +++ WOChildStatusDown = 0, +++ WOChildStatusSpawning, +++ WOChildStatusReady, +++ WOChildStatusBusy, +++ WOChildStatusExcessive, +++ WOChildStatusTerminating, +++ WOChildStatusMax +++} WOChildStatus; +++ +++@class WOWatchDog; +++ +++@interface WOWatchDogChild : NSObject +++{ +++ int pid; +++ int SIGCHLDStatus; +++ int counter; +++ NGActiveSocket *controlSocket; +++ WOChildStatus status; +++ WOWatchDog *watchDog; +++ NSCalendarDate *lastSpawn; +++ BOOL loggedNotRespawn; +++} +++ +++- (void) setWatchDog: (WOWatchDog *) newWatchDog; +++ +++- (void) setPid: (int) newPid; +++- (int) pid; +++ +++- (void) setSIGCHLDStatus: (int) newSIGCHLDStatus; +++- (int) SIGCHLDStatus; +++- (void) handleSIGCHLDStatus; +++ +++- (void) setControlSocket: (NGActiveSocket *) newSocket; +++- (NGActiveSocket *) controlSocket; +++ +++- (void) setStatus: (WOChildStatus) newStatus; +++- (WOChildStatus) status; +++ +++- (void) setLastSpawn: (NSCalendarDate *) newLastSpawn; +++- (NSCalendarDate *) lastSpawn; +++- (NSCalendarDate *) nextSpawn; +++- (void) logNotRespawn; +++ +++- (BOOL) readMessage; +++ +++- (void) notify; +++- (void) terminate; +++ +++@end +++ +++@interface WOWatchDog : NSObject +++{ +++ NSString *appName; +++ int argc; +++ const char **argv; +++ +++ BOOL terminate; +++ BOOL willTerminate; +++ NSNumber *terminationSignal; +++ int pendingSIGCHLD; +++ int pendingSIGHUP; +++ +++ NGPassiveSocket *listeningSocket; +++ +++ int numberOfChildren; +++ NSMutableArray *children; +++ NSMutableArray *readyChildren; +++ NSMutableArray *downChildren; +++} +++ ++++ (id) sharedWatchDog; +++ +++- (void) declareChildReady: (WOWatchDogChild *) readyChild; +++- (void) declareChildDown: (WOWatchDogChild *) readyChild; +++ +++- (int) run: (NSString *) appName +++ argc: (int) argc +++ argv: (const char **) argv; +++ +++@end +++ +++@implementation WOWatchDogChild +++ ++++ (WOWatchDogChild *) watchDogChild +++{ +++ WOWatchDogChild *newChild; +++ +++ newChild = [self new]; +++ [newChild autorelease]; +++ +++ return newChild; +++} +++ +++- (id) init +++{ +++ if ((self = [super init])) +++ { +++ pid = -1; +++ controlSocket = nil; +++ SIGCHLDStatus = -1; +++ status = WOChildStatusDown; +++ counter = 0; +++ lastSpawn = nil; +++ loggedNotRespawn = NO; ++ } ++- else if (kill(child, SIGKILL)) { ++- waitpid(child, &status, 0); ++- killedChild = YES; ++- ++- fprintf(stderr, " killed child %i", child); ++- ++- if (WIFEXITED(status)) ++- fprintf(stderr, " exit=%i", WEXITSTATUS(status)); ++- if (WIFSIGNALED(status)) ++- fprintf(stderr, " signal=%i", WTERMSIG(status)); ++- ++- fprintf(stderr, ".\n"); ++- fflush(stderr); ++- ++- child = -1; ++- return; +++ +++ return self; +++} +++ +++- (void) dealloc +++{ +++ [self setControlSocket: nil]; +++ [lastSpawn release]; +++ [super dealloc]; +++} +++ +++- (void) setWatchDog: (WOWatchDog *) newWatchDog +++{ +++ watchDog = newWatchDog; +++} +++ +++- (void) setPid: (int) newPid +++{ +++ pid = newPid; +++} +++ +++- (int) pid +++{ +++ return pid; +++} +++ +++- (void) setSIGCHLDStatus: (int) newSIGCHLDStatus +++{ +++ SIGCHLDStatus = newSIGCHLDStatus; +++} +++ +++- (int) SIGCHLDStatus +++{ +++ return SIGCHLDStatus; +++} +++ +++- (void) handleSIGCHLDStatus +++{ +++ int code; +++ +++ [self logWithFormat: @"received SIGCHLD from pid %d", pid]; +++ code = WEXITSTATUS (SIGCHLDStatus); +++ if (code != 0) +++ [self logWithFormat: @"child %d exited with code %i", pid, code]; +++ if (WIFSIGNALED (SIGCHLDStatus)) +++ [self logWithFormat: @" (terminated due to signal %i%@)", +++ WTERMSIG (SIGCHLDStatus), +++ WCOREDUMP (SIGCHLDStatus) ? @", coredump" : @""]; +++ if (WIFSTOPPED (SIGCHLDStatus)) +++ [self logWithFormat: @" (stopped due to signal %i)", WSTOPSIG (SIGCHLDStatus)]; +++ SIGCHLDStatus = -1; +++ [self setStatus: WOChildStatusDown]; +++ [self setControlSocket: nil]; +++} +++ +++- (void) setControlSocket: (NGActiveSocket *) newSocket +++{ +++ NSRunLoop *runLoop; +++ +++ runLoop = [NSRunLoop currentRunLoop]; +++ if (controlSocket) +++ [runLoop removeEvent: (void *) [controlSocket fileDescriptor] +++ type: ET_RDESC +++ forMode: NSDefaultRunLoopMode +++ all: YES]; +++ [controlSocket close]; +++ ASSIGN (controlSocket, newSocket); +++ if (controlSocket) +++ [runLoop addEvent: (void *) [controlSocket fileDescriptor] +++ type: ET_RDESC +++ watcher: self +++ forMode: NSDefaultRunLoopMode]; +++} +++ +++- (NGActiveSocket *) controlSocket +++{ +++ return controlSocket; +++} +++ +++- (void) setStatus: (WOChildStatus) newStatus +++{ +++ status = newStatus; +++} +++ +++- (WOChildStatus) status +++{ +++ return status; +++} +++ +++- (void) setLastSpawn: (NSCalendarDate *) newLastSpawn +++{ +++ ASSIGN (lastSpawn, newLastSpawn); +++ loggedNotRespawn = NO; +++} +++ +++- (NSCalendarDate *) lastSpawn +++{ +++ return lastSpawn; +++} +++ +++- (NSCalendarDate *) nextSpawn +++{ +++ return [lastSpawn addYear: 0 month: 0 day: 0 +++ hour: 0 minute: 0 +++ second: respawnDelay]; +++} +++ +++- (void) logNotRespawn +++{ +++ if (!loggedNotRespawn) +++ { +++ [self logWithFormat: +++ @"avoiding to respawn child before %@", [self nextSpawn]]; +++ loggedNotRespawn = YES; ++ } +++} +++ +++- (BOOL) readMessage +++{ +++ WOChildMessage message; +++ BOOL rc; +++ +++ if ([controlSocket readBytes: &message +++ count: sizeof (WOChildMessage)] == NGStreamError) { +++ rc = NO; +++ [self errorWithFormat: @"FAILURE receiving status for child %d", pid]; +++ [self errorWithFormat: @" socket: %@", controlSocket]; ++ } +++ else { +++ rc = YES; +++ if (message == WOChildMessageAccept) { +++ status = WOChildStatusBusy; +++ } +++ else if (message == WOChildMessageReady) { +++ status = WOChildStatusReady; +++ [watchDog declareChildReady: self]; +++ } +++ // else if (message == WOChildMessageShutdown) { +++ // status = WOChildStatusDown; +++ // [watchDog declareChildDown: self]; +++ // } +++ // [self logStatus]; +++ } +++ +++ return rc; ++ } ++ ++-static void _writePid(NSString *pidFile) { ++- if ([pidFile length] > 0) { ++- FILE *pf; ++- ++- if ((pf = fopen([pidFile cString], "w"))) { ++- fprintf(pf, "%i\n", getpid()); ++- fflush(pf); ++- fclose(pf); +++- (BOOL) _sendMessage: (WOChildMessage) message +++{ +++ return ([controlSocket writeBytes: &message +++ count: sizeof (WOChildMessage)] != NGStreamError +++ && [self readMessage]); +++} +++ +++- (void) _killKill +++{ +++ if (status != WOChildStatusDown) { +++ [self warnWithFormat: @"sending KILL signal to pid %d", pid]; +++ kill (pid, SIGKILL); +++ } +++} +++ +++- (void) _kill +++{ +++ if (status != WOChildStatusDown) { +++ [self logWithFormat: @"sending terminate signal to pid %d", pid]; +++ status = WOChildStatusTerminating; +++ kill (pid, SIGTERM); +++ /* We hardcode a 5 minutes delay before ensuring that all children are +++ terminated. This enables long requests to finish properly while +++ avoiding 100% CPU usage for deadlocked children. */ +++ [NSTimer scheduledTimerWithTimeInterval: 5.0 * 60 +++ target: self +++ selector: @selector (_killKill) +++ userInfo: nil +++ repeats: NO]; +++ } +++} +++ +++- (void) notify +++{ +++ WOChildMessage message; +++ +++ counter++; +++ message = WOChildMessageAccept; +++ if (![self _sendMessage: message]) { +++ [self errorWithFormat: @"FAILURE notifying child %d", pid]; +++ [self _kill]; +++ } +++} +++ +++- (void) terminate +++{ +++ if (status == WOChildStatusDown) { +++ [self logWithFormat: @"child is already down"]; +++ } else { +++ [self setControlSocket: nil]; +++ [self _kill]; +++ } +++} +++ +++- (void) receivedEvent: (void*)data +++ type: (RunLoopEventType)type +++ extra: (void*)extra +++ forMode: (NSString*)mode +++{ +++ if ([controlSocket isAlive]) +++ [self readMessage]; +++ else { +++ /* This happens when a socket has been closed by the child but the child +++ has not terminated yet. */ +++ [[NSRunLoop currentRunLoop] removeEvent: data +++ type: ET_RDESC +++ forMode: NSDefaultRunLoopMode +++ all: YES]; +++ [self setControlSocket: nil]; +++ } +++} +++ +++@end +++ +++@implementation WOWatchDog +++ ++++ (id) sharedWatchDog +++{ +++ static WOWatchDog *sharedWatchDog = nil; +++ +++ if (!sharedWatchDog) +++ sharedWatchDog = [self new]; +++ +++ return sharedWatchDog; +++} +++ +++- (id) init +++{ +++ if ((self = [super init])) +++ { +++ listeningSocket = nil; +++ terminate = NO; +++ willTerminate = NO; +++ terminationSignal = nil; +++ pendingSIGCHLD = 0; +++ pendingSIGHUP = 0; +++ +++ numberOfChildren = 0; +++ children = [[NSMutableArray alloc] initWithCapacity: 10]; +++ readyChildren = [[NSMutableArray alloc] initWithCapacity: 10]; +++ downChildren = [[NSMutableArray alloc] initWithCapacity: 10]; ++ } +++ +++ return self; +++} +++ +++- (void) _releaseListeningSocket +++{ +++ if (listeningSocket) { +++ [[NSRunLoop currentRunLoop] removeEvent: (void *) [listeningSocket fileDescriptor] +++ type: ET_RDESC +++ forMode: NSDefaultRunLoopMode +++ all: YES]; +++ [listeningSocket close]; +++ [listeningSocket release]; +++ listeningSocket = nil; ++ } ++ } ++-static void _delPid(void) { ++- if ([pidFile length] > 0) { ++- if (unlink([pidFile cString]) == 0) ++- pidFile = nil; +++ +++- (void) dealloc +++{ +++ [self _releaseListeningSocket]; +++ [terminationSignal release]; +++ [appName release]; +++ [children release]; +++ [super dealloc]; +++} +++ +++- (void) _runChildWithControlSocket: (NGActiveSocket *) controlSocket +++{ +++ WOApplication *app; +++ extern char **environ; +++ +++ [NSProcessInfo initializeWithArguments: (char **) argv +++ count: argc +++ environment: environ]; +++ NGInitTextStdio(); +++ app = [NSClassFromString(appName) new]; +++ [app autorelease]; +++ [app setListeningSocket: listeningSocket]; +++ [app setControlSocket: controlSocket]; +++ [app run]; +++} +++ +++- (void) receivedEvent: (void*)data +++ type: (RunLoopEventType)type +++ extra: (void*)extra +++ forMode: (NSString*)mode +++{ +++ int nextId; +++ WOWatchDogChild *child; +++ +++ // NSLog (@"have a child accept the connection"); +++ nextId = [readyChildren count] - 1; +++ if (nextId > -1) +++ { +++ child = [readyChildren objectAtIndex: nextId]; +++ [readyChildren removeObjectAtIndex: nextId]; +++ [child notify]; +++ } +++ // else +++ // NSLog (@"all children busy"); +++} +++ +++- (void) _cleanupSignalAndEventHandlers +++{ +++ int count; +++ NSRunLoop *runLoop; +++ +++ [[UnixSignalHandler sharedHandler] removeObserver: self]; +++ +++ runLoop = [NSRunLoop currentRunLoop]; +++ [runLoop removeEvent: (void *) [listeningSocket fileDescriptor] +++ type: ET_RDESC +++ forMode: NSDefaultRunLoopMode +++ all: YES]; +++ +++ for (count = 0; count < numberOfChildren; count++) { +++ [[children objectAtIndex: count] setControlSocket: nil]; +++ // controlSocket = [[children objectAtIndex: count] controlSocket]; +++ // if (controlSocket) +++ // [runLoop removeEvent: (void *) [controlSocket fileDescriptor] +++ // type: ET_RDESC +++ // forMode: NSDefaultRunLoopMode +++ // all: YES]; ++ } ++ } ++ ++-static void exitWatchdog(void) { ++- killChild(); ++- _delPid(); +++- (BOOL) _spawnChild: (WOWatchDogChild *) child +++{ +++ NGActiveSocket *pair[2]; +++ BOOL isChild; +++ int childPid; +++ extern char **environ; +++ +++ isChild = NO; +++ +++ if ([NGActiveSocket socketPair: pair]) { +++ childPid = fork (); +++ if (childPid == 0) { +++ setsid (); +++ isChild = YES; +++ [self _cleanupSignalAndEventHandlers]; +++ [self _runChildWithControlSocket: pair[0]]; +++ } else if (childPid > 0) { +++ [self logWithFormat: @"child spawned with pid %d", childPid]; +++ [child setPid: childPid]; +++ [child setStatus: WOChildStatusSpawning]; +++ [child setControlSocket: pair[1]]; +++ [child setLastSpawn: [NSCalendarDate date]]; +++ // [self logWithFormat: @"parent ready for child: %d", childPid]; +++ } else { +++ perror ("fork"); +++ } +++ } +++ +++ return isChild; ++ } ++ ++-static void wsignalHandler(int _signal) { ++- switch (_signal) { ++- case SIGINT: ++- /* Control-C */ ++- fprintf(stderr, "[%i]: watchdog handling signal ctrl-c ..\n", getpid()); ++- killChild(); ++- exit(0); ++- /* shouldn't get here */ ++- abort(); +++- (void) _ensureNumberOfChildren +++{ +++ int currentNumber, delta, count, min, max; +++ WOWatchDogChild *child; ++ ++- case SIGSEGV: ++- /* Coredump ! */ ++- fprintf(stderr, ++- "[%i]: watchdog handling segmentation fault " ++- "(SERIOUS PROBLEM) ..\n", ++- getpid()); ++- killChild(); ++- exit(123); ++- /* shouldn't get here */ ++- abort(); +++ currentNumber = [children count]; +++ if (currentNumber < numberOfChildren) { +++ delta = numberOfChildren - currentNumber; +++ for (count = 0; count < delta; count++) { +++ child = [WOWatchDogChild watchDogChild]; +++ [child setWatchDog: self]; +++ [children addObject: child]; +++ [downChildren addObject: child]; +++ } +++ [self logWithFormat: @"preparing %d children", delta]; +++ } +++ else if (currentNumber > numberOfChildren) { +++ delta = currentNumber - numberOfChildren; +++ max = [downChildren count]; +++ if (max > delta) +++ min = max - delta; +++ else +++ min = 0; +++ for (count = max - 1; count >= min; count--) { +++ child = [downChildren objectAtIndex: count]; +++ [downChildren removeObjectAtIndex: count]; +++ [children removeObject: child]; +++ delta--; +++ [self logWithFormat: @"%d processes purged from pool", delta]; +++ } ++ ++- case SIGTERM: ++- /* TERM signal (kill 'pid') */ ++- fprintf(stderr, "[%i]: watchdog handling SIGTERM ..\n", getpid()); ++- killChild(); ++- exit(0); ++- /* shouldn't get here */ ++- abort(); ++- ++- case SIGHUP: ++- /* HUP signal (restart children) */ ++- fprintf(stderr, "[%i]: watchdog handling SIGHUP ..\n", getpid()); ++- killChild(); ++- killedChild = YES; ++- signal(_signal, wsignalHandler); ++- return; ++- ++- case SIGCHLD: ++- break; ++- ++- default: ++- fprintf(stderr, "[%i]: watchdog handling signal %i ..\n", ++- getpid(), _signal); ++- break; +++ max = [readyChildren count]; +++ if (max > delta) +++ max -= delta; +++ for (count = max - 1; count > -1; count--) { +++ child = [readyChildren objectAtIndex: count]; +++ [readyChildren removeObjectAtIndex: count]; +++ [child terminate]; +++ [child setStatus: WOChildStatusExcessive]; +++ delta--; +++ } +++ [self logWithFormat: @"%d processes left to terminate", delta]; ++ } ++- fflush(stderr); ++- ++- switch (_signal) { ++- case SIGTERM: ++- case SIGINT: ++- case SIGKILL: ++- case SIGILL: ++- killChild(); ++- exit(0); ++- break; ++- ++- case SIGHUP: ++- killChild(); ++- break; ++- ++- case SIGCHLD: { ++- int returnStatus; ++- pid_t result; ++- ++- // NSLog(@"SIGNAL: SIGCHLD"); ++- // fetch return state ++- ++- do { ++- result = waitpid(-1, &returnStatus, WNOHANG); ++- if (result > 0) { ++- fprintf(stderr, "[%i]: process %i exited with code %i", ++- getpid(), (int)result, WEXITSTATUS(returnStatus)); +++} ++ ++- if (WIFSIGNALED(returnStatus)) { ++- fprintf(stderr, " (terminated due to signal %i%s)", ++- WTERMSIG(returnStatus), ++- WCOREDUMP(returnStatus) ? ", coredump" : ""); ++- } ++- if (WIFSTOPPED(returnStatus)) { ++- fprintf(stderr, " (stopped due to signal %i)", ++- WSTOPSIG(returnStatus)); ++- } ++- ++- fprintf(stderr, "\n"); ++- fflush(stderr); +++- (void) _noop +++{ +++} +++ +++- (BOOL) _ensureChildren +++{ +++ int count, max; +++ WOWatchDogChild *child; +++ BOOL isChild, delayed; +++ NSCalendarDate *now, *nextSpawn; +++ +++ isChild = NO; +++ +++ if (!willTerminate) { +++ [self _ensureNumberOfChildren]; +++ max = [downChildren count]; +++ for (count = max - 1; !isChild && count > -1; count--) { +++ delayed = NO; +++ child = [downChildren objectAtIndex: count]; +++ +++ if ([child status] == WOChildStatusExcessive) +++ [children removeObject: child]; +++ else { +++ now = [NSCalendarDate date]; +++ nextSpawn = [child nextSpawn]; +++ if ([nextSpawn earlierDate: now] == nextSpawn) +++ isChild = [self _spawnChild: child]; +++ else { +++ delayed = YES; +++ [child logNotRespawn]; ++ } ++ } ++- while (result > 0); ++- ++- break; +++ if (!delayed) +++ [downChildren removeObjectAtIndex: count]; ++ } ++- ++- default: ++- fprintf(stderr, "watchdog[%i]: caught signal %i\n", getpid(), _signal); ++- break; ++ } ++- signal(_signal, wsignalHandler); +++ +++ return isChild; ++ } ++ ++-static void signalHandler(int _signal) { ++- fprintf(stderr, "[%i]: handling signal %i ..\n", ++- getpid(), _signal); ++- fflush(stderr); ++- ++- switch (_signal) { ++- case SIGPIPE: ++- fprintf(stderr, "[%i]: caught signal SIGPIPE\n", getpid()); ++- break; ++- ++- default: ++- fprintf(stderr, "[%i]: caught signal %i\n", getpid(), _signal); ++- break; +++/* SOPE on GNUstep does not need to parse the argument line, since the +++ arguments will be put in the NSArgumentDomain. I don't know about +++ libFoundation but OSX is supposed to act the same way. */ +++- (NGInternetSocketAddress *) _listeningAddress +++{ +++ NGInternetSocketAddress *listeningAddress; +++ NSUserDefaults *ud; +++ id port, allow; +++ +++ listeningAddress = nil; +++ +++ ud = [NSUserDefaults standardUserDefaults]; +++ port = [ud objectForKey:@"p"]; +++ if (!port) { +++ port = [ud objectForKey:@"WOPort"]; +++ if (!port) +++ port = @"auto"; ++ } ++- signal(_signal, signalHandler); +++ allow = [ud objectForKey:@"WOHttpAllowHost"]; +++ if (allow) +++ [self warnWithFormat: @"'WOHttpAllowHost' is ignored in watchdog mode, use a real firewall instead"]; +++ if ([port isKindOfClass: [NSString class]]) { +++ if ([port isEqualToString: @"auto"]) { +++ listeningAddress +++ = [[NGInternetSocketAddress alloc] initWithPort:0 onHost:@"127.0.0.1"]; +++ [listeningAddress autorelease]; +++ } else if ([port rangeOfString: @":"].location == NSNotFound) { +++ if (allow) +++ listeningAddress = +++ [NGInternetSocketAddress wildcardAddressWithPort:[port intValue]]; +++ else +++ port = [NSString stringWithFormat: @"127.0.0.1:%d", [port intValue]]; +++ } +++ } +++ else { +++ if (allow) +++ listeningAddress = +++ [NGInternetSocketAddress wildcardAddressWithPort:[port intValue]]; +++ else { +++ port = [NSString stringWithFormat: @"127.0.0.1:%@", port]; +++ } +++ } +++ +++ if (!listeningAddress) +++ listeningAddress = (NGInternetSocketAddress *) NGSocketAddressFromString(port); +++ +++ return listeningAddress; ++ } ++ +++- (BOOL) _prepareListeningSocket +++{ +++ NGInternetSocketAddress *addr; +++ NSString *address; +++ BOOL rc; +++ int backlog; +++ +++ addr = [self _listeningAddress]; +++ NS_DURING { +++ [listeningSocket release]; +++ listeningSocket = [[NGPassiveSocket alloc] initWithDomain: [addr domain]]; +++ [listeningSocket bindToAddress: addr]; +++ backlog = [[NSUserDefaults standardUserDefaults] +++ integerForKey: @"WOListenQueueSize"]; +++ if (!backlog) +++ backlog = 5; +++ [listeningSocket listenWithBacklog: backlog]; +++ address = [addr address]; +++ if (!address) +++ address = @"*"; +++ [self logWithFormat: @"listening on %@:%d", address, [addr port]]; +++ [[NSRunLoop currentRunLoop] addEvent: (void *) [listeningSocket fileDescriptor] +++ type: ET_RDESC +++ watcher: self +++ forMode: NSDefaultRunLoopMode]; +++ rc = YES; +++ } +++ NS_HANDLER { +++ // [self logWithFormat:@"failure listening on address 127.0.0.1:%d", port]; +++ rc = NO; +++ } +++ NS_ENDHANDLER; +++ +++ return rc; +++} +++ +++- (WOWatchDogChild *) _childWithPID: (pid_t) childPid +++{ +++ WOWatchDogChild *currentChild, *child; +++ int count; +++ +++ child = nil; +++ for (count = 0; !child && count < numberOfChildren; count++) { +++ currentChild = [children objectAtIndex: count]; +++ if ([currentChild pid] == childPid) +++ child = currentChild; +++ } +++ +++ return child; +++} +++ +++- (void) _handleSIGPIPE:(NSNumber *)_signal { +++ [self logWithFormat: @"received SIGPIPE (unhandled)"]; +++} +++ +++- (void) _handleSIGCHLD:(NSNumber *)_signal { +++ WOWatchDogChild *child; +++ pid_t childPid; +++ int status; +++ +++ childPid = wait (&status); +++ if (childPid > -1) { +++ pendingSIGCHLD++; +++ child = [self _childWithPID: childPid]; +++ [child setSIGCHLDStatus: status]; +++ } +++} +++ +++- (void) _handleTermination:(NSNumber *)_signal { +++ if (!terminationSignal) { +++ ASSIGN (terminationSignal, _signal); +++ if (pidFile) +++ unlink (pidFile); +++ } +++} +++ +++- (void) _handleSIGHUP:(NSNumber *)_signal { +++ pendingSIGHUP++; +++} +++ +++- (void) _setupSignals +++{ +++#if !defined(__MINGW32__) && !defined(NeXT_Foundation_LIBRARY) +++ UnixSignalHandler *us; +++ +++ us = [UnixSignalHandler sharedHandler]; +++ [us addObserver:self selector:@selector(_handleSIGPIPE:) +++ forSignal:SIGPIPE immediatelyNotifyOnSignal:YES]; +++ [us addObserver:self selector:@selector(_handleSIGCHLD:) +++ forSignal:SIGCHLD immediatelyNotifyOnSignal:YES]; +++ [us addObserver:self selector:@selector(_handleTermination:) +++ forSignal:SIGINT immediatelyNotifyOnSignal:YES]; +++ [us addObserver:self selector:@selector(_handleTermination:) +++ forSignal:SIGTERM immediatelyNotifyOnSignal:YES]; +++ // [us addObserver:self selector:@selector(_handleSIGKILL:) +++ // forSignal:SIGKILL immediatelyNotifyOnSignal:YES]; +++ [us addObserver:self selector:@selector(_handleSIGHUP:) +++ forSignal:SIGHUP immediatelyNotifyOnSignal:YES]; +++#endif +++} +++ +++- (void) declareChildReady: (WOWatchDogChild *) readyChild +++{ +++ [readyChildren addObject: readyChild]; +++} +++ +++- (void) declareChildDown: (WOWatchDogChild *) downChild +++{ +++ if (![downChildren containsObject: downChild]) +++ [downChildren addObject: downChild]; +++} +++ +++- (void) _ensureWorkersCount +++{ +++ int newNumberOfChildren; +++ NSUserDefaults *ud; +++ +++ ud = [NSUserDefaults standardUserDefaults]; +++ [ud synchronize]; +++ newNumberOfChildren = [ud integerForKey: @"WOHttpAdaptorForkCount"]; +++ if (newNumberOfChildren) +++ [self logWithFormat: @"user default 'WOHttpAdaptorForkCount' has been" +++ " replaced with 'WOWorkersCount'"]; +++ else +++ newNumberOfChildren = [ud integerForKey: @"WOWorkersCount"]; +++ if (newNumberOfChildren < 1) +++ newNumberOfChildren = 1; +++ numberOfChildren = newNumberOfChildren; +++} +++ +++- (void) _handlePostTerminationSignal +++{ +++ WOWatchDogChild *child; +++ int count; +++ +++ [self logWithFormat: @"Terminating with signal %@", terminationSignal]; +++ [self _releaseListeningSocket]; +++ for (count = 0; count < numberOfChildren; count++) { +++ child = [children objectAtIndex: count]; +++ if ([child status] != WOChildStatusDown +++ && [child status] != WOChildStatusTerminating) +++ [child terminate]; +++ } +++ [terminationSignal release]; +++ terminationSignal = nil; +++ if ([downChildren count] == numberOfChildren) { +++ [self logWithFormat: @"all children exited. We now terminate."]; +++ terminate = YES; +++ } +++ else +++ willTerminate = YES; +++} +++ +++- (void) _handlePostSIGCHLDStatus +++{ +++ int status, count; +++ WOWatchDogChild *child; +++ +++ for (count = 0; pendingSIGCHLD && count < numberOfChildren; count++) { +++ child = [children objectAtIndex: count]; +++ status = [child SIGCHLDStatus]; +++ if (status != -1) { +++ [child handleSIGCHLDStatus]; +++ pendingSIGCHLD--; +++ [self declareChildDown: child]; +++ if (willTerminate && [downChildren count] == numberOfChildren) { +++ [self logWithFormat: @"all children exited. We now terminate."]; +++ terminate = YES; +++ } +++ } +++ } +++} +++ +++- (void) _setupProcessName +++{ +++ NSProcessInfo *processInfo; +++ NSString *name; +++ +++ /* this does not seem to work */ +++ processInfo = [NSProcessInfo processInfo]; +++ name = [processInfo processName]; +++ if (!name) +++ name = @""; +++ [processInfo setProcessName: [NSString stringWithFormat: @"%@: %@ watchdog", +++ name, appName]]; +++} +++ +++- (int) run: (NSString *) newAppName +++ argc: (int) newArgC argv: (const char **) newArgV +++{ +++ NSAutoreleasePool *pool; +++ NSRunLoop *runLoop; +++ NSDate *limitDate; +++ BOOL listening; +++ int retries; +++ +++ willTerminate = NO; +++ +++ ASSIGN (appName, newAppName); +++ [self _setupProcessName]; +++ +++ argc = newArgC; +++ argv = newArgV; +++ +++ listening = NO; +++ retries = 0; +++ while (!listening && retries < 5) { +++ listening = [self _prepareListeningSocket]; +++ retries++; +++ if (!listening) { +++ [self warnWithFormat: @"listening socket: attempt %d failed", retries]; +++ [NSThread sleepForTimeInterval: 1.0]; +++ } +++ } +++ if (listening) { +++ [self logWithFormat: @"watchdog process pid: %d", getpid ()]; +++ [self _setupSignals]; +++ [self _ensureWorkersCount]; +++ +++ // NSLog (@"ready to process requests"); +++ runLoop = [NSRunLoop currentRunLoop]; +++ +++ /* This timer ensures the looping of the runloop at reasonable intervals +++ for correct processing of signal handlers. */ +++ [NSTimer scheduledTimerWithTimeInterval: 0.5 +++ target: self +++ selector: @selector (_noop) +++ userInfo: nil +++ repeats: YES]; +++ terminate = NO; +++ while (!terminate) { +++ pool = [NSAutoreleasePool new]; +++ +++ while (pendingSIGHUP) { +++ [self logWithFormat: @"received SIGHUP"]; +++ [self _ensureWorkersCount]; +++ pendingSIGHUP--; +++ } +++ +++ // [self logWithFormat: @"watchdog loop"]; +++ NS_DURING { +++ terminate = [self _ensureChildren]; +++ if (!terminate) { +++ limitDate = [runLoop limitDateForMode:NSDefaultRunLoopMode]; +++ [runLoop runMode: NSDefaultRunLoopMode beforeDate: limitDate]; +++ } +++ } +++ NS_HANDLER { +++ terminate = YES; +++ [self errorWithFormat: +++ @"an exception occured in runloop %@", localException]; +++ } +++ NS_ENDHANDLER; +++ +++ if (!terminate) { +++ if (terminationSignal) +++ [self _handlePostTerminationSignal]; +++ while (pendingSIGCHLD) +++ [self _handlePostSIGCHLDStatus]; +++ } +++ [pool release]; +++ } +++ +++ [[UnixSignalHandler sharedHandler] removeObserver: self]; +++ } +++ else +++ [self errorWithFormat: @"unable to listen on specified port," +++ @" check that no other process is already using it"]; +++ +++ return 0; +++} +++ +++@end +++ +++static BOOL _writePid(NSString *nsPidFile) { +++ NSString *pid; +++ BOOL rc; +++ +++ pid = [NSString stringWithFormat: @"%d", getpid()]; +++ rc = [pid writeToFile: nsPidFile atomically: NO]; +++ +++ return rc; +++} +++ ++ int WOWatchDogApplicationMain ++ (NSString *appName, int argc, const char *argv[]) ++ { ++ NSAutoreleasePool *pool; ++ NSUserDefaults *ud; +++ NSString *logFile, *nsPidFile; +++ int rc, stdErrNo; +++ pid_t childPid; +++ NSProcessInfo *processInfo; ++ ++ pool = [[NSAutoreleasePool alloc] init]; +++ ++ #if LIB_FOUNDATION_LIBRARY || defined(GS_PASS_ARGUMENTS) ++ { ++ extern char **environ; ++@@ -241,179 +972,68 @@ ++ environment:(void*)environ]; ++ } ++ #endif ++- +++ +++ /* This invocation forces the class initialization of WOCoreApplication, +++ which causes the NSUserDefaults to be initialized as well with +++ Defaults.plist. */ +++ [NSClassFromString (appName) class]; +++ ++ ud = [NSUserDefaults standardUserDefaults]; ++- ++- /* default is to use the watch dog! */ ++- /* Note: the Defaults.plist is not yet loaded at this stage! */ ++- if ([ud objectForKey:@"WOUseWatchDog"] != nil) { ++- if (![ud boolForKey:@"WOUseWatchDog"]) ++- return WOApplicationMain(appName, argc, argv); +++ processInfo = [NSProcessInfo processInfo]; +++ +++ logFile = [ud objectForKey: @"WOLogFile"]; +++ if (!logFile) +++ logFile = [NSString stringWithFormat: @"/var/log/%@/%@.log", +++ [processInfo processName], +++ [processInfo processName]]; +++ if (![logFile isEqualToString: @"-"]) { +++ stdErrNo = dup(fileno(stderr)); +++ stdout = freopen([logFile cString], "a", stdout); +++ stderr = freopen([logFile cString], "a", stderr); ++ } ++- ++- /* watch dog */ ++- { ++- int failCount = 0; ++- int forkCount = 0; ++- BOOL repeat = YES; ++- BOOL isVerbose = NO; ++- ++- isVerbose = [[ud objectForKey:@"watchdog_verbose"] boolValue]; ++- pidFile = [[[ud objectForKey:@"watchdog_pidfile"] stringValue] copy]; ++- ++- /* write current pid to pidfile */ ++- _writePid(pidFile); ++- ++- /* register exit handler */ ++- atexit(exitWatchdog); ++- ++- /* register signal handlers of watch dog */ ++- signal(SIGPIPE, wsignalHandler); ++- signal(SIGCHLD, wsignalHandler); ++- signal(SIGINT, wsignalHandler); ++- signal(SIGTERM, wsignalHandler); ++- signal(SIGKILL, wsignalHandler); ++- signal(SIGHUP, wsignalHandler); ++- ++- /* loop */ ++- ++- while (repeat) { ++- time_t clientStartTime; ++- ++- clientStartTime = time(NULL); ++- killedChild = NO; ++- ++- if ((child = fork()) == -1) { ++- fprintf(stderr, "[%i]: fork failed: %s\n", getpid(), strerror(errno)); ++- failCount++; ++- ++- if (failCount > 5) { ++- fprintf(stderr, " fork failed %i times, sleeping 60 seconds ..\n", ++- failCount); ++- sleep(60); ++- } ++- else { ++- sleep(1); ++- } +++ if (stdout && stderr) { +++ if ([ud boolForKey: @"WONoDetach"]) +++ childPid = 0; +++ else +++ childPid = fork(); +++ +++ if (childPid) { +++ rc = 0; +++ } +++ else { +++ nsPidFile = [ud objectForKey: @"WOPidFile"]; +++ if (!nsPidFile) +++ nsPidFile = [NSString stringWithFormat: @"/var/run/%@/%@.pid", +++ [processInfo processName], +++ [processInfo processName]]; +++ pidFile = [nsPidFile UTF8String]; +++ if (_writePid(nsPidFile)) { +++ respawnDelay = [ud integerForKey: @"WORespawnDelay"]; +++ if (!respawnDelay) +++ respawnDelay = 5; +++ /* default is to use the watch dog! */ +++ if ([ud objectForKey:@"WOUseWatchDog"] != nil +++ && ![ud boolForKey:@"WOUseWatchDog"]) +++ rc = WOApplicationMain(appName, argc, argv); +++ else +++ rc = [[WOWatchDog sharedWatchDog] run: appName argc: argc argv: argv]; ++ } ++ else { ++- if (child == 0) { ++- /* child process */ ++- signal(SIGPIPE, SIG_DFL); ++- signal(SIGCHLD, SIG_DFL); ++- signal(SIGINT, SIG_DFL); ++- signal(SIGTERM, SIG_DFL); ++- signal(SIGKILL, SIG_DFL); ++- ++- if (isVerbose) ++- fprintf(stderr, "starting child %i ..\n", getpid()); ++- ++- pidFile = [pidFile stringByAppendingPathExtension:@"child"]; ++- _writePid(pidFile); ++- ++- atexit(_delPid); ++- ++- exit(WOApplicationMain(appName, argc, argv)); ++- ++- /* shouldn't even get here ! */ ++- fprintf(stderr, "internal server error !\n"); ++- abort(); ++- } ++- else { ++- /* parent (watch dog) */ ++- int status = 0; ++- pid_t result = 0; ++- time_t clientStopTime; ++- unsigned uptime; ++- ++- forkCount++; ++- ++- if (isVerbose) { ++- fprintf(stderr, "forked child process %i (#%i) ..\n", ++- child, forkCount); ++- } ++- ++- failCount = 0; ++- status = 0; ++- ++- if ((result = waitpid(child, &status, 0)) == -1) { ++- if (killedChild) { ++- killedChild = NO; ++- continue; ++- } ++- ++- fprintf(stderr, ++- "### waiting for child %i (#%i) failed: %s\n", ++- child, forkCount, strerror(errno)); ++- continue; ++- } ++- ++- clientStopTime = time(NULL); ++- uptime = clientStopTime - clientStartTime; ++- ++- if (WIFSIGNALED(status)) { ++- fprintf(stderr, ++- "### child %i (#%i) was terminated by signal %i " ++- "(uptime=%ds).\n", ++- child, forkCount, WTERMSIG(status), uptime); ++- ++- lastFailExit = time(NULL); ++- failExitCount++; ++- } ++- else if (WIFEXITED(status)) { ++- unsigned exitCode; ++- ++- if ((exitCode = WEXITSTATUS(status)) != 0) { ++- time_t now; ++- ++- now = time(NULL); ++- ++- if (uptime < 3) { ++- if (failExitCount > 0) { ++- unsigned secsSinceLastFail; ++- ++- secsSinceLastFail = (now - lastFailExit); ++- ++- if (secsSinceLastFail > 120) { ++- /* reset fail count */ ++- failExitCount = 0; ++- } ++- else if (failExitCount > 20) { ++- printf("### child %i (#%i) already failed %i times " ++- "in the last %i seconds, stopping watchdog !\n", ++- child, forkCount, failExitCount, secsSinceLastFail); ++- repeat = NO; ++- } ++- } ++- } ++- failExitCount++; ++- lastFailExit = now; ++- ++- fprintf(stderr, ++- "### child %i (#%i) exited with status %i " ++- "(#fails=%i, uptime=%ds).\n", ++- child, forkCount, exitCode, failExitCount, uptime); ++- } ++- else { ++- fprintf(stderr, ++- "### child %i (#%i) exited successfully (uptime=%ds).\n", ++- child, forkCount, uptime); ++- } ++- ++- if (exitCode == 123) // ??? ++- repeat = NO; ++- } ++- else { ++- fprintf(stderr, ++- "### abnormal termination of child %i (#%i) status=%i" ++- "(was not signaled nor exited).", ++- child, forkCount, status); ++- } ++- } +++ [ud errorWithFormat: @"unable to open pid file: %@", pidFile]; +++ rc = -1; ++ } ++ } ++- return 0; ++ } +++ else { +++ stdout = fdopen(stdErrNo, "a"); +++ stderr = fdopen(stdErrNo, "a"); +++ fprintf(stderr, "failed to redirect output channels to log file '%s'\n", +++ [logFile cString]); +++ } +++ +++ [pool release]; +++ +++ return rc; ++ } ++ #endif ++ ++@@ -421,8 +1041,8 @@ ++ ++ @interface NSUserDefaults(ServerDefaults) ++ + (id)hackInServerDefaults:(NSUserDefaults *)_ud ++- withAppDomainPath:(NSString *)_appDomainPath ++- globalDomainPath:(NSString *)_globalDomainPath; +++ withAppDomainPath:(NSString *)_appDomainPath +++ globalDomainPath:(NSString *)_globalDomainPath; ++ @end ++ ++ int WOWatchDogApplicationMainWithServerDefaults ++@@ -437,7 +1057,7 @@ ++ { ++ extern char **environ; ++ [NSProcessInfo initializeWithArguments:(void*)argv count:argc ++- environment:(void*)environ]; +++ environment:(void*)environ]; ++ } ++ #endif ++ ++@@ -446,8 +1066,8 @@ ++ ++ ud = [NSUserDefaults standardUserDefaults]; ++ sd = [defClass hackInServerDefaults:ud ++- withAppDomainPath:appDomainPath ++- globalDomainPath:globalDomainPath]; +++ withAppDomainPath:appDomainPath +++ globalDomainPath:globalDomainPath]; ++ ++ #if 0 ++ if (((sd == nil) || (sd == ud)) && (appDomainPath != nil)) { ++Index: sope-appserver/NGObjWeb/GNUmakefile.postamble ++=================================================================== ++--- sope-appserver/NGObjWeb/GNUmakefile.postamble (revision 1664) +++++ sope-appserver/NGObjWeb/GNUmakefile.postamble (working copy) ++@@ -23,14 +23,20 @@ ++ ++ # install makefiles ++ ++-after-install :: ++- $(MKDIRS) $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/Additional/ ++- $(INSTALL_DATA) ngobjweb.make $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/Additional/ngobjweb.make +++after-install :: $(DESTDIR)/$(GNUSTEP_MAKEFILES)/Additional/ngobjweb.make ++ ++ ifneq ($(GNUSTEP_MAKE_VERSION),1.3.0) ++-after-install :: +++after-install :: $(DESTDIR)/$(GNUSTEP_MAKEFILES)/woapp.make $(DESTDIR)/$(GNUSTEP_MAKEFILES)/wobundle.make +++endif +++ +++$(DESTDIR)/$(GNUSTEP_MAKEFILES)/Additional/ngobjweb.make: ngobjweb.make +++ $(MKDIRS) $(DESTDIR)/$(GNUSTEP_MAKEFILES)/Additional/ +++ $(INSTALL_DATA) ngobjweb.make $(DESTDIR)/$(GNUSTEP_MAKEFILES)/Additional/ngobjweb.make +++ +++$(DESTDIR)/$(GNUSTEP_MAKEFILES)/woapp.make: woapp-gs.make ++ $(INSTALL_DATA) woapp-gs.make \ ++- $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/woapp.make +++ $(DESTDIR)/$(GNUSTEP_MAKEFILES)/woapp.make +++ +++$(DESTDIR)/$(GNUSTEP_MAKEFILES)/wobundle.make: wobundle-gs.make ++ $(INSTALL_DATA) wobundle-gs.make \ ++- $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/wobundle.make ++-endif +++ $(DESTDIR)/$(GNUSTEP_MAKEFILES)/wobundle.make ++Index: sope-appserver/NGObjWeb/WOMessage+XML.m ++=================================================================== ++--- sope-appserver/NGObjWeb/WOMessage+XML.m (revision 1664) +++++ sope-appserver/NGObjWeb/WOMessage+XML.m (working copy) ++@@ -84,7 +84,7 @@ ++ id builder; ++ ++ builder = [[[NSClassFromString(@"DOMSaxBuilder") alloc] init] autorelease]; ++- dom = [[builder buildFromData:data] retain]; +++ dom = [builder buildFromData:data]; ++ } ++ ++ /* cache DOM structure */ ++Index: sope-appserver/NGObjWeb/ChangeLog ++=================================================================== ++--- sope-appserver/NGObjWeb/ChangeLog (revision 1664) +++++ sope-appserver/NGObjWeb/ChangeLog (working copy) ++@@ -1,3 +1,97 @@ +++2009-12-22 Wolfgang Sourdeau +++ +++ * WOWatchDogApplicationMain.m (_listeningAddress): read "WOPort" +++ from the user defaults rather than by invoking [WOApplication +++ port], which returns an NSNumber. +++ +++2009-12-14 Wolfgang Sourdeau +++ +++ * WOWatchDogApplicationMain.m (-run:argc:argv:): added a +++ repeatable timer, triggered every 0.5 seconds, that ensures the +++ proper looping of the runloop when a signal was received. +++ +++2009-12-09 Wolfgang Sourdeau +++ +++ * WOWatchDogApplicationMain.m (_handleSIGCHLD:) +++ (_handleTermination:, _handleSIGHUP:): the actual handling is now +++ done elsewhere, in order to avoid messing with memory allocation +++ and risking a dead lock. +++ (-_handlePostTerminationSignal): we set "terminate" to YES if all +++ children are already down, in order to avoid another deadlock +++ where the process termination would stall waiting for SIGCHLD. +++ (-receivedEvent:type:extra:forMode:): check that the control +++ socket is still "alive" before reading from it. If not, we +++ unregister the filedescriptor passed as "data" from the runloop +++ listener. +++ +++2009-12-07 Wolfgang Sourdeau +++ +++ * WOCoreApplication.m (+initialize): we invoke +++ "registerUserDefaults" from here now. This enables Defaults.plist +++ to be registered as soon as the watchdog is active. +++ +++ * WOWatchDogApplicationMain.m (-terminate): we use a SIGTERM to +++ terminate the children instead of passing a message. We also setup +++ a timer that will send a SIGKILL after 5 minutes. +++ (-_releaseListeningSocket): we close the socket here so that other +++ processes can start listening. +++ (WOWatchDogApplicationMain): we accept "-" as argument to +++ "WOLogFile" so that we avoid redirecting the output and the error +++ channels. +++ +++2009-11-11 Wolfgang Sourdeau +++ +++ * WOCoreApplication.m (-setControlSocket, -controlSocket) +++ (-setListeningSocket, -listeningSocke): new helper accessors for +++ the new watchdog mechanism. +++ +++ * WOHttpAdaptor/WOHttpAdaptor.m: slightly refactored to use the +++ control socket provided by the watchdog. +++ +++ * WOWatchDogApplicationMain.m: rewritten the watchdog mechanism: +++ - added WOWatchDog and WOWatchDogChild classes +++ - make use of UnixSignalHandler +++ - added support for preforked preocesses (WOWorkersCount) +++ - detach watchdog processes from terminal by default (WONoDetach) +++ - redirect stderr and stdout to file +++ (WOLogFile = /var/log/[name]/[name].log) +++ - write pid file +++ (WOPidFile = /var/run/[name]/[name].pid) +++ - use "127.0.0.1:port" as default bind address, unless +++ WOHTTPAllowHost is specified +++ - added support for delaying process respawning +++ (WORespawnDelay = 5 seconds) +++ +++2009-10-26 Wolfgang Sourdeau +++ +++ * WOMessage+XML.m (-contentAsDOMDocument): do not retain "dom" as +++ it will be assigned to self->domCache, to avoid a leak. +++ +++2009-10-21 Wolfgang Sourdeau +++ +++ * WebDAV/SoObjectResultEntry.m (-valueForKey:): we now take +++ WOUseRelativeURLs into account when the "href" key is requested, +++ to work around a bug in iCal 4. +++ +++2009-07-02 Wolfgang Sourdeau +++ +++ * WOMessage.m (-setHeaders:, -setHeader:forKey:, headerForKey:, +++ -appendHeader:forKey:, -appendHeaders:forKey:, setHeaders:forKey:, +++ -headersForKey:): convert the specified header key to lowercase, +++ to ensure they are managed case-insensitively. +++ * WOHttpAdaptor/WOHttpTransaction.m +++ (-deliverResponse:toRequest:onStream:): if the content-type is +++ specified and already has "text/plain" as prefix, we don't +++ override it. +++ +++2009-07-01 Wolfgang Sourdeau +++ +++ * WOHttpAdaptor/WOHttpTransaction.m +++ (-deliverResponse:toRequest:onStream:): we test the content-length +++ and impose a content-type of text/plain when 0. This work-arounds +++ a bug in Mozilla clients where empty responses with a content-type +++ set to X/xml will trigger an exception. +++ ++ 2009-06-10 Helge Hess ++ ++ * DAVPropMap.plist: mapped {DAV:}current-user-principal (v4.9.37) ++Index: sope-appserver/NGObjWeb/DAVPropMap.plist ++=================================================================== ++--- sope-appserver/NGObjWeb/DAVPropMap.plist (revision 1664) +++++ sope-appserver/NGObjWeb/DAVPropMap.plist (working copy) ++@@ -157,6 +157,7 @@ ++ "{urn:ietf:params:xml:ns:caldav}supported-calendar-data" = ++ davSupportedCalendarDataTypes; ++ "{urn:ietf:params:xml:ns:caldav}calendar-description" = davDescription; +++ "{urn:ietf:params:xml:ns:caldav}calendar-timezone" = davCalendarTimeZone; ++ ++ /* CardDAV */ ++ "{urn:ietf:params:xml:ns:carddav}addressbook-home-set" = davAddressbookHomeSet; ++Index: sope-appserver/NGObjWeb/WebDAV/SoObjectResultEntry.m ++=================================================================== ++--- sope-appserver/NGObjWeb/WebDAV/SoObjectResultEntry.m (revision 1664) +++++ sope-appserver/NGObjWeb/WebDAV/SoObjectResultEntry.m (working copy) ++@@ -25,7 +25,14 @@ ++ @implementation SoObjectResultEntry ++ ++ static BOOL debugOn = NO; +++static BOOL useRelativeURLs = NO; ++ ++++ (void) initialize +++{ +++ useRelativeURLs = [[NSUserDefaults standardUserDefaults] +++ boolForKey: @"WOUseRelativeURLs"]; +++} +++ ++ - (id)initWithURI:(NSString *)_href object:(id)_o values:(NSDictionary *)_d { ++ if ((self = [super init])) { ++ if (debugOn) { ++@@ -85,10 +92,36 @@ ++ return YES; ++ } ++ +++- (NSString *)_relativeHREF { +++ NSString *newHREF; +++ NSRange hostRange; +++ +++ if ([self->href hasPrefix: @"/"]) +++ return self->href; +++ else { +++ hostRange = [self->href rangeOfString: @"://"]; +++ if (hostRange.length > 0) { +++ newHREF = [self->href substringFromIndex: NSMaxRange (hostRange)]; +++ hostRange = [newHREF rangeOfString: @"/"]; +++ if (hostRange.length > 0) { +++ newHREF = [newHREF substringFromIndex: hostRange.location]; +++ } +++ } else { +++ newHREF = self->href; +++ } +++ +++ return newHREF; +++ } +++} +++ ++ - (id)valueForKey:(NSString *)_key { ++- if ([_key isEqualToString:@"{DAV:}href"]) ++- return self->href; ++- +++ if ([_key isEqualToString:@"{DAV:}href"]) { +++ if (useRelativeURLs) +++ return [self _relativeHREF]; +++ else +++ return self->href; +++ } +++ ++ if ([_key isEqualToString:@"{DAV:}status"]) ++ return nil; ++ ++Index: sope-appserver/NGObjWeb/WebDAV/SoWebDAVRenderer.m ++=================================================================== ++--- sope-appserver/NGObjWeb/WebDAV/SoWebDAVRenderer.m (revision 1664) +++++ sope-appserver/NGObjWeb/WebDAV/SoWebDAVRenderer.m (working copy) ++@@ -49,6 +49,8 @@ ++ #define XMLNS_INTTASK \ ++ @"{http://schemas.microsoft.com/mapi/id/{00062003-0000-0000-C000-000000000046}/}" ++ +++static Class NSURLKlass = Nil; +++ ++ @interface SoWebDAVRenderer(Privates) ++ - (BOOL)renderStatusResult:(id)_object withDefaultStatus:(int)_defStatus ++ inContext:(WOContext *)_ctx; ++@@ -79,6 +81,8 @@ ++ ++ if ((debugOn = [ud boolForKey:@"SoRendererDebugEnabled"])) ++ NSLog(@"enabled debugging in SoWebDAVRenderer (SoRendererDebugEnabled)"); +++ +++ NSURLKlass = [NSURL class]; ++ } ++ ++ + (id)sharedRenderer { ++@@ -616,16 +620,19 @@ ++ [r appendContentString:s]; ++ } ++ else { +++ s = [self stringForValue:value ofProperty:_key prefixes:nsToPrefix]; ++ [r appendContentCharacter:'<']; ++ [r appendContentString:extName]; ++- [r appendContentCharacter:'>']; ++- ++- s = [self stringForValue:value ofProperty:_key prefixes:nsToPrefix]; ++- [r appendContentString:s]; ++- ++- [r appendContentString:@""]; +++ if ([s length] > 0) { +++ [r appendContentCharacter:'>']; +++ [r appendContentString:s]; +++ [r appendContentString:@""]; +++ } +++ else { +++ [r appendContentString:@"/>"]; +++ } ++ if (formatOutput) [r appendContentCharacter:'\n']; ++ } ++ } ++@@ -694,8 +701,13 @@ ++ } ++ ++ /* tidy href */ ++- href = [self tidyHref:href baseURL:baseURL]; ++- +++ if (useRelativeURLs) { +++ if ([href isKindOfClass: NSURLKlass]) +++ href = [href path]; +++ } +++ else +++ href = [self tidyHref:href baseURL:baseURL]; +++ ++ /* tidy status */ ++ stat = [self tidyStatus:stat]; ++ } ++Index: sope-appserver/NGObjWeb/WODirectAction.m ++=================================================================== ++--- sope-appserver/NGObjWeb/WODirectAction.m (revision 1664) +++++ sope-appserver/NGObjWeb/WODirectAction.m (working copy) ++@@ -46,7 +46,7 @@ ++ } ++ - (id)initWithContext:(WOContext *)_ctx { ++ if ((self = [self initWithRequest:[_ctx request]])) { ++- self->context = [_ctx retain]; +++ self->context = _ctx; ++ } ++ return self; ++ } ++@@ -54,16 +54,16 @@ ++ return [self initWithRequest:nil]; ++ } ++ ++-- (void)dealloc { ++- [self->context release]; ++- [super dealloc]; ++-} +++// - (void)dealloc { +++// [self->context release]; +++// [super dealloc]; +++// } ++ ++ /* accessors */ ++ ++ - (WOContext *)context { ++ if (self->context == nil) ++- self->context = [[[WOApplication application] context] retain]; +++ self->context = [[WOApplication application] context]; ++ return self->context; ++ } ++ ++Index: sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.m ++=================================================================== ++--- sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.m (revision 1664) +++++ sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.m (working copy) ++@@ -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 (revision 1664) +++++ sope-appserver/NGObjWeb/DynamicElements/_WOComplexHyperlink.m (working copy) ++@@ -41,6 +41,7 @@ ++ WOAssociation *string; ++ WOAssociation *target; ++ WOAssociation *disabled; +++ WOAssociation *isAbsolute; ++ WOElement *template; ++ ++ /* new in WO4: */ ++@@ -360,6 +361,7 @@ ++ { ++ if ((self = [super initWithName:_name hyperlinkInfo:_info template:_t])) { ++ self->href = _info->href; +++ self->isAbsolute = _info->isAbsolute; ++ } ++ return self; ++ } ++@@ -375,8 +377,11 @@ ++ // TODO: we need a binding to disable rewriting! ++ NSRange r; ++ +++ if ([[self->isAbsolute valueInContext:_ctx] boolValue] == YES) +++ return NO; +++ ++ r.length = [_s length]; ++- +++ ++ /* do not rewrite pure fragment URLs */ ++ if (r.length > 0 && [_s characterAtIndex:0] == '#') ++ return NO; ++Index: sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.h ++=================================================================== ++--- sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.h (revision 1664) +++++ sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.h (working copy) ++@@ -41,7 +41,8 @@ ++ WOAssociation *pageName; ++ WOAssociation *actionClass; ++ WOAssociation *directActionName; ++- +++ WOAssociation *isAbsolute; +++ ++ BOOL sidInUrl; ++ ++ /* 'ivar' associations */ ++Index: sope-appserver/NGObjWeb/WOMessage.m ++=================================================================== ++--- sope-appserver/NGObjWeb/WOMessage.m (revision 1664) +++++ sope-appserver/NGObjWeb/WOMessage.m (working copy) ++@@ -182,7 +182,7 @@ ++ NSString *key; ++ ++ keys = [_headers keyEnumerator]; ++- while ((key = [keys nextObject])) { +++ while ((key = [[keys nextObject] lowercaseString])) { ++ id value; ++ ++ value = [_headers objectForKey:key]; ++@@ -198,34 +198,39 @@ ++ } ++ ++ - (void)setHeader:(NSString *)_header forKey:(NSString *)_key { ++- [self->header setObject:[_header stringValue] forKey:_key]; +++ [self->header setObject:[_header stringValue] +++ forKey:[_key lowercaseString]]; ++ } ++ - (NSString *)headerForKey:(NSString *)_key { ++- return [[self->header objectEnumeratorForKey:_key] nextObject]; +++ return [[self->header objectEnumeratorForKey:[_key lowercaseString]] +++ nextObject]; ++ } ++ ++ - (void)appendHeader:(NSString *)_header forKey:(NSString *)_key { ++- [self->header addObject:_header forKey:_key]; +++ [self->header addObject:_header forKey:[_key lowercaseString]]; ++ } ++ - (void)appendHeaders:(NSArray *)_headers forKey:(NSString *)_key { ++- [self->header addObjects:_headers forKey:_key]; +++ [self->header addObjects:_headers forKey:[_key lowercaseString]]; ++ } ++ ++ - (void)setHeaders:(NSArray *)_headers forKey:(NSString *)_key { ++ NSEnumerator *e; ++ id value; +++ NSString *lowerKey; ++ +++ lowerKey = [_key lowercaseString]; ++ e = [_headers objectEnumerator]; ++ ++- [self->header removeAllObjectsForKey:_key]; +++ [self->header removeAllObjectsForKey:lowerKey]; ++ ++ while ((value = [e nextObject])) ++- [self->header addObject:value forKey:_key]; +++ [self->header addObject:value forKey:lowerKey]; ++ } ++ - (NSArray *)headersForKey:(NSString *)_key { ++ NSEnumerator *values; ++ ++- if ((values = [self->header objectEnumeratorForKey:_key])) { +++ if ((values +++ = [self->header objectEnumeratorForKey:[_key lowercaseString]])) { ++ NSMutableArray *array = nil; ++ id value = nil; ++ ++@@ -243,17 +248,14 @@ ++ NSEnumerator *values; ++ ++ if ((values = [self->header keyEnumerator])) { ++- NSMutableArray *array = nil; +++ NSMutableArray *array; ++ id name = nil; ++- array = [[NSMutableArray alloc] init]; ++- +++ array = [NSMutableArray array]; +++ ++ while ((name = [values nextObject])) ++ [array addObject:name]; ++ ++- name = [array copy]; ++- [array release]; ++- ++- return [name autorelease]; +++ return array; ++ } ++ return nil; ++ } ++Index: sope-appserver/NGObjWeb/SoObjects/SoObject.m ++=================================================================== ++--- sope-appserver/NGObjWeb/SoObjects/SoObject.m (revision 1664) +++++ sope-appserver/NGObjWeb/SoObjects/SoObject.m (working copy) ++@@ -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/NGObjWeb/WOAdaptor.h ++=================================================================== ++--- sope-appserver/NGObjWeb/NGObjWeb/WOAdaptor.h (revision 1664) +++++ sope-appserver/NGObjWeb/NGObjWeb/WOAdaptor.h (working copy) ++@@ -27,6 +27,13 @@ ++ @class NSString, NSDictionary; ++ @class WOCoreApplication; ++ +++typedef enum { +++ WOChildMessageAccept = 0, +++ WOChildMessageReady, +++ WOChildMessageShutdown, +++ WOChildMessageMax +++} WOChildMessage; +++ ++ @interface WOAdaptor : NSObject ++ { ++ @protected ++Index: sope-appserver/NGObjWeb/NGObjWeb/WOCoreApplication.h ++=================================================================== ++--- sope-appserver/NGObjWeb/NGObjWeb/WOCoreApplication.h (revision 1664) +++++ sope-appserver/NGObjWeb/NGObjWeb/WOCoreApplication.h (working copy) ++@@ -31,6 +31,8 @@ ++ @class WOAdaptor, WORequest, WOResponse, WORequestHandler; ++ @class NSBundle; ++ +++@class NGActiveSocket, NGPassiveSocket; +++ ++ NGObjWeb_EXPORT NSString *WOApplicationWillFinishLaunchingNotification; ++ NGObjWeb_EXPORT NSString *WOApplicationDidFinishLaunchingNotification; ++ NGObjWeb_EXPORT NSString *WOApplicationWillTerminateNotification; ++@@ -41,6 +43,9 @@ ++ NSRecursiveLock *lock; ++ NSLock *requestLock; ++ +++ NGActiveSocket *controlSocket; +++ NGPassiveSocket *listeningSocket; +++ ++ struct { ++ BOOL isTerminating:1; ++ } cappFlags; ++@@ -55,6 +60,14 @@ ++ - (void)activateApplication; ++ - (void)deactivateApplication; ++ +++/* Watchdog helpers */ +++ +++- (void)setControlSocket: (NGActiveSocket *) newSocket; +++- (NGActiveSocket *)controlSocket; +++ +++- (void)setListeningSocket: (NGPassiveSocket *) newSocket; +++- (NGPassiveSocket *)listeningSocket; +++ ++ /* adaptors */ ++ ++ - (NSArray *)adaptors; ++Index: sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpAdaptor.h ++=================================================================== ++--- sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpAdaptor.h (revision 1664) +++++ sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpAdaptor.h (working copy) ++@@ -46,7 +46,6 @@ ++ NSMutableArray *delayedResponses; ++ } ++ ++-+ (BOOL)optionLogStream; ++ + (BOOL)optionLogPerf; ++ ++ @end ++Index: sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpTransaction.m ++=================================================================== ++--- sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpTransaction.m (revision 1664) +++++ sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpTransaction.m (working copy) ++@@ -48,8 +48,8 @@ ++ NSString *WOAsyncResponseReadyNotificationName = ++ @"WOAsyncResponseReadyNotification"; ++ NSString *WOAsyncResponse = @"WOAsyncResponse"; +++static BOOL WOHttpAdaptor_LogStream = NO; ++ ++- ++ @interface WOCoreApplication(SimpleParserSelection) ++ ++ - (BOOL)shouldUseSimpleHTTPParserForTransaction:(id)_tx; ++@@ -85,13 +85,14 @@ ++ ud = [NSUserDefaults standardUserDefaults]; ++ useSimpleParser = [ud boolForKey:@"WOHttpTransactionUseSimpleParser"]; ++ doCore = [[ud objectForKey:@"WOCoreOnHTTPAdaptorException"] boolValue]?1:0; +++ WOHttpAdaptor_LogStream = [ud boolForKey:@"WOHttpAdaptor_LogStream"]; ++ ++ adLogPath = [[ud stringForKey:@"WOAdaptorLogPath"] copy]; ++ if (adLogPath == nil) adLogPath = @""; ++ } ++ ++ - (BOOL)optionLogStream { ++- return [WOHttpAdaptor optionLogStream]; +++ return WOHttpAdaptor_LogStream; ++ } ++ - (BOOL)optionLogPerf { ++ return perfLogger ? YES : NO; ++@@ -108,6 +109,9 @@ ++ NSAssert(_app, @"missing application ..."); ++ self->socket = [_socket retain]; ++ self->application = [_app retain]; +++ if ([[_app recordingPath] length] > 0) +++ WOHttpAdaptor_LogStream = YES; +++ ++ return self; ++ } ++ ++@@ -696,7 +700,7 @@ ++ *(&out) = nil; ++ ++ [self _httpValidateResponse:_response]; ++- +++ ++ out = [(NGCTextStream *)[NGCTextStream alloc] initWithSource:_out]; ++ ++ NS_DURING { ++@@ -705,6 +709,7 @@ ++ id body; ++ BOOL doZip; ++ BOOL isok = YES; +++ int length; ++ ++ doZip = [_response shouldZipResponseToRequest:_request]; ++ ++@@ -738,7 +743,11 @@ ++ ++ /* add content length header */ ++ ++- snprintf((char *)buf, sizeof(buf), "%d", [body length]); +++ if ((length = [body length]) == 0 +++ && ![[_response headerForKey: @"content-type"] hasPrefix:@"text/plain"]) { +++ [_response setHeader:@"text/plain" forKey:@"content-type"]; +++ } +++ snprintf((char *)buf, sizeof(buf), "%d", length); ++ t1 = [[NSString alloc] initWithCString:(char *)buf]; ++ [_response setHeader:t1 forKey:@"content-length"]; ++ [t1 release]; t1 = nil; ++@@ -766,7 +775,7 @@ ++ NSString *value; ++ ++ if (!hasConnectionHeader) { ++- if ([fieldName caseInsensitiveCompare:@"connection"]==NSOrderedSame) +++ if ([fieldName isEqualToString:@"connection"]) ++ hasConnectionHeader = YES; ++ } ++ ++Index: sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpAdaptor.m ++=================================================================== ++--- sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpAdaptor.m (revision 1664) +++++ sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpAdaptor.m (working copy) ++@@ -71,18 +71,13 @@ ++ ++ static NGLogger *logger = nil; ++ static NGLogger *perfLogger = nil; ++-static BOOL WOHttpAdaptor_LogStream = NO; ++ static BOOL WOContactSNS = NO; ++ static BOOL WOCoreOnHTTPAdaptorException = NO; ++ static int WOHttpAdaptorSendTimeout = 10; ++ static int WOHttpAdaptorReceiveTimeout = 10; ++-static int WOHttpAdaptorForkCount = 0; ++ static id allow = nil; ++ static BOOL debugOn = NO; ++ ++-+ (BOOL)optionLogStream { ++- return WOHttpAdaptor_LogStream; ++-} ++ + (BOOL)optionLogPerf { ++ return perfLogger != nil ? YES : NO; ++ } ++@@ -108,8 +103,6 @@ ++ logger = [lm loggerForClass:self]; ++ perfLogger = [lm loggerForDefaultKey:@"WOProfileHttpAdaptor"]; ++ ++- WOHttpAdaptor_LogStream = [ud boolForKey:@"WOHttpAdaptor_LogStream"]; ++- ++ // TODO: this should be queried on demand to allow different defaults ++ WOContactSNS = [[ud objectForKey:@"WOContactSNS"] boolValue]; ++ ++@@ -134,9 +127,6 @@ ++ allow = [allow copy]; ++ } ++ ++- WOHttpAdaptorForkCount = ++- [[ud objectForKey:@"WOHttpAdaptorForkCount"] intValue]; ++- ++ if (WOCoreOnHTTPAdaptorException) ++ [logger warnWithFormat:@"will dump core on HTTP adaptor exception!"]; ++ } ++@@ -219,33 +209,31 @@ ++ application:_application])) { ++ id arg = nil; ++ ++- if ([[_application recordingPath] length] > 0) ++- WOHttpAdaptor_LogStream = YES; ++- ++ [self _registerForSignals]; +++ if (![_application controlSocket]) { +++ if ([_args count] < 1) +++ self->address = [self addressFromDefaultsOfApplication:_application]; +++ else +++ self->address = [self addressFromArguments:_args]; ++ ++- if ([_args count] < 1) ++- self->address = [self addressFromDefaultsOfApplication:_application]; ++- else ++- self->address = [self addressFromArguments:_args]; +++ self->address = [self->address retain]; ++ ++- self->address = [self->address retain]; +++ if (self->address == nil) { +++ [_application errorWithFormat: +++ @"got no address for HTTP server (using arg '%@')", arg]; +++ [self release]; +++ return nil; +++ } ++ ++- if (self->address == nil) { ++- [_application errorWithFormat: ++- @"got no address for HTTP server (using arg '%@')", arg]; ++- [self release]; ++- return nil; ++- } ++- ++- if (!WOContactSNS) { ++- [_application logWithFormat:@"%@ listening on address %@", +++ if (!WOContactSNS) { +++ [_application logWithFormat:@"%@ listening on address %@", ++ NSStringFromClass([self class]), ++ [(id)self->address stringValue]]; +++ } ++ } ++ ++ self->lock = [[NSRecursiveLock alloc] init]; ++- +++ ++ self->maxThreadCount = [[WOCoreApplication workerThreadCount] intValue]; ++ ++ [self setSendTimeout:WOHttpAdaptorSendTimeout]; ++@@ -270,145 +258,76 @@ ++ return self->address; ++ } ++ ++-/* forking */ ++- ++-static pid_t *childPIDs = NULL; ++-static BOOL isForkMaster = YES; ++- ++-- (void)forkChildren { ++- unsigned i; ++- ++- if (WOHttpAdaptorForkCount == 0) ++- return; ++- ++- [self logWithFormat:@"Note: forking %d children for socket processing.", ++- WOHttpAdaptorForkCount]; ++- ++-#if !defined(__MINGW32__) ++- [[UnixSignalHandler sharedHandler] ++- addObserver:self selector:@selector(handleSIGCHLD:) ++- forSignal:SIGCHLD immediatelyNotifyOnSignal:NO]; ++-#endif ++- ++- childPIDs = calloc(WOHttpAdaptorForkCount + 1, sizeof(pid_t)); ++- for (i = 0; i < WOHttpAdaptorForkCount; i++) { ++- childPIDs[i] = fork(); ++- ++- if (childPIDs[i] == 0) { ++- /* child */ ++- isForkMaster = NO; ++- return; ++- } ++- else if (childPIDs[i] > 0) ++- printf("Note: successfully forked child: %i\n", childPIDs[i]); ++- else ++- [self errorWithFormat:@"failed to fork child %i.", i]; ++- } ++-} ++-- (void)killChildren { ++- int i; ++- ++- if (!isForkMaster) ++- return; ++- ++- for (i = 0; i < WOHttpAdaptorForkCount; i++) { ++- if (childPIDs[i] != 0) ++- kill(childPIDs[i], SIGKILL); ++- } ++-} ++- ++-- (void)checkStatusOfChildren { ++- /* ++- Note: currently this does not refork crashed processes. Reforking is harder ++- than it may sound because the crash can happen at arbitary execution ++- states. ++- That is, the "master process" is not virgin anymore, eg it might have ++- open database connections. ++- ++- So the solution might be to refork the whole cluster once a minimum ++- backend threshold is reached. ++- */ ++- unsigned int i; ++- ++- if (!isForkMaster) ++- return; ++- ++- for (i = 0; i < WOHttpAdaptorForkCount; i++) { ++- pid_t result; ++- int status; ++- ++- if (childPIDs[i] == 0) ++- continue; ++- ++- result = waitpid(childPIDs[i], &status, WNOHANG); ++- if (result == 0) /* did not exit yet */ ++- continue; ++- ++- if (result == -1) { /* error */ ++- [self errorWithFormat:@"failed to get status of child %i: %s", ++- childPIDs[i], strerror(errno)]; ++- continue; ++- } ++- ++- [self logWithFormat:@"Note: child %i terminated.", childPIDs[i]]; ++- childPIDs[i] = 0; ++- } ++-} ++- ++ /* events */ ++ ++ - (void)handleSIGPIPE:(int)_signal { ++ [self warnWithFormat:@"caught SIGPIPE !"]; ++ } ++-- (void)handleSIGCHLD:(int)_signal { ++- [self checkStatusOfChildren]; ++-} ++ ++ - (void)registerForEvents { ++ int backlog; +++ NGActiveSocket *controlSocket; +++ WOChildMessage message; +++ +++ controlSocket = [[WOCoreApplication application] controlSocket]; +++ if (controlSocket) { +++ ASSIGN(self->socket, [[WOCoreApplication application] listeningSocket]); +++ [[NSNotificationCenter defaultCenter] +++ addObserver:self +++ selector:@selector(acceptControlMessage:) +++ name:NSFileObjectBecameActiveNotificationName +++ object:nil]; +++ [(WORunLoop *)[WORunLoop currentRunLoop] +++ addFileObject:controlSocket +++ activities:NSPosixReadableActivity +++ forMode:NSDefaultRunLoopMode]; +++ message = WOChildMessageReady; +++ [controlSocket safeWriteBytes: &message +++ count: sizeof (WOChildMessage)]; +++ // [self logWithFormat: @"notified the watchdog that we are ready"]; +++ } +++ else { +++ backlog = [[WOCoreApplication listenQueueSize] intValue]; ++ ++- backlog = [[WOCoreApplication listenQueueSize] intValue]; +++ if (backlog == 0) +++ backlog = 5; ++ ++- if (backlog == 0) ++- backlog = 5; +++ [self->socket release]; self->socket = nil; ++ ++- [self->socket release]; self->socket = nil; +++ self->socket = +++ [[NGPassiveSocket alloc] initWithDomain:[self->address domain]]; ++ ++- self->socket = ++- [[NGPassiveSocket alloc] initWithDomain:[self->address domain]]; +++ [self->socket bindToAddress:self->address]; ++ ++- [self->socket bindToAddress:self->address]; ++- ++- if ([[self->address domain] isEqual:[NGInternetSocketDomain domain]]) { ++- if ([(NGInternetSocketAddress *)self->address port] == 0) { ++- /* let the kernel choose an IP address */ +++ if ([[self->address domain] isEqual:[NGInternetSocketDomain domain]]) { +++ if ([(NGInternetSocketAddress *)self->address port] == 0) { +++ /* let the kernel choose an IP address */ ++ ++- [self debugWithFormat:@"bound to wildcard: %@", self->address]; ++- [self debugWithFormat:@"got local: %@", [self->socket localAddress]]; +++ [self debugWithFormat:@"bound to wildcard: %@", self->address]; +++ [self debugWithFormat:@"got local: %@", [self->socket localAddress]]; ++ ++- self->address = [[self->socket localAddress] retain]; +++ self->address = [[self->socket localAddress] retain]; ++ ++- [self logWithFormat:@"bound to kernel assigned address %@: %@", +++ [self logWithFormat:@"bound to kernel assigned address %@: %@", ++ self->address, self->socket]; +++ } ++ } ++- } ++ ++- [self->socket listenWithBacklog:backlog]; +++ [self->socket listenWithBacklog:backlog]; ++ ++- [[NSNotificationCenter defaultCenter] +++ [[NSNotificationCenter defaultCenter] ++ addObserver:self selector:@selector(acceptConnection:) ++- name:NSFileObjectBecameActiveNotificationName ++- object:self->socket]; ++- [(WORunLoop *)[WORunLoop currentRunLoop] +++ name:NSFileObjectBecameActiveNotificationName +++ object:self->socket]; +++ +++ [(WORunLoop *)[WORunLoop currentRunLoop] ++ addFileObject:self->socket ++ activities:NSPosixReadableActivity ++ forMode:NSDefaultRunLoopMode]; ++- ++- [self forkChildren]; +++ } ++ } +++ ++ - (void)unregisterForEvents { ++- [self killChildren]; ++- ++ [(WORunLoop *)[WORunLoop currentRunLoop] ++ removeFileObject:self->socket forMode:NSDefaultRunLoopMode]; ++ [[NSNotificationCenter defaultCenter] removeObserver:self]; ++@@ -603,52 +522,91 @@ ++ return _connection; ++ } ++ +++- (NGActiveSocket *)_accept { +++ NGActiveSocket *connection; +++ +++ NS_DURING { +++ connection = [self->socket accept]; +++ if (!connection) +++ [self _serverCatched:[self->socket lastException]]; +++ else +++ [self debugWithFormat:@"accepted connection: %@", connection]; +++ } +++ NS_HANDLER { +++ connection = nil; +++ [self _serverCatched:localException]; +++ } +++ NS_ENDHANDLER; +++ +++ return connection; +++} +++ +++- (void)_handleConnection:(NGActiveSocket *)connection { +++ if (connection != nil) { +++ if (self->maxThreadCount <= 1) { +++ NS_DURING +++ [self _handleAcceptedConnection:[connection retain]]; +++ NS_HANDLER +++ [self _serverCatched:localException]; +++ NS_ENDHANDLER; +++ } +++ else { +++ [NSThread detachNewThreadSelector: +++ @selector(_handleAcceptedConnectionInThread:) +++ toTarget:self +++ withObject:[connection retain]]; +++ [self logWithFormat:@"detached new thread for request."]; +++ //[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]]; +++ } +++ connection = nil; +++ } +++} +++ +++- (void) acceptControlMessage: (NSNotification *) aNotification +++{ +++ NGActiveSocket *controlSocket, *connection; +++ WOChildMessage message; +++ NSAutoreleasePool *pool; +++ +++ // NSLog (@"received control message"); +++ controlSocket = [aNotification object]; +++ // [self logWithFormat:@"child accepting message from socket: %@", controlSocket]; +++ while (![controlSocket safeReadBytes: &message +++ count: sizeof (WOChildMessage)]) +++ NSLog (@"renotifying watchdog"); +++ if (message == WOChildMessageAccept) { +++ pool = [NSAutoreleasePool new]; +++ connection = [self _accept]; +++ if ([controlSocket safeWriteBytes: &message +++ count: sizeof (WOChildMessage)]) +++ ; +++ [self _handleConnection: connection]; +++ message = WOChildMessageReady; +++ [controlSocket safeWriteBytes: &message count: sizeof (WOChildMessage)]; +++ [pool release]; +++ } +++ else if (message == WOChildMessageShutdown) { +++ [controlSocket safeWriteBytes: &message +++ count: sizeof (WOChildMessage)]; +++ [[WOCoreApplication application] terminate]; +++ } +++} +++ ++ - (void)acceptConnection:(id)_notification { +++ NGActiveSocket *connection; ++ #if USE_POOLS ++ NSAutoreleasePool *pool; ++- *(&pool) = [[NSAutoreleasePool alloc] init]; +++ +++ pool = [[NSAutoreleasePool alloc] init]; ++ #endif ++ { ++- NGActiveSocket *connection; ++- ++- NS_DURING { ++- *(&connection) = (NGActiveSocket *)[self->socket accept]; ++- if (connection == nil) ++- [self _serverCatched:[self->socket lastException]]; ++- else ++- [self debugWithFormat:@"accepted connection: %@", connection]; ++- } ++- NS_HANDLER { ++- connection = nil; ++- [self _serverCatched:localException]; ++- } ++- NS_ENDHANDLER; ++- ++- connection = (NGActiveSocket *)[self _checkAccessOnConnection:connection]; ++- ++- if (connection != nil) { ++- if (self->maxThreadCount <= 1) { ++- NS_DURING ++- [self _handleAcceptedConnection:[connection retain]]; ++- NS_HANDLER ++- [self _serverCatched:localException]; ++- NS_ENDHANDLER; ++- } ++- else { ++- [NSThread detachNewThreadSelector: ++- @selector(_handleAcceptedConnectionInThread:) ++- toTarget:self ++- withObject:[connection retain]]; ++- [self logWithFormat:@"detached new thread for request."]; ++- //[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]]; ++- } ++- connection = nil; ++- } +++ connection = [self _checkAccessOnConnection:[self _accept]]; +++ [self _handleConnection: connection]; ++ } ++ #if USE_POOLS ++ [pool release]; pool = nil; ++ #endif ++- +++ ++ if (self->isTerminated) { ++ if (self->socket) { ++ [[NSNotificationCenter defaultCenter] ++Index: sope-appserver/NGObjWeb/WOCoreApplication.m ++=================================================================== ++--- sope-appserver/NGObjWeb/WOCoreApplication.m (revision 1664) +++++ sope-appserver/NGObjWeb/WOCoreApplication.m (working copy) ++@@ -75,6 +75,43 @@ ++ NGObjWeb_DECLARE id WOApp = nil; ++ static NSMutableArray *activeApps = nil; // THREAD ++ ++++ (void)registerUserDefaults { +++ NSDictionary *owDefaults = nil; +++ NSString *apath; +++ +++ apath = [[self class] findNGObjWebResource:@"Defaults" ofType:@"plist"]; +++ if (apath == nil) +++ [self errorWithFormat:@"Cannot find Defaults.plist resource of " +++ @"NGObjWeb library!"]; +++#if HEAVY_DEBUG +++ else +++ [self debugWithFormat:@"Note: loading default defaults: %@", apath]; +++#endif +++ +++ owDefaults = [NSDictionary dictionaryWithContentsOfFile:apath]; +++ if (owDefaults) { +++ [[NSUserDefaults standardUserDefaults] registerDefaults:owDefaults]; +++#if HEAVY_DEBUG +++ [self debugWithFormat:@"did register NGObjWeb defaults: %@\n%@", +++ apath, owDefaults]; +++#endif +++ } +++ else { +++ [self errorWithFormat:@"could not load NGObjWeb defaults: '%@'", +++ apath]; +++ } +++} +++ ++++ (void)initialize +++{ +++ static BOOL initialized = NO; +++ +++ if (!initialized) { +++ [self registerUserDefaults]; +++ initialized = YES; +++ } +++} +++ ++ + (id)application { ++ if (WOApp == nil) { ++ [self warnWithFormat:@"%s: some code called +application without an " ++@@ -115,33 +152,6 @@ ++ } ++ } ++ ++-- (void)registerUserDefaults { ++- NSDictionary *owDefaults = nil; ++- NSString *apath; ++- ++- apath = [[self class] findNGObjWebResource:@"Defaults" ofType:@"plist"]; ++- if (apath == nil) ++- [self errorWithFormat:@"Cannot find Defaults.plist resource of " ++- @"NGObjWeb library!"]; ++-#if HEAVY_DEBUG ++- else ++- [self debugWithFormat:@"Note: loading default defaults: %@", apath]; ++-#endif ++- ++- owDefaults = [NSDictionary dictionaryWithContentsOfFile:apath]; ++- if (owDefaults) { ++- [[NSUserDefaults standardUserDefaults] registerDefaults:owDefaults]; ++-#if HEAVY_DEBUG ++- [self debugWithFormat:@"did register NGObjWeb defaults: %@\n%@", ++- apath, owDefaults]; ++-#endif ++- } ++- else { ++- [self errorWithFormat:@"could not load NGObjWeb defaults: '%@'", ++- apath]; ++- } ++-} ++- ++ - (id)init { ++ #if COCOA_Foundation_LIBRARY ++ /* ++@@ -157,7 +167,6 @@ ++ NSUserDefaults *ud; ++ NGLoggerManager *lm; ++ ++- [self registerUserDefaults]; ++ ud = [NSUserDefaults standardUserDefaults]; ++ lm = [NGLoggerManager defaultLoggerManager]; ++ logger = [lm loggerForClass:[self class]]; ++@@ -190,6 +199,9 @@ ++ forSignal:SIGHUP immediatelyNotifyOnSignal:NO]; ++ } ++ #endif +++ +++ controlSocket = nil; +++ listeningSocket = nil; ++ } ++ return self; ++ } ++@@ -202,9 +214,32 @@ ++ [self->adaptors release]; ++ [self->requestLock release]; ++ [self->lock release]; +++ [self->listeningSocket release]; +++ [self->controlSocket release]; ++ [super dealloc]; ++ } ++ +++/* Watchdog helpers */ +++- (void)setControlSocket: (NGActiveSocket *) newSocket +++{ +++ ASSIGN(self->controlSocket, newSocket); +++} +++ +++- (NGActiveSocket *)controlSocket +++{ +++ return self->controlSocket; +++} +++ +++- (void)setListeningSocket: (NGPassiveSocket *) newSocket +++{ +++ ASSIGN(self->listeningSocket, newSocket); +++} +++ +++- (NGPassiveSocket *)listeningSocket +++{ +++ return self->listeningSocket; +++} +++ ++ /* NGLogging */ ++ ++ + (id)logger { ++@@ -225,6 +260,7 @@ ++ /* STDIO is forbidden in signal handlers !!! no malloc !!! */ ++ #if 1 ++ self->cappFlags.isTerminating = 1; +++ [self->listeningSocket close]; ++ #else ++ static int termCount = 0; ++ unsigned pid; ++@@ -786,7 +822,9 @@ ++ id woport; ++ id addr; ++ ++- woport = [[self userDefaults] objectForKey:@"WOPort"]; +++ woport = [[self userDefaults] objectForKey:@"p"]; +++ if (!woport) +++ woport = [[self userDefaults] objectForKey:@"WOPort"]; ++ if ([woport isKindOfClass:[NSNumber class]]) ++ return woport; ++ woport = [woport stringValue]; +--- sope-4.9.r1664.orig/debian/patches/sope-gsmake2.diff ++++ sope-4.9.r1664/debian/patches/sope-gsmake2.diff @@ -0,0 +1,3144 @@ +Index: configure +=================================================================== @@ -3741,4900 +11477,8 @@ + endif + -include GNUmakefile.postamble +--include fhs.make ---- sope-4.9.r1660.orig/debian/patches/sope-patchset-r1660.diff -+++ sope-4.9.r1660/debian/patches/sope-patchset-r1660.diff -@@ -0,0 +1,4889 @@ -+Index: sope-ldap/NGLdap/NGLdapEntry.m -+=================================================================== -+--- sope-ldap/NGLdap/NGLdapEntry.m (revision 1660) -++++ sope-ldap/NGLdap/NGLdapEntry.m (working copy) -+@@ -105,14 +105,16 @@ -+ - (NGLdapAttribute *)attributeWithName:(NSString *)_name { -+ NSEnumerator *e; -+ NGLdapAttribute *a; -+- -++ NSString *upperName; -++ -+ if (_name == nil) -+ return nil; -+ -++ upperName = [_name uppercaseString]; -+ e = [self->attributes objectEnumerator]; -+ -+ while ((a = [e nextObject])) { -+- if ([[a attributeName] isEqualToString:_name]) -++ if ([[[a attributeName] uppercaseString] isEqualToString:upperName]) -+ return a; -+ } -+ return nil; -+Index: sope-ldap/NGLdap/ChangeLog -+=================================================================== -+--- sope-ldap/NGLdap/ChangeLog (revision 1660) -++++ sope-ldap/NGLdap/ChangeLog (working copy) -+@@ -1,3 +1,8 @@ -++2009-08-13 Wolfgang Sourdeau -++ -++ * NGLdapEntry.m (-attributeWithName:): attribute names are now -++ accessed in a case-insensitive way. -++ -+ 2009-04-02 Wolfgang Sourdeau -+ -+ * NGLdapConnection.m (useSSL,startTLS): new method enabling -+Index: sope-gdl1/PostgreSQL/PostgreSQL72Channel.m -+=================================================================== -+--- sope-gdl1/PostgreSQL/PostgreSQL72Channel.m (revision 1660) -++++ sope-gdl1/PostgreSQL/PostgreSQL72Channel.m (working copy) -+@@ -713,6 +713,39 @@ -+ return ms; -+ } -+ -++/* GCSEOAdaptorChannel protocol */ -++static NSString *sqlFolderFormat = (@"CREATE TABLE %@ (\n" \ -++ @" c_name VARCHAR (256) NOT NULL PRIMARY KEY,\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-gdl1/MySQL/MySQL4Channel.m -+=================================================================== -+--- sope-gdl1/MySQL/MySQL4Channel.m (revision 1660) -++++ sope-gdl1/MySQL/MySQL4Channel.m (working copy) -+@@ -755,6 +755,39 @@ -+ return pkey; -+ } -+ -++/* GCSEOAdaptorChannel protocol */ -++static NSString *sqlFolderFormat = (@"CREATE TABLE %@ (\n" \ -++ @" c_name VARCHAR (256) NOT NULL PRIMARY KEY,\n" -++ @" c_content VARCHAR (100000) NOT NULL,\n" -++ @" c_creationdate INT NOT NULL,\n" -++ @" c_lastmodified INT NOT NULL,\n" -++ @" c_version INT NOT NULL,\n" -++ @" c_deleted INT 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 /* MySQL4Channel */ -+ -+ void __link_MySQL4Channel() { -+Index: sope-gdl1/Oracle8/OracleAdaptorChannel.m -+=================================================================== -+--- sope-gdl1/Oracle8/OracleAdaptorChannel.m (revision 1660) -++++ sope-gdl1/Oracle8/OracleAdaptorChannel.m (working copy) -+@@ -1,7 +1,7 @@ -+ /* -+ ** OracleAdaptorChannel.m -+ ** -+-** Copyright (c) 2007 Inverse groupe conseil inc. and Ludovic Marcotte -++** Copyright (c) 2007-2009 Inverse inc. and Ludovic Marcotte -+ ** -+ ** Author: Ludovic Marcotte -+ ** -+@@ -30,6 +30,11 @@ -+ -+ #import -+ -++#include -++ -++static BOOL debugOn = NO; -++static int maxTry = 3; -++static int maxSleep = 500; -+ // -+ // -+ // -+@@ -41,10 +46,11 @@ -+ -+ @implementation OracleAdaptorChannel (Private) -+ -+-- (void) _cleanup -++- (void) _cleanup -+ { -+ column_info *info; -+ int c; -++ sword result; -+ -+ [_resultSetProperties removeAllObjects]; -+ -+@@ -58,11 +64,29 @@ -+ // so we just free the value instead. -+ if (info->value) -+ { -+- if (OCIDescriptorFree((dvoid *)info->value, (ub4)OCI_DTYPE_LOB) != OCI_SUCCESS) -++ if (info->type == SQLT_CLOB -++ || info->type == SQLT_BLOB -++ || info->type == SQLT_BFILEE -++ || info->type == SQLT_CFILEE) -++ { -++ result = OCIDescriptorFree((dvoid *)info->value, (ub4) OCI_DTYPE_LOB); -++ if (result != OCI_SUCCESS) -++ { -++ NSLog (@"value was not a LOB descriptor"); -++ abort(); -++ } -++ } -++ else -+ free(info->value); -+ info->value = NULL; -+ } -+- free(info); -++ else -++ { -++ NSLog (@"trying to free an already freed value!"); -++ abort(); -++ } -++ free(info); -++ -+ [_row_buffer removeObjectAtIndex: c]; -+ } -+ -+@@ -78,8 +102,7 @@ -+ // -+ @implementation OracleAdaptorChannel -+ -+-static void -+-DBTerminate() -++static void DBTerminate() -+ { -+ if (OCITerminate(OCI_DEFAULT)) -+ NSLog(@"FAILED: OCITerminate()"); -+@@ -89,6 +112,11 @@ -+ -+ + (void) initialize -+ { -++ NSUserDefaults *ud; -++ -++ ud = [NSUserDefaults standardUserDefaults]; -++ debugOn = [ud boolForKey: @"OracleAdaptorDebug"]; -++ -+ // We Initialize the OCI process environment. -+ if (OCIInitialize((ub4)OCI_DEFAULT, (dvoid *)0, -+ (dvoid * (*)(dvoid *, size_t)) 0, -+@@ -156,14 +184,17 @@ -+ [super closeChannel]; -+ -+ // We logoff from the database. -+- if (OCILogoff(_oci_ctx, _oci_err)) -++ if (!_oci_ctx || !_oci_err || OCILogoff(_oci_ctx, _oci_err)) -+ { -+ NSLog(@"FAILED: OCILogoff()"); -+ } -+ -++ if (_oci_ctx) -++ OCIHandleFree(_oci_ctx, OCI_HTYPE_SVCCTX); -+ -+- OCIHandleFree(_oci_ctx, OCI_HTYPE_SVCCTX); -+- OCIHandleFree(_oci_err, OCI_HTYPE_ERROR); -++ if (_oci_err) -++ OCIHandleFree(_oci_err, OCI_HTYPE_ERROR); -++ -+ // OCIHandleFree(_oci_env, OCI_HTYPE_ENV); -+ -+ _oci_ctx = (OCISvcCtx *)0; -+@@ -177,7 +208,8 @@ -+ // -+ - (void) dealloc -+ { -+- //NSLog(@"OracleAdaptorChannel: -dealloc"); -++ if (debugOn) -++ NSLog(@"OracleAdaptorChannel: -dealloc"); -+ -+ [self _cleanup]; -+ -+@@ -222,7 +254,7 @@ -+ { -+ EOAttribute *attribute; -+ OCIParam *param; -+- -++ int rCount; -+ column_info *info; -+ ub4 i, clen, count; -+ text *sql, *cname; -+@@ -231,6 +263,9 @@ -+ -+ [self _cleanup]; -+ -++ if (debugOn) -++ [self logWithFormat: @"expression: %@", theExpression]; -++ -+ if (!theExpression || ![theExpression length]) -+ { -+ [NSException raise: @"OracleInvalidExpressionException" -+@@ -244,7 +279,9 @@ -+ } -+ -+ sql = (text *)[theExpression UTF8String]; -+- -++ -++ rCount = 0; -++ retry: -+ // We alloc our statement handle -+ if ((status = OCIHandleAlloc((dvoid *)_oci_env, (dvoid **)&_current_stm, (ub4)OCI_HTYPE_STMT, (CONST size_t) 0, (dvoid **) 0))) -+ { -+@@ -264,13 +301,39 @@ -+ // We check if we're doing a SELECT and if so, we're fetching data! -+ OCIAttrGet(_current_stm, OCI_HTYPE_STMT, &type, 0, OCI_ATTR_STMT_TYPE, _oci_err); -+ self->isFetchInProgress = (type == OCI_STMT_SELECT ? YES : NO); -+- -++ -+ // We execute our statement. Not that we _MUST_ set iter to 0 for non-SELECT statements. -+ if ((status = OCIStmtExecute(_oci_ctx, _current_stm, _oci_err, (self->isFetchInProgress ? (ub4)0 : (ub4)1), (ub4)0, (CONST OCISnapshot *)NULL, (OCISnapshot *)NULL, -+ ([(OracleAdaptorContext *)[self adaptorContext] autoCommit] ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT)))) -+ { -++ ub4 serverStatus; -++ -+ checkerr(_oci_err, status); -+ NSLog(@"Statement execute failed (OCI_ERROR): %@", theExpression); -++ -++ // We check to see if we lost connection and need to reconnect. -++ serverStatus = 0; -++ OCIAttrGet((dvoid *)_oci_env, OCI_HTYPE_SERVER, (dvoid *)&serverStatus, (ub4 *)0, OCI_ATTR_SERVER_STATUS, _oci_err); -++ -++ if (serverStatus == OCI_SERVER_NOT_CONNECTED) -++ { -++ // We cleanup our previous handles -++ [self cancelFetch]; -++ [self closeChannel]; -++ -++ // We try to reconnect a couple of times before giving up... -++ while (rCount < maxTry) -++ { -++ usleep(maxSleep); -++ rCount++; -++ -++ if ([self openChannel]) -++ { -++ NSLog(@"Connection re-established to Oracle - retrying to process the statement."); -++ goto retry; -++ } -++ } -++ } -+ return NO; -+ } -+ -+@@ -302,7 +365,9 @@ -+ // We read the maximum width of a column -+ info->max_width = 0; -+ status = OCIAttrGet((dvoid*)param, (ub4)OCI_DTYPE_PARAM, (dvoid*)&(info->max_width), (ub4 *)0, (ub4)OCI_ATTR_DATA_SIZE, (OCIError *)_oci_err); -+- -++ -++ if (debugOn) -++ NSLog(@"name: %s, type: %d", cname, info->type); -+ attribute = [EOAttribute attributeWithOracleType: info->type name: cname length: clen width: info->max_width]; -+ [_resultSetProperties addObject: attribute]; -+ -+@@ -394,16 +459,17 @@ -+ return NO; -+ } -+ -+- -+ if (OCIEnvInit((OCIEnv **)&_oci_env, (ub4)OCI_DEFAULT, (size_t)0, (dvoid **)0)) -+ { -+ NSLog(@"FAILED: OCIEnvInit()"); -++ [self closeChannel]; -+ return NO; -+ } -+ -+ if (OCIHandleAlloc((dvoid *)_oci_env, (dvoid *)&_oci_err, (ub4)OCI_HTYPE_ERROR, (size_t)0, (dvoid **)0)) -+ { -+ NSLog(@"FAILED: OCIHandleAlloc() on errhp"); -++ [self closeChannel]; -+ return NO; -+ } -+ -+@@ -414,7 +480,10 @@ -+ // Under Oracle 10g, the third parameter of OCILogon() has the form: [//]host[:port][/service_name] -+ // See http://download-west.oracle.com/docs/cd/B12037_01/network.101/b10775/naming.htm#i498306 for -+ // all juicy details. -+- database = [[NSString stringWithFormat:@"%@:%@", [o serverName], [o port]] UTF8String]; -++ if ([o serverName] && [o port]) -++ database = [[NSString stringWithFormat:@"%@:%@/%@", [o serverName], [o port], [o databaseName]] UTF8String]; -++ else -++ database = [[o databaseName] UTF8String]; -+ -+ // We logon to the database. -+ if (OCILogon(_oci_env, _oci_err, &_oci_ctx, (const OraText*)username, strlen(username), -+@@ -422,6 +491,7 @@ -+ { -+ NSLog(@"FAILED: OCILogon(). username = %s password = %s" -+ @" database = %s", username, password, database); -++ [self closeChannel]; -+ return NO; -+ } -+ -+@@ -438,6 +508,11 @@ -+ { -+ sword status; -+ -++ // We check if our connection is open prior to trying to fetch any data. OCIStmtFetch2() returns -++ // NO error code if the OCI environment is set up but the OCILogon() has failed. -++ if (![self isOpen]) -++ return nil; -++ -+ status = OCIStmtFetch2(_current_stm, _oci_err, (ub4)1, (ub4)OCI_FETCH_NEXT, (sb4)0, (ub4)OCI_DEFAULT); -+ -+ if (status == OCI_NO_DATA) -+@@ -609,7 +684,7 @@ -+ -+ /* GCSEOAdaptorChannel protocol */ -+ static NSString *sqlFolderFormat = (@"CREATE TABLE %@ (\n" \ -+- @" c_name VARCHAR2 (256) NOT NULL,\n" -++ @" c_name VARCHAR2 (256) NOT NULL PRIMARY KEY,\n" -+ @" c_content CLOB NOT NULL,\n" -+ @" c_creationdate INTEGER NOT NULL,\n" -+ @" c_lastmodified INTEGER NOT NULL,\n" -+Index: sope-gdl1/Oracle8/OracleAdaptorChannelController.m -+=================================================================== -+--- sope-gdl1/Oracle8/OracleAdaptorChannelController.m (revision 1660) -++++ sope-gdl1/Oracle8/OracleAdaptorChannelController.m (working copy) -+@@ -31,6 +31,8 @@ -+ #import -+ #import -+ -++static BOOL debugOn = NO; -++ -+ // -+ // -+ // -+@@ -48,6 +50,14 @@ -+ // -+ @implementation OracleAdaptorChannelController -+ -+++ (void) initialize -++{ -++ NSUserDefaults *ud; -++ -++ ud = [NSUserDefaults standardUserDefaults]; -++ debugOn = [ud boolForKey: @"OracleAdaptorDebug"]; -++} -++ -+ - (EODelegateResponse) adaptorChannel: (id) theChannel -+ willInsertRow: (NSMutableDictionary *) theRow -+ forEntity: (EOEntity *) theEntity -+@@ -56,7 +66,8 @@ -+ NSArray *keys; -+ int i, c; -+ -+- NSLog(@"willInsertRow: %@ %@", [theRow description], [theEntity description]); -++ if (debugOn) -++ NSLog(@"willInsertRow: %@ %@", [theRow description], [theEntity description]); -+ -+ s = AUTORELEASE([[NSMutableString alloc] init]); -+ -+@@ -101,7 +112,8 @@ -+ NSArray *keys; -+ int i, c; -+ -+- NSLog(@"willUpdatetRow: %@ %@", [theRow description], [theQualifier description]); -++ if (debugOn) -++ NSLog(@"willUpdateRow: %@ %@", [theRow description], [theQualifier description]); -+ -+ s = AUTORELEASE([[NSMutableString alloc] init]); -+ -+Index: sope-mime/NGImap4/NGImap4Functions.m -+=================================================================== -+--- sope-mime/NGImap4/NGImap4Functions.m (revision 1660) -++++ sope-mime/NGImap4/NGImap4Functions.m (working copy) -+@@ -367,3 +367,16 @@ -+ } -+ -+ @end /* NGImap4FolderHandler */ -++ -++NSString * -++SaneFolderName(NSString *folderName) -++{ -++ NSString *saneFName; -++ -++ saneFName = [[folderName stringByReplacingString: @"\\" -++ withString: @"\\\\"] -++ stringByReplacingString: @"\"" -++ withString: @"\\\""]; -++ -++ return saneFName; -++} -+Index: sope-mime/NGImap4/NGImap4Client.h -+=================================================================== -+--- sope-mime/NGImap4/NGImap4Client.h (revision 1660) -++++ sope-mime/NGImap4/NGImap4Client.h (working copy) -+@@ -62,6 +62,8 @@ -+ NGImap4ResponseNormalizer *normer; -+ NSMutableArray *responseReceiver; -+ -++ BOOL loggedIn; -++ -+ BOOL isLogin; -+ unsigned tagId; -+ -+@@ -120,6 +122,7 @@ -+ - (NSDictionary *)list:(NSString *)_folder pattern:(NSString *)_pattern; -+ - (NSDictionary *)lsub:(NSString *)_folder pattern:(NSString *)_pattern; -+ - (NSDictionary *)select:(NSString *)_folder; -++- (NSDictionary *)unselect; -+ - (NSDictionary *)status:(NSString *)_folder flags:(NSArray *)_flags; -+ - (NSDictionary *)rename:(NSString *)_folder to:(NSString *)_newName; -+ - (NSDictionary *)delete:(NSString *)_folder; -+@@ -138,7 +141,7 @@ -+ flags:(NSArray *)_flags; -+ - (NSDictionary *)storeFrom:(unsigned)_from to:(unsigned)_to -+ add:(NSNumber *)_add flags:(NSArray *)_flags; -+-- (NSDictionary *)storeFlags:(NSArray *)_flags forMSNs:(id)_msns -++- (NSDictionary *)storeFlags:(NSArray *)_flags forUIDs:(id)_uids -+ addOrRemove:(BOOL)_flag; -+ -+ - (NSDictionary *)copyUid:(unsigned)_uid toFolder:(NSString *)_folder; -+Index: sope-mime/NGImap4/NGImap4Client.m -+=================================================================== -+--- sope-mime/NGImap4/NGImap4Client.m (revision 1660) -++++ sope-mime/NGImap4/NGImap4Client.m (working copy) -+@@ -24,6 +24,8 @@ -+ #include "NGImap4Client.h" -+ #include "NGImap4Context.h" -+ #include "NGImap4Support.h" -++#include "NGImap4Envelope.h" -++#include "NGImap4EnvelopeAddress.h" -+ #include "NGImap4Functions.h" -+ #include "NGImap4ResponseParser.h" -+ #include "NGImap4ResponseNormalizer.h" -+@@ -53,17 +55,17 @@ -+ -+ @end /* NGImap4Client(ConnectionRegistration); */ -+ -+-#if GNUSTEP_BASE_LIBRARY -+-/* FIXME: TODO: move someplace better (hh: NGExtensions...) */ -+-@implementation NSException(setUserInfo) -++// #if GNUSTEP_BASE_LIBRARY -++// /* FIXME: TODO: move someplace better (hh: NGExtensions...) */ -++// @implementation NSException(setUserInfo) -+ -+-- (id)setUserInfo:(NSDictionary *)_userInfo { -+- ASSIGN(self->_e_info, _userInfo); -+- return self; -+-} -++// - (id)setUserInfo:(NSDictionary *)_userInfo { -++// ASSIGN(self->_e_info, _userInfo); -++// return self; -++// } -+ -+-@end /* NSException(setUserInfo) */ -+-#endif -++// @end /* NSException(setUserInfo) */ -++// #endif -+ -+ @interface NGImap4Client(Private) -+ -+@@ -84,6 +86,8 @@ -+ -+ - (NSDictionary *)login; -+ -++- (NSDictionary *) _sopeSORT: (id)_sortSpec qualifier:(EOQualifier *)_qual encoding:(NSString *)_encoding; -++ -+ @end -+ -+ /* -+@@ -110,6 +114,8 @@ -+ static BOOL ImapDebugEnabled = NO; -+ static NSArray *Imap4SystemFlags = nil; -+ -++static NSMutableDictionary *capabilities; -++ -+ - (BOOL)useSSL { -+ return self->useSSL; -+ } -+@@ -140,6 +146,8 @@ -+ -+ Imap4SystemFlags = [[NSArray alloc] initWithObjects: @"seen", @"answered", -+ @"deleted", @"draft", nil]; -++ -++ capabilities = [[NSMutableDictionary alloc] init]; -+ } -+ -+ /* constructors */ -+@@ -195,11 +203,14 @@ -+ self->debug = ImapDebugEnabled; -+ self->responseReceiver = [[NSMutableArray alloc] initWithCapacity:128]; -+ self->normer = [[NGImap4ResponseNormalizer alloc] initWithClient:self]; -++ self->loggedIn = NO; -++ self->context = nil; -+ } -+ return self; -+ } -+ -+ - (void)dealloc { -++ if (self->loggedIn) [self logout]; -+ [self removeFromConnectionRegister]; -+ [self->normer release]; -+ [self->text release]; -+@@ -457,8 +468,8 @@ -+ - (void)reconnect { -+ if ([self->context lastException] != nil) -+ return; -+- -+- [self closeConnection]; -++ -++ [self closeConnection]; -+ self->tagId = 0; -+ [self openConnection]; -+ -+@@ -481,6 +492,7 @@ -+ */ -+ NGHashMap *map; -+ NSString *s, *log; -++ NSDictionary *response; -+ -+ if (self->isLogin ) -+ return nil; -+@@ -499,7 +511,11 @@ -+ -+ self->isLogin = NO; -+ -+- return [self->normer normalizeResponse:map]; -++ response = [self->normer normalizeResponse:map]; -++ -++ self->loggedIn = [[response valueForKey:@"result"] boolValue]; -++ -++ return response; -+ } -+ -+ - (NSDictionary *)logout { -+@@ -508,6 +524,8 @@ -+ -+ map = [self processCommand:@"logout"]; -+ [self closeConnection]; -++ [self->selectedFolder release]; self->selectedFolder = nil; -++ self->loggedIn = NO; -+ -+ return [self->normer normalizeResponse:map]; -+ } -+@@ -547,7 +565,7 @@ -+ if (!(_pattern = [self _folder2ImapFolder:_pattern])) -+ return nil; -+ -+- s = [NSString stringWithFormat:@"list \"%@\" \"%@\"", _folder, _pattern]; -++ s = [NSString stringWithFormat:@"list \"%@\" \"%@\"", SaneFolderName(_folder), _pattern]; -+ map = [self processCommand:s]; -+ -+ if (self->delimiter == nil) { -+@@ -563,9 +581,20 @@ -+ } -+ -+ - (NSDictionary *)capability { -++ NSDictionary *result; -+ id capres; -+- capres = [self processCommand:@"capability"]; -+- return [self->normer normalizeCapabilityRespone:capres]; -++ -++ result = [capabilities objectForKey: [self->address description]]; -++ -++ if (!result) -++ { -++ capres = [self processCommand:@"capability"]; -++ result = [self->normer normalizeCapabilityRespone:capres]; -++ -++ if (result) -++ [capabilities setObject: result forKey: [self->address description]]; -++ } -++ return result; -+ } -+ -+ - (NSDictionary *)lsub:(NSString *)_folder pattern:(NSString *)_pattern { -+@@ -591,7 +620,7 @@ -+ return nil; -+ } -+ -+- s = [NSString stringWithFormat:@"lsub \"%@\" \"%@\"", _folder, _pattern]; -++ s = [NSString stringWithFormat:@"lsub \"%@\" \"%@\"", SaneFolderName(_folder), _pattern]; -+ map = [self processCommand:s]; -+ -+ if (self->delimiter == nil) { -+@@ -617,24 +646,25 @@ -+ 'flags' - array of strings (eg (answered,flagged,draft,seen); -+ 'RawResponse' - the raw IMAP4 response -+ */ -+- NSString *s; -+- id tmp; -+- -+- tmp = self->selectedFolder; // remember ptr to old folder name -+- -++ NSString *s, *newFolder; -++ -+ if (![_folder isNotEmpty]) -+ return nil; -+ if ((_folder = [self _folder2ImapFolder:_folder]) == nil) -+ return nil; -+ -+- self->selectedFolder = [_folder copy]; -+- -+- [tmp release]; tmp = nil; // release old folder name -++ newFolder = [NSString stringWithString: _folder]; -++ ASSIGN (self->selectedFolder, newFolder); -+ -+- s = [NSString stringWithFormat:@"select \"%@\"", self->selectedFolder]; -++ s = [NSString stringWithFormat:@"select \"%@\"", SaneFolderName(self->selectedFolder)]; -+ return [self->normer normalizeSelectResponse:[self processCommand:s]]; -+ } -+ -++- (NSDictionary *)unselect { -++ [self->selectedFolder release]; self->selectedFolder = nil; -++ return [self->normer normalizeResponse:[self processCommand:@"unselect"]]; -++} -++ -+ - (NSDictionary *)status:(NSString *)_folder flags:(NSArray *)_flags { -+ NSString *cmd; -+ -+@@ -646,7 +676,7 @@ -+ return nil; -+ -+ cmd = [NSString stringWithFormat:@"status \"%@\" (%@)", -+- _folder, [_flags componentsJoinedByString:@" "]]; -++ SaneFolderName(_folder), [_flags componentsJoinedByString:@" "]]; -+ return [self->normer normalizeStatusResponse:[self processCommand:cmd]]; -+ } -+ -+@@ -663,24 +693,28 @@ -+ if ((_newName = [self _folder2ImapFolder:_newName]) == nil) -+ return nil; -+ -+- cmd = [NSString stringWithFormat:@"rename \"%@\" \"%@\"", _folder, _newName]; -++ cmd = [NSString stringWithFormat:@"rename \"%@\" \"%@\"", -++ SaneFolderName(_folder), SaneFolderName(_newName)]; -+ -+ return [self->normer normalizeResponse:[self processCommand:cmd]]; -+ } -+ -+ - (NSDictionary *)_performCommand:(NSString *)_op onFolder:(NSString *)_fname { -+ NSString *command; -+- -++ -+ if ((_fname = [self _folder2ImapFolder:_fname]) == nil) -+ return nil; -+- -++ -+ // eg: 'delete "blah"' -+- command = [NSString stringWithFormat:@"%@ \"%@\"", _op, _fname]; -+- -++ command = [NSString stringWithFormat:@"%@ \"%@\"", _op, SaneFolderName(_fname)]; -++ -+ return [self->normer normalizeResponse:[self processCommand:command]]; -+ } -+ -+ - (NSDictionary *)delete:(NSString *)_name { -++ if ([self->selectedFolder isEqualToString:_name]) { -++ [self unselect]; -++ } -+ return [self _performCommand:@"delete" onFolder:_name]; -+ } -+ - (NSDictionary *)create:(NSString *)_name { -+@@ -820,23 +854,23 @@ -+ return [self->normer normalizeResponse:[self processCommand:cmd]]; -+ } -+ -+-- (NSDictionary *)storeFlags:(NSArray *)_flags forMSNs:(id)_msns -++- (NSDictionary *)storeFlags:(NSArray *)_flags forUIDs:(id)_uids -+ addOrRemove:(BOOL)_flag -+ { -+ NSString *cmd; -+ NSString *flagstr; -+ NSString *seqstr; -+ -+- if ([_msns isKindOfClass:[NSArray class]]) { -++ if ([_uids isKindOfClass:[NSArray class]]) { -+ // TODO: improve by using ranges, eg 1:5 instead of 1,2,3,4,5 -+- _msns = [_msns valueForKey:@"stringValue"]; -+- seqstr = [_msns componentsJoinedByString:@","]; -++ _uids = [_uids valueForKey:@"stringValue"]; -++ seqstr = [_uids componentsJoinedByString:@","]; -+ } -+ else -+- seqstr = [_msns stringValue]; -++ seqstr = [_uids stringValue]; -+ -+ flagstr = [_flags2ImapFlags(self, _flags) componentsJoinedByString:@" "]; -+- cmd = [NSString stringWithFormat:@"store %@ %cFLAGS (%@)", -++ cmd = [NSString stringWithFormat:@"UID STORE %@ %cFLAGS (%@)", -+ seqstr, _flag ? '+' : '-', flagstr]; -+ -+ return [self->normer normalizeResponse:[self processCommand:cmd]]; -+@@ -967,11 +1001,12 @@ -+ descr = @"Could not process qualifier for imap search "; -+ descr = [descr stringByAppendingString:reason]; -+ -+- exception = [[NGImap4SearchException alloc] initWithFormat:@"%@", descr]; -+ ui = [NSDictionary dictionaryWithObject:_q forKey:@"qualifier"]; -+- [exception setUserInfo:ui]; -++ exception -++ = [NGImap4SearchException exceptionWithName: @"NGImap4SearchException" -++ reason: descr -++ userInfo: ui]; -+ [self->context setLastException:exception]; -+- [exception release]; -+ } -+ -+ - (NSString *)_searchExprForQual:(EOQualifier *)_qualifier { -+@@ -1093,7 +1128,18 @@ -+ Eg: UID SORT ( DATE REVERSE SUBJECT ) UTF-8 TODO -+ */ -+ NSString *tmp; -++ NSArray *capa; -+ -++ // We first check to see if our server supports IMAP SORT. If not -++ // we'll sort ourself the results. -++ capa = [[self capability] objectForKey: @"capability"]; -++ -++ if ([capa indexOfObject: @"sort"] == NSNotFound) -++ { -++ return [self _sopeSORT: _sortSpec qualifier: _qual encoding: _encoding]; -++ } -++ -++ -+ if ([_sortSpec isKindOfClass:[NSArray class]]) -+ tmp = [self _generateIMAP4SortOrderings:_sortSpec]; -+ else if ([_sortSpec isKindOfClass:[EOSortOrdering class]]) -+@@ -1107,9 +1153,10 @@ -+ tmp = @"DATE"; -+ } -+ -++ -+ return [self primarySort:tmp -+- qualifierString:[self _searchExprForQual:_qual] -+- encoding:_encoding]; -++ qualifierString:[self _searchExprForQual:_qual] -++ encoding:_encoding]; -+ } -+ - (NSDictionary *)sort:(NSArray *)_sortOrderings -+ qualifier:(EOQualifier *)_qual -+@@ -1130,7 +1177,7 @@ -+ return nil; -+ } -+ -+- s = [@"search" stringByAppendingString:s]; -++ s = [@"UID SEARCH" stringByAppendingString:s]; -+ return [self->normer normalizeSearchResponse:[self processCommand:s]]; -+ } -+ -+@@ -1142,7 +1189,7 @@ -+ if ((_folder = [self _folder2ImapFolder:_folder]) == nil) -+ return nil; -+ -+- cmd = [NSString stringWithFormat:@"getacl \"%@\"", _folder]; -++ cmd = [NSString stringWithFormat:@"getacl \"%@\"", SaneFolderName(_folder)]; -+ return [self->normer normalizeGetACLResponse:[self processCommand:cmd]]; -+ } -+ -+@@ -1155,7 +1202,7 @@ -+ return nil; -+ -+ cmd = [NSString stringWithFormat:@"setacl \"%@\" \"%@\" \"%@\"", -+- _folder, _uid, _r]; -++ SaneFolderName(_folder), _uid, _r]; -+ return [self->normer normalizeResponse:[self processCommand:cmd]]; -+ } -+ -+@@ -1166,7 +1213,7 @@ -+ return nil; -+ -+ cmd = [NSString stringWithFormat:@"deleteacl \"%@\" \"%@\"", -+- _folder, _uid]; -++ SaneFolderName(_folder), _uid]; -+ return [self->normer normalizeResponse:[self processCommand:cmd]]; -+ } -+ -+@@ -1177,7 +1224,7 @@ -+ return nil; -+ -+ cmd = [NSString stringWithFormat:@"listrights \"%@\" \"%@\"", -+- _folder, _uid]; -++ SaneFolderName(_folder), _uid]; -+ return [self->normer normalizeListRightsResponse:[self processCommand:cmd]]; -+ } -+ -+@@ -1187,12 +1234,94 @@ -+ if ((_folder = [self _folder2ImapFolder:_folder]) == nil) -+ return nil; -+ -+- cmd = [NSString stringWithFormat:@"myrights \"%@\"", _folder]; -++ cmd = [NSString stringWithFormat:@"myrights \"%@\"", SaneFolderName(_folder)]; -+ return [self->normer normalizeMyRightsResponse:[self processCommand:cmd]]; -+ } -+ -+ /* Private Methods */ -+ -++- (NSDictionary *) _sopeSORT: (id)_sortSpec qualifier:(EOQualifier *)_qual encoding:(NSString *)_encoding { -++ NSMutableDictionary *result; -++ NSDictionary *d; -++ NSCalendarDate *envDate; -++ -++ result = [NSMutableDictionary dictionary]; -++ [result setObject: [NSNumber numberWithBool: NO] forKey: @"result"]; -++ -++ // _sortSpec: [REVERSE] {DATE,FROM,SUBJECT} -++ d = [self searchWithQualifier: _qual]; -++ -++ if ((d = [d objectForKey: @"RawResponse"])) { -++ NSMutableDictionary *dict; -++ NSArray *a, *s_a; -++ BOOL b; -++ int i; -++ -++ a = [d objectForKey: @"search"]; -++ if ([a isNotEmpty]) { -++ d = [self fetchUids: a -++ parts: [NSArray arrayWithObjects: @"ENVELOPE", -++ @"RFC822.SIZE", nil]]; -++ a = [d objectForKey: @"fetch"]; -++ -++ dict = [NSMutableDictionary dictionary]; -++ b = YES; -++ -++ for (i = 0; i < [a count]; i++) { -++ NGImap4Envelope *env; -++ id o, uid, s; -++ -++ o = [a objectAtIndex: i]; -++ env = [o objectForKey: @"envelope"]; -++ uid = [o objectForKey: @"uid"]; -++ -++ if ([_sortSpec rangeOfString: @"SUBJECT"].length) { -++ s = [env subject]; -++ if ([s isKindOfClass: [NSData class]]) -++ s = [[[NSString alloc] initWithData: s encoding: NSUTF8StringEncoding] autorelease]; -++ -++ [dict setObject: (s != nil ? s : (id)@"") forKey: uid]; -++ } -++ else if ([_sortSpec rangeOfString: @"FROM"].length) { -++ s = [[[env from] lastObject] email]; -++ [dict setObject: (s != nil ? s : (id)@"") forKey: uid]; -++ } -++ else if ([_sortSpec rangeOfString: @"SIZE"].length) { -++ s = [o objectForKey: @"size"]; -++ [dict setObject: (s != nil ? s : [NSNumber numberWithInt: 0]) -++ forKey: uid]; -++ b = NO; -++ } -++ else { -++ envDate = [env date]; -++ if (!envDate) -++ envDate = [NSCalendarDate date]; -++ [dict setObject: envDate forKey: uid]; -++ b = NO; -++ } -++ } -++ -++ if (b) -++ s_a = [dict keysSortedByValueUsingSelector: @selector(caseInsensitiveCompare:)]; -++ else -++ s_a = [dict keysSortedByValueUsingSelector: @selector(compare:)]; -++ -++ if ([_sortSpec rangeOfString: @"REVERSE"].length) { -++ s_a = [[s_a reverseObjectEnumerator] allObjects]; -++ } -++ -++ } -++ else { -++ s_a = [NSArray array]; -++ } -++ [result setObject: [NSNumber numberWithBool: YES] forKey: @"result"]; -++ [result setObject: s_a forKey: @"sort"]; -++ } -++ -++ return result; -++} -++ -++ -+ - (NSException *)_processCommandParserException:(NSException *)_exception { -+ [self logWithFormat:@"ERROR(%s): catched IMAP4 parser exception %@: %@", -+ __PRETTY_FUNCTION__, [_exception name], [_exception reason]]; -+@@ -1412,21 +1541,24 @@ -+ return nil; -+ } -+ -+- array = [_folder pathComponents]; -++// array = [_folder pathComponents]; -++ array = [_folder componentsSeparatedByString:@"/"]; -+ -+- if ([array isNotEmpty]) { -++ if ([array count]) { -+ NSString *o; -+ -+ o = [array objectAtIndex:0]; -+- if (([o isEqualToString:@"/"]) || ([o length] == 0)) -++ if ([o length] == 0) -+ array = [array subarrayWithRange:NSMakeRange(1, [array count] - 1)]; -+- -+- o = [array lastObject]; -+- if (([o length] == 0) || ([o isEqualToString:@"/"])) -+- array = [array subarrayWithRange:NSMakeRange(0, [array count] - 1)]; -++ -++ if ([array count]) { -++ o = [array lastObject]; -++ if ([o length] == 0) -++ array = [array subarrayWithRange:NSMakeRange(0, [array count] - 1)]; -++ } -+ } -+ return [[array componentsJoinedByString:self->delimiter] -+- stringByEncodingImap4FolderName]; -++ stringByEncodingImap4FolderName]; -+ } -+ -+ - (NSString *)_imapFolder2Folder:(NSString *)_folder { -+@@ -1442,10 +1574,16 @@ -+ return nil; -+ } -+ -++ if ([_folder hasPrefix: self->delimiter]) -++ _folder = [_folder substringFromIndex: 1]; -++ if ([_folder hasSuffix: self->delimiter]) -++ _folder = [_folder substringToIndex: [_folder length] - 2]; -++ -+ array = [array arrayByAddingObjectsFromArray: -+ [_folder componentsSeparatedByString:[self delimiter]]]; -+- -+- return [[NSString pathWithComponents:array] stringByDecodingImap4FolderName]; -++ -++ return [[array componentsJoinedByString: @"/"] -++ stringByDecodingImap4FolderName]; -+ } -+ -+ - (void)setContext:(NGImap4Context *)_ctx { -+Index: sope-mime/NGImap4/NGSieveClient.m -+=================================================================== -+--- sope-mime/NGImap4/NGSieveClient.m (revision 1660) -++++ sope-mime/NGImap4/NGSieveClient.m (working copy) -+@@ -294,8 +294,8 @@ -+ return con; -+ } -+ -+- logLen = [self->login cStringLength]; -+- bufLen = (logLen * 2) + [self->password cStringLength] +2; -++ logLen = [self->login lengthOfBytesUsingEncoding: NSUTF8StringEncoding]; -++ bufLen = (logLen * 2) + [self->password lengthOfBytesUsingEncoding: NSUTF8StringEncoding] +2; -+ -+ buf = calloc(bufLen + 2, sizeof(char)); -+ -+@@ -306,8 +306,9 @@ -+ password -+ */ -+ sprintf(buf, "%s %s %s", -+- [self->login cString], [self->login cString], -+- [self->password cString]); -++ [self->login cStringUsingEncoding:NSUTF8StringEncoding], -++ [self->login cStringUsingEncoding:NSUTF8StringEncoding], -++ [self->password cStringUsingEncoding:NSUTF8StringEncoding]); -+ -+ buf[logLen] = '\0'; -+ buf[logLen * 2 + 1] = '\0'; -+@@ -656,7 +657,7 @@ -+ fputc('\n', stderr); -+ } -+ else -+- fprintf(stderr, "C: %s\n", [_txt cString]); -++ fprintf(stderr, "C: %s\n", [_txt cStringUsingEncoding:NSUTF8StringEncoding]); -+ } -+ -+ /* write */ -+Index: sope-mime/NGImap4/NGImap4Connection.h -+=================================================================== -+--- sope-mime/NGImap4/NGImap4Connection.h (revision 1660) -++++ sope-mime/NGImap4/NGImap4Connection.h (working copy) -+@@ -89,6 +89,9 @@ -+ -+ - (NSArray *)subfoldersForURL:(NSURL *)_url; -+ - (NSArray *)allFoldersForURL:(NSURL *)_url; -++- (NSArray *)allFoldersForURL:(NSURL *)_url -++ onlySubscribedFolders: (BOOL) subscribedFoldersOnly; -++- (BOOL)selectFolder:(id)_url; -+ -+ /* message operations */ -+ -+Index: sope-mime/NGImap4/NGImap4Connection.m -+=================================================================== -+--- sope-mime/NGImap4/NGImap4Connection.m (revision 1660) -++++ sope-mime/NGImap4/NGImap4Connection.m (working copy) -+@@ -22,6 +22,7 @@ -+ #include "NGImap4Connection.h" -+ #include "NGImap4MailboxInfo.h" -+ #include "NGImap4Client.h" -++#include "NGImap4Functions.h" -+ #include "imCommon.h" -+ -+ @implementation NGImap4Connection -+@@ -66,7 +67,7 @@ -+ self->creationTime = [[NSDate alloc] init]; -+ -+ // TODO: retrieve from IMAP4 instead of using a default -+- self->separator = imap4Separator; -++ self->separator = [imap4Separator copy]; -+ } -+ return self; -+ } -+@@ -321,13 +322,15 @@ -+ return nil; -+ if ([folderName characterAtIndex:0] == '/') -+ folderName = [folderName substringFromIndex:1]; -++ if ([folderName hasSuffix: @"/"]) -++ folderName = [folderName substringToIndex:[folderName length] - 1]; -+ -+ if (_delfn) folderName = [folderName stringByDeletingLastPathComponent]; -+ -+ if ([[self imap4Separator] isEqualToString:@"/"]) -+ return folderName; -+ -+- names = [folderName pathComponents]; -++ names = [folderName componentsSeparatedByString: @"/"]; -+ return [names componentsJoinedByString:[self imap4Separator]]; -+ } -+ - (NSString *)imap4FolderNameForURL:(NSURL *)_url { -+@@ -373,7 +376,9 @@ -+ -+ /* folder operations */ -+ -+-- (NSDictionary *)primaryFetchMailboxHierarchyForURL:(NSURL *)_url { -++- (NSDictionary *)primaryFetchMailboxHierarchyForURL:(NSURL *)_url -++ onlySubscribedFolders: (BOOL) subscribedFoldersOnly -++{ -+ NSDictionary *result; -+ -+ if ((result = [self cachedHierarchyResults]) != nil) -+@@ -381,8 +386,12 @@ -+ -+ if (debugCache) [self logWithFormat:@" no folders cached yet .."]; -+ -+- result = [[self client] list:(onlyFetchInbox ? @"INBOX" : @"*") -+- pattern:@"*"]; -++ if (subscribedFoldersOnly) -++ result = [[self client] lsub:(onlyFetchInbox ? @"INBOX" : @"") -++ pattern:@"*"]; -++ else -++ result = [[self client] list:(onlyFetchInbox ? @"INBOX" : @"") -++ pattern:@"*"]; -+ if (![[result valueForKey:@"result"] boolValue]) { -+ [self errorWithFormat:@"Could not list mailbox hierarchy!"]; -+ return nil; -+@@ -400,6 +409,11 @@ -+ return result; -+ } -+ -++- (NSDictionary *)primaryFetchMailboxHierarchyForURL:(NSURL *)_url -++{ -++ return [self primaryFetchMailboxHierarchyForURL: _url onlySubscribedFolders: NO]; -++} -++ -+ - (NSArray *)subfoldersForURL:(NSURL *)_url { -+ NSDictionary *result; -+ -+@@ -413,10 +427,13 @@ -+ return [self extractSubfoldersForURL:_url fromResultSet:result]; -+ } -+ -+-- (NSArray *)allFoldersForURL:(NSURL *)_url { -++- (NSArray *)allFoldersForURL:(NSURL *)_url -++ onlySubscribedFolders: (BOOL) subscribedFoldersOnly -++{ -+ NSDictionary *result; -+ -+- if ((result = [self primaryFetchMailboxHierarchyForURL:_url]) == nil) -++ if ((result = [self primaryFetchMailboxHierarchyForURL:_url -++ onlySubscribedFolders: subscribedFoldersOnly]) == nil) -+ return nil; -+ if ([result isKindOfClass:[NSException class]]) { -+ [self errorWithFormat:@"failed to retrieve hierarchy: %@", result]; -+@@ -426,6 +443,11 @@ -+ return [self extractFoldersFromResultSet:result]; -+ } -+ -++- (NSArray *)allFoldersForURL:(NSURL *)_url -++{ -++ return [self allFoldersForURL: _url onlySubscribedFolders: NO]; -++} -++ -+ /* message operations */ -+ -+ - (NSArray *)fetchUIDsInURL:(NSURL *)_url qualifier:(id)_qualifier -+@@ -646,7 +668,7 @@ -+ -+ /* store flags */ -+ -+- result = [[self client] storeFlags:_f forMSNs:result addOrRemove:YES]; -++ result = [[self client] storeFlags:_f forUIDs:result addOrRemove:YES]; -+ if (![[result valueForKey:@"result"] boolValue]) { -+ return [self errorForResult:result -+ text:@"Failed to change flags of IMAP4 message"]; -+@@ -760,11 +782,11 @@ -+ // TODO: we should probably just fetch the whole hierarchy? -+ -+ folderName = [self imap4FolderNameForURL:_url]; -+- result = [[self client] select:folderName]; -+- if (![[result valueForKey:@"result"] boolValue]) -+- return NO; -+- -+- return YES; -++ -++ result = [self->client status: folderName -++ flags: [NSArray arrayWithObject: @"UIDVALIDITY"]]; -++ -++ return ([[result valueForKey: @"result"] boolValue]); -+ } -+ -+ - (id)infoForMailboxAtURL:(NSURL *)_url { -+@@ -789,7 +811,8 @@ -+ /* construct path */ -+ -+ newPath = [self imap4FolderNameForURL:_url]; -+- newPath = [newPath stringByAppendingString:[self imap4Separator]]; -++ if ([newPath length]) -++ newPath = [newPath stringByAppendingString:[self imap4Separator]]; -+ newPath = [newPath stringByAppendingString:_mailbox]; -+ -+ /* create */ -+Index: sope-mime/NGImap4/NGImap4ResponseNormalizer.m -+=================================================================== -+--- sope-mime/NGImap4/NGImap4ResponseNormalizer.m (revision 1660) -++++ sope-mime/NGImap4/NGImap4ResponseNormalizer.m (working copy) -+@@ -76,22 +76,6 @@ -+ return self; -+ } -+ -+-/* client callbacks */ -+- -+-- (void)closeConnection { -+- [(id)self->client closeConnection]; -+-} -+- -+-- (NSString *)delimiter { -+- return [self->client delimiter]; -+-} -+- -+-/* folder handling */ -+- -+-- (NSString *)_imapFolder2Folder:(NSString *)_folder { -+- return [self->client _imapFolder2Folder:_folder]; -+-} -+- -+ /* primary */ -+ -+ - (NSMutableDictionary *)normalizeResponse:(NGHashMap *)_map { -+@@ -117,7 +101,7 @@ -+ if ((obj = [_map objectForKey:@"bye"])) { -+ [result setObject:NoNumber forKey:@"result"]; -+ [result setObject:obj forKey:@"reason"]; -+- [self closeConnection]; -++ [self->client closeConnection]; -+ return result; -+ } -+ -+@@ -292,7 +276,7 @@ -+ /* -+ filter for fetch response -+ fetch : NSArray (fetch responses) -+- 'header' - RFC822.HEADER -++ 'header' - RFC822.HEADER and BODY[HEADER.FIELDS (...)] -+ 'text' - RFC822.TEXT -+ 'size' - SIZE -+ 'flags' - FLAGS -+@@ -336,7 +320,12 @@ -+ switch (c) { -+ case 'b': -+ /* Note: we check for _prefix_! eg body[1] is valid too */ -+- if (klen > 3 && [key hasPrefix:@"body"]) { -++ if (klen > 17 && [key hasPrefix:@"body[header.fields"]) { -++ keys[count] = @"header"; -++ values[count] = objForKey(obj, @selector(objectForKey:), key); -++ count++; -++ } -++ else if (klen > 3 && [key hasPrefix:@"body"]) { -+ keys[count] = @"body"; -+ values[count] = objForKey(obj, @selector(objectForKey:), key); -+ count++; -+@@ -516,7 +505,7 @@ -+ } -+ continue; -+ } -+- [tmp setObject:qDesc forKey:[self _imapFolder2Folder:obj]]; -++ [tmp setObject:qDesc forKey:[self->client _imapFolder2Folder:obj]]; -+ } -+ [result setObject:tmp forKey:@"quotas"]; -+ return [[result copy] autorelease]; -+@@ -615,7 +604,7 @@ -+ -+ while ((o = [enumerator nextObject])) { -+ [folder setObject:_imapFlags2Flags(self, [o objectForKey:@"flags"]) -+- forKey:[self _imapFolder2Folder:[o objectForKey:@"folderName"]]]; -++ forKey:[self->client _imapFolder2Folder:[o objectForKey:@"folderName"]]]; -+ } -+ -+ { -+@@ -648,14 +637,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/EOQualifier+IMAPAdditions.m -+=================================================================== -+--- sope-mime/NGImap4/EOQualifier+IMAPAdditions.m (revision 1660) -++++ sope-mime/NGImap4/EOQualifier+IMAPAdditions.m (working copy) -+@@ -53,13 +53,13 @@ -+ if (FlagKeyWords) return; -+ -+ ud = [NSUserDefaults standardUserDefaults]; -+- FlagKeyWords = [[NSArray alloc] initWithObjects: @"answered", @"deleted", -+- @"draft", @"flagged", @"new", @"old", @"recent", -+- @"seen", @"unanswered", @"undeleted", @"undraft", -+- @"unflagged", @"unseen", nil]; -+- OtherKeyWords = [[NSArray alloc] initWithObjects: -+- @"bcc", @"body", @"cc", @"from", @"subject", -+- @"text", @"to", @"keyword", @"unkeyword", nil]; -++ FlagKeyWords = [[NSArray alloc] initWithObjects: @"ANSWERED", @"DELETED", -++ @"DRAFT", @"FLAGGED", @"NEW", @"OLD", @"RECENT", -++ @"SEEN", @"UNANSWERED", @"UNDELETED", @"UNDRAFT", -++ @"UNFLAGGED", @"UNSEEN", nil]; -++ OtherKeyWords = [[NSArray alloc] initWithObjects: @"ALL", @"BCC", @"BODY", -++ @"CC", @"FROM", @"SUBJECT", @"TEXT", @"TO", -++ @"KEYWORD", @"UID", @"UNKEYWORD", nil]; -+ -+ debugOn = [ud boolForKey:@"ImapDebugQualifierGeneration"]; -+ } -+@@ -266,10 +266,10 @@ -+ -+ enumerator = [lvalue objectEnumerator]; -+ while ((lvalue = [enumerator nextObject]) != nil) { -+- lvalue = [lvalue lowercaseString]; -++ lvalue = [lvalue uppercaseString]; -+ -+ if ([FlagKeyWords containsObject:lvalue]) { -+- if (insertNot) [search appendString:@"not "]; -++ if (insertNot) [search appendString:@"NOT "]; -+ [search appendString:lvalue]; -+ } -+ else { -+@@ -280,15 +280,31 @@ -+ return nil; -+ } -+ -+-- (NSString *)imap4OperatorForDateComparisonSelector:(SEL)lselector { -++- (NSString *)imap4OperatorForDateKeyword:(NSString *)dkey -++andComparisonSelector:(SEL)lselector { -++ NSString *operatorPrefix, *dateOperator, *imap4Operator; -++ -+ if (sel_eq(lselector, EOQualifierOperatorEqual)) -+- return @" senton "; -+- if (sel_eq(lselector, EOQualifierOperatorGreaterThan)) -+- return @" sentsince "; -+- if (sel_eq(lselector, EOQualifierOperatorLessThan)) -+- return @" sentbefore "; -+- -+- return nil; -++ dateOperator = @"ON"; -++ else if (sel_eq(lselector, EOQualifierOperatorGreaterThan)) -++ dateOperator = @"SINCE"; -++ else if (sel_eq(lselector, EOQualifierOperatorLessThan)) -++ dateOperator = @"BEFORE"; -++ else -++ dateOperator = nil; -++ -++ if (dateOperator) { -++ if ([dkey isEqualToString: @"DATE"]) -++ operatorPrefix = @"SENT"; -++ else -++ operatorPrefix = @""; -++ imap4Operator = [NSString stringWithFormat: @"%@%@ ", -++ operatorPrefix, dateOperator]; -++ } -++ else -++ imap4Operator = nil; -++ -++ return imap4Operator; -+ } -+ -+ - (NSException *)appendToImap4SearchString:(NSMutableString *)search -+@@ -300,11 +316,11 @@ -+ id lvalue; -+ SEL lselector; -+ -+- lkey = [[self key] lowercaseString]; -++ lkey = [[self key] uppercaseString]; -+ lvalue = [self value]; -+ lselector = [self selector]; -+ -+- if ([lkey isEqualToString:@"flags"]) { -++ if ([lkey isEqualToString:@"FLAGS"]) { -+ /* NOTE: special "not" processing! */ -+ return [self appendFlagsCheckToImap4SearchString:search -+ insertNot:insertNot]; -+@@ -312,9 +328,9 @@ -+ -+ /* not a flag */ -+ if (insertNot) -+- [search appendString:@"not "]; -++ [search appendString:@"NOT "]; -+ -+- if ([lkey isEqualToString:@"date"]) { -++ if ([lkey isEqualToString:@"DATE"] || [lkey isEqualToString:@"RECEIVE-DATE"]) { -+ NSString *s; -+ -+ if (![lvalue isKindOfClass:[NSCalendarDate class]]) { -+@@ -322,35 +338,38 @@ -+ @"expected a NSDate as value"]; -+ } -+ -+- if ((s = [self imap4OperatorForDateComparisonSelector:lselector]) == nil) -++ if ((s = [self imap4OperatorForDateKeyword:lkey -++ andComparisonSelector:lselector]) == nil) -+ return [self invalidImap4SearchQualifier:@"unexpected selector"]; -+ -+- // TODO: operator created but NOT added? -++ [search appendString:s]; -+ -+ // TODO: much faster without descriptionWithCalendarFormat:?! -+- s = [lvalue descriptionWithCalendarFormat:@"%d-%b-%Y"]; -++ s = [lvalue descriptionWithCalendarFormat:@"\"%d-%b-%Y\""]; -+ [search appendString:s]; -+ return nil; -+ } -+ -+- if ([lkey isEqualToString:@"uid"]) { -+- if (!sel_eq(lselector, EOQualifierOperatorEqual)) -++ if ([lkey isEqualToString:@"UID"]) { -++ if (!sel_eq(lselector, EOQualifierOperatorEqual)) { -+ return [self invalidImap4SearchQualifier:@"unexpected qualifier 2"]; -++ } -+ -+- [search appendString:@"uid "]; -++ [search appendString:@"UID "]; -+ [search appendString:[lvalue stringValue]]; -+ return nil; -+ } -+ -+- if ([lkey isEqualToString:@"size"]) { -++ if ([lkey isEqualToString:@"SIZE"]) { -+ if (sel_eq(lselector, EOQualifierOperatorGreaterThan)) -+- [search appendString:@"larger "]; -++ [search appendString:@"LARGER "]; -+ else if (sel_eq(lselector, EOQualifierOperatorLessThan)) -+- [search appendString:@"smaller "]; -++ [search appendString:@"SMALLER "]; -+ else -+ return [self invalidImap4SearchQualifier:@"unexpected qualifier 3"]; -+ -+ [search appendString:[lvalue stringValue]]; -++ -+ return nil; -+ } -+ -+@@ -386,7 +405,7 @@ -+ if (!sel_eq(lselector, EOQualifierOperatorEqual)) -+ return [self invalidImap4SearchQualifier:@"unexpected qualifier 5"]; -+ -+- [search appendString:@"header "]; -++ [search appendString:@"HEADER "]; -+ [search appendString:lkey]; -+ [search appendString:@" \""]; -+ [search appendString:[lvalue stringValue]]; -+Index: sope-mime/NGImap4/NGImap4ResponseParser.m -+=================================================================== -+--- sope-mime/NGImap4/NGImap4ResponseParser.m (revision 1660) -++++ sope-mime/NGImap4/NGImap4ResponseParser.m (working copy) -+@@ -31,6 +31,7 @@ -+ @interface NGImap4ResponseParser(ParsingPrivates) -+ - (BOOL)_parseNumberUntaggedResponse:(NGMutableHashMap *)result_; -+ - (NSDictionary *)_parseBodyContent; -++- (NSData *) _parseBodyHeaderFields; -+ -+ - (NSData *)_parseData; -+ -+@@ -84,6 +85,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 +114,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); -+@@ -488,6 +492,50 @@ -+ return [self _parseDataIntoRAM:size]; -+ } -+ -++/* -++ Similair to _parseData but used to parse something like this : -++ -++ BODY[HEADER.FIELDS (X-PRIORITY)] {17} -++ X-Priority: 1 -++ -++ ) -++ -++ Headers are returned as data, as is. -++*/ -++- (NSData *) _parseBodyHeaderFields -++{ -++ NSData *result; -++ unsigned size; -++ NSNumber *sizeNum; -++ -++ /* we skip until we're ready to parse {length} */ -++ _parseUntil(self, '{'); -++ -++ result = nil; -++ -++ if ((sizeNum = _parseUnsigned(self)) == nil) { -++ NSException *e; -++ -++ e = [[NGImap4ParserException alloc] -++ initWithFormat:@"expect a number between {}"]; -++ [self setLastException:[e autorelease]]; -++ return nil; -++ } -++ _consumeIfMatch(self, '}'); -++ _consumeIfMatch(self, '\n'); -++ -++ if ((size = [sizeNum intValue]) == 0) { -++ [self logWithFormat:@"ERROR(%s): got content size '0'!", -++ __PRETTY_FUNCTION__]; -++ return nil; -++ } -++ -++ if (UseMemoryMappedData && (size > Imap4MMDataBoundary)) -++ return [self _parseDataToFile:size]; -++ -++ return [self _parseDataIntoRAM:size]; -++} -++ -+ static int _parseTaggedResponse(NGImap4ResponseParser *self, -+ NGMutableHashMap *result_) -+ { -+@@ -648,13 +696,124 @@ -+ [result_ addObject:_parseUntil(self, '\n') forKey:@"description"]; -+ } -+ -++static inline void -++_purifyQuotedString(NSMutableString *quotedString) { -++ unichar *currentChar, *qString, *maxC, *startC; -++ unsigned int max, questionMarks; -++ BOOL possiblyQuoted, skipSpaces; -++ NSMutableString *newString; -++ -++ newString = [NSMutableString string]; -++ -++ max = [quotedString length]; -++ qString = malloc (sizeof (unichar) * max); -++ [quotedString getCharacters: qString]; -++ currentChar = qString; -++ startC = qString; -++ maxC = qString + max; -++ -++ possiblyQuoted = NO; -++ skipSpaces = NO; -++ -++ questionMarks = 0; -++ -++ while (currentChar < maxC) { -++ if (possiblyQuoted) { -++ if (questionMarks == 2) { -++ if ((*currentChar == 'Q' || *currentChar == 'q' -++ || *currentChar == 'B' || *currentChar == 'b') -++ && ((currentChar + 1) < maxC -++ && (*(currentChar + 1) == '?'))) { -++ currentChar++; -++ questionMarks = 3; -++ } -++ else { -++ possiblyQuoted = NO; -++ } -++ } -++ else if (questionMarks == 4) { -++ if (*currentChar == '=') { -++ skipSpaces = YES; -++ possiblyQuoted = NO; -++ currentChar++; -++ [newString appendString: [NSString stringWithCharacters: startC -++ length: (currentChar - startC)]]; -++ startC = currentChar; -++ } -++ else { -++ possiblyQuoted = NO; -++ } -++ } -++ else { -++ if (*currentChar == '?') { -++ questionMarks++; -++ } -++ else if (*currentChar == ' ' && questionMarks != 3) { -++ possiblyQuoted = NO; -++ } -++ } -++ } -++ else if (*currentChar == '=' -++ && ((currentChar + 1) < maxC -++ && (*(currentChar + 1) == '?'))) { -++ [newString appendString: [NSString stringWithCharacters: startC -++ length: (currentChar - startC)]]; -++ startC = currentChar; -++ possiblyQuoted = YES; -++ skipSpaces = NO; -++ currentChar++; -++ questionMarks = 1; -++ } -++ -++ currentChar++; -++ -++ if (skipSpaces) { -++ while (currentChar < maxC -++ && (*currentChar == ' ' -++ || *currentChar == '\t')) -++ currentChar++; -++ skipSpaces = NO; -++ startC = currentChar; -++ } -++ } -++ -++ if (startC < maxC) -++ [newString appendString: [NSString stringWithCharacters: startC -++ length: (currentChar - startC)]]; -++ -++ [quotedString setString: newString]; -++ free (qString); -++} -++ -+ - (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; -++ } -++ -++ _purifyQuotedString(quotedString); -++ -++ return quotedString; -+ } -+ - (void)_consumeOptionalSpace { -+ if (_la(self, 0) == ' ') _consume(self, 1); -+@@ -685,6 +844,10 @@ -+ name = [self _parseQuotedString]; -+ _parseUntil(self, '\n'); -+ } -++ else if (_la(self, 0) == '{') { -++ name = [self _parseQuotedStringOrNIL]; -++ _parseUntil(self, '\n'); -++ } -+ else -+ name = _parseUntil(self, '\n'); -+ -+@@ -1030,10 +1193,15 @@ -+ _consume(self, 7); -+ -+ if (_la(self, 0) == '"') { -+- _consume(self, 1); -+- name = _parseUntil(self, '"'); -++ name = [self _parseQuotedString]; -++// _consume(self, 1); -++// name = _parseUntil(self, '"'); -+ _consumeIfMatch(self, ' '); -+ } -++ else if (_la(self, 0) == '{') { -++ name = [self _parseQuotedStringOrNIL]; -++ _consumeIfMatch(self, ' '); -++ } -+ else { -+ name = _parseUntil(self, ' '); -+ } -+@@ -1090,6 +1258,8 @@ -+ return @""; -+ -+ s = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; -++ if (s == nil) -++ s = [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding]; -+ if (s == nil) { -+ [self logWithFormat: -+ @"ERROR(%s): could not convert data (%d bytes) into string.", -+@@ -1185,7 +1355,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 +1367,7 @@ -+ address = [[NGImap4EnvelopeAddress alloc] initWithPersonalName:pname -+ sourceRoute:route mailbox:mailbox -+ host:host]; -++ -+ return address; -+ } -+ -+@@ -1382,7 +1553,15 @@ -+ #if 0 -+ [self logWithFormat:@"PARSE KEY: %@", key]; -+ #endif -+- if ([key hasPrefix:@"body["]) { -++ if ([key hasPrefix:@"body[header.fields"]) { -++ NSData *content; -++ -++ if ((content = [self _parseBodyHeaderFields]) != nil) -++ [fetch setObject:content forKey:key]; -++ else -++ [self logWithFormat:@"ERROR: got no body content for key: '%@'",key]; -++ } -++ else if ([key hasPrefix:@"body["]) { -+ NSDictionary *content; -+ -+ if ((content = [self _parseBodyContent]) != nil) -+@@ -1594,8 +1773,11 @@ -+ if (_decode) -+ data = [data decodeQuotedPrintableValueOfMIMEHeaderField:nil]; -+ -+- return [[[StrClass alloc] initWithData:data encoding:encoding] -+- autorelease]; -++ if ([data isKindOfClass: [NSString class]]) -++ return (NSString *) data; -++ else -++ return [[[StrClass alloc] initWithData:data encoding:encoding] -++ autorelease]; -+ } -+ else { -+ str = _parseUntil2(self, ' ', ')'); -+@@ -1620,13 +1802,35 @@ -+ return str; -+ } -+ -+- -+ static NSString *_parseBodyString(NGImap4ResponseParser *self, -+ BOOL _convertString) -+ { -+ 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; -+@@ -1646,7 +1850,7 @@ -+ _consumeIfMatch(self, ' '); -+ value = _parseBodyDecodeString(self, YES, YES); -+ -+- [list setObject:value forKey:[key lowercaseString]]; -++ if (value) [list setObject:value forKey:[key lowercaseString]]; -+ } -+ _consumeIfMatch(self, ')'); -+ } -+@@ -1731,13 +1935,14 @@ -+ static NSDictionary *_parseSingleBody(NGImap4ResponseParser *self, -+ BOOL isBodyStructure) { -+ NSString *type, *subtype, *bodyId, *description, -+- *encoding, *bodysize; -++ *result, *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,13 +1967,18 @@ -+ _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, '('); -+- [dict setObject:_parseBodyString(self, YES) forKey:@"date"]; -++ result = _parseBodyString(self, YES); -++ if (result == nil) result = @""; -++ [dict setObject:result forKey:@"date"]; -+ _consumeIfMatch(self, ' '); -+- [dict setObject:_parseBodyString(self, YES) forKey:@"subject"]; -++ result = _parseBodyString(self, YES); -++ if (result == nil) result = @""; -++ [dict setObject:result forKey:@"subject"]; -+ _consumeIfMatch(self, ' '); -+ [dict setObject:_parseParenthesizedAddressList(self) forKey:@"from"]; -+ _consumeIfMatch(self, ' '); -+@@ -1783,14 +1993,20 @@ -+ _consumeIfMatch(self, ' '); -+ [dict setObject:_parseParenthesizedAddressList(self) forKey:@"bcc"]; -+ _consumeIfMatch(self, ' '); -+- [dict setObject:_parseBodyString(self, YES) forKey:@"in-reply-to"]; -++ result = _parseBodyString(self, YES); -++ if (result == nil) result = @""; -++ [dict setObject:result forKey:@"in-reply-to"]; -+ _consumeIfMatch(self, ' '); -+- [dict setObject:_parseBodyString(self, YES) forKey:@"messageId"]; -++ result = _parseBodyString(self, YES); -++ if (result == nil) result = @""; -++ [dict setObject:result forKey:@"messageId"]; -+ _consumeIfMatch(self, ')'); -+ _consumeIfMatch(self, ' '); -+ [dict setObject:_parseBody(self, isBodyStructure) forKey:@"body"]; -+ _consumeIfMatch(self, ' '); -+- [dict setObject:_parseBodyString(self, YES) forKey:@"bodyLines"]; -++ result = _parseBodyString(self, YES); -++ if (result == nil) result = @""; -++ [dict setObject:result forKey:@"bodyLines"]; -+ } -+ } -+ -+@@ -1805,14 +2021,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 +2040,7 @@ -+ static NSDictionary *_parseMultipartBody(NGImap4ResponseParser *self, -+ BOOL isBodyStructure) { -+ NSMutableArray *parts; -++ NSArray *languages; -+ NSString *kind; -+ NSMutableDictionary *dict; -+ -+@@ -1854,14 +2066,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 +2377,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 -+ { -+@@ -2225,9 +2447,9 @@ -+ [s release]; -+ -+ if (c == '\n') { -+- if ([self->serverResponseDebug cStringLength] > 2) { -++ if ([self->serverResponseDebug lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding] > 2) { -+ fprintf(stderr, "S[%p]: %s", self, -+- [self->serverResponseDebug cString]); -++ [self->serverResponseDebug cStringUsingEncoding:NSISOLatin1StringEncoding]); -+ } -+ [self->serverResponseDebug release]; -+ self->serverResponseDebug = -+Index: sope-mime/NGImap4/ChangeLog -+=================================================================== -+--- sope-mime/NGImap4/ChangeLog (revision 1660) -++++ sope-mime/NGImap4/ChangeLog (working copy) -+@@ -1,3 +1,61 @@ -++2009-10-06 Wolfgang Sourdeau -++ -++ * NGImap4Client.m (-delete:): if the folder we want to delete is -++ the same as self->selectedFolder, we unselect it first to ensure a -++ "SELECT" happens if a new folder with the same name is created. -++ -++ * EOQualifier+IMAPAdditions.m -++ (-imap4OperatorForDateKeyword:andComparisonSelector:): modified -++ operator handler to handle "receive-date" search key as well as -++ "date", prefixing the real filter with "sent" or not. -++ -++2009-09-22 Wolfgang Sourdeau -++ -++ * NGImap4Client.m (_sopeSORT:qualifier:encoding:): added support -++ for sorting by message size. -++ -++2009-07-01 Wolfgang Sourdeau -++ -++ * NGImap4Connection.m (-initWithClient:password:): we need to copy -++ the imap4Separator, otherwise it will be released when the connection -++ is deallocated. -++ -++2009-06-15 Wolfgang Sourdeau -++ -++ * NSString+Imap4.m (-stringByEncodingImap4FolderName, -++ -stringByDecodingImap4FolderName): reimplemented the original -++ methods in a unicode-safe way, thereby simplifying the code at the -++ same time. -++ -++ * NGImap4Functions.m (SaneFolderName): new function designed to -++ sanitize folder names prior to using them in IMAP commands. -++ -++2008-10-23 Wolfgang Sourdeau -++ -++ * NGImap4Client.m ([NGImap -sort:qualifier:encoding:]): message -++ without date that are sorted on servers which do not have the SORT -++ capability are now given the current date as a work-around. -++ -++2008-09-22 Wolfgang Sourdeau -++ -++ * NGImap4Connection.m ([NGImap -doesMailboxExistAtURL:]): restore -++ the previously selected folder state. -++ -++2008-09-19 Wolfgang Sourdeau -++ -++ * NGImap4Client.m ([NGImap -select:]): simplified method by -++ removing the need for storing the previous folder before releasing -++ it. This strangely seems to fix a crash with gnustep 1.14. -++ -++2008-09-01 Ludovic Marcotte -++ -++ * NGImap4ConnectionManager.m: implemented _garbageCollect. -++ -++2008-08-28 Wolfgang Sourdeau -++ -++ * NGImap4Client.m ([NGImap -unselect]): new method to send -++ "UNSELECT" to the imap server. -++ -+ 2007-08-24 Wolfgang Sourdeau -+ -+ * NGImap4Connection.m: some fix for folders ending with a slash (OGo -+Index: sope-mime/NGImap4/NGImap4ConnectionManager.m -+=================================================================== -+--- sope-mime/NGImap4/NGImap4ConnectionManager.m (revision 1660) -++++ sope-mime/NGImap4/NGImap4ConnectionManager.m (working copy) -+@@ -38,6 +38,9 @@ -+ debugCache = [ud boolForKey:@"NGImap4EnableIMAP4CacheDebug"]; -+ poolingOff = [ud boolForKey:@"NGImap4DisableIMAP4Pooling"]; -+ -++ if ([ud objectForKey:@"NGImap4PoolingCleanupInterval"]) -++ PoolScanInterval = [[ud objectForKey:@"NGImap4PoolingCleanupInterval"] doubleValue]; -++ -+ if (debugOn) NSLog(@"Note: NGImap4EnableIMAP4Debug is enabled!"); -+ if (poolingOff) NSLog(@"WARNING: IMAP4 connection pooling is disabled!"); -+ } -+@@ -53,18 +56,17 @@ -+ if ((self = [super init])) { -+ if (!poolingOff) { -+ self->urlToEntry = [[NSMutableDictionary alloc] initWithCapacity:256]; -++ self->gcTimer = [[NSTimer scheduledTimerWithTimeInterval: -++ PoolScanInterval -++ target:self selector:@selector(_garbageCollect:) -++ userInfo:nil repeats:YES] retain]; -+ } -+- -+- self->gcTimer = [[NSTimer scheduledTimerWithTimeInterval: -+- PoolScanInterval -+- target:self selector:@selector(_garbageCollect:) -+- userInfo:nil repeats:YES] retain]; -+ } -+ return self; -+ } -+ -+ - (void)dealloc { -+- if (self->gcTimer) [self->gcTimer invalidate]; -++ [self->gcTimer invalidate]; -+ [self->urlToEntry release]; -+ [self->gcTimer release]; -+ [super dealloc]; -+@@ -91,6 +93,25 @@ -+ -+ - (void)_garbageCollect:(NSTimer *)_timer { -+ // TODO: scan for old IMAP4 channels -++ NGImap4Connection *entry; -++ NSDate *now; -++ NSArray *a; -++ int i; -++ -++ a = [self->urlToEntry allKeys]; -++ now = [NSDate date]; -++ -++ for (i = 0; i < [a count]; i++) -++ { -++ entry = [self->urlToEntry objectForKey: [a objectAtIndex: i]]; -++ -++ if ([now timeIntervalSinceDate: [entry creationTime]] > PoolScanInterval) -++ { -++ [[entry client] logout]; -++ [self->urlToEntry removeObjectForKey: [a objectAtIndex: i]]; -++ } -++ } -++ -+ [self debugWithFormat:@"should collect IMAP4 channels (%d active)", -+ [self->urlToEntry count]]; -+ } -+@@ -105,34 +126,42 @@ -+ NGImap4Connection *entry; -+ NGImap4Client *client; -+ -++ if (poolingOff) { -++ client = [self imap4ClientForURL:_url password:_p]; -++ entry = [[NGImap4Connection alloc] initWithClient:client -++ password:_p]; -++ return [entry autorelease]; -++ } -++ else { -+ /* check cache */ -+ -+- if ((entry = [self entryForURL:_url]) != nil) { -+- if ([entry isValidPassword:_p]) { -++ if ((entry = [self entryForURL:_url]) != nil) { -++ if ([entry isValidPassword:_p]) { -++ if (debugCache) -++ [self logWithFormat:@"valid password, reusing cache entry ..."]; -++ return entry; -++ } -++ -++ /* different password, password could have changed! */ -+ if (debugCache) -+- [self logWithFormat:@"valid password, reusing cache entry ..."]; -+- return entry; -++ [self logWithFormat:@"different password than cached entry: %@", _url]; -++ entry = nil; -+ } -+- -+- /* different password, password could have changed! */ -+- if (debugCache) -+- [self logWithFormat:@"different password than cached entry: %@", _url]; -+- entry = nil; -+- } -+- else -+- [self debugWithFormat:@"no connection cached yet for url: %@", _url]; -++ else -++ [self debugWithFormat:@"no connection cached yet for url: %@", _url]; -+ -+- /* try to login */ -++ /* try to login */ -+ -+- client = [entry isValidPassword:_p] -+- ? [entry client] -+- : [self imap4ClientForURL:_url password:_p]; -++ client = [entry isValidPassword:_p] -++ ? [entry client] -++ : [self imap4ClientForURL:_url password:_p]; -++ -++ if (client == nil) -++ return nil; -+ -+- if (client == nil) -+- return nil; -+- -+ /* sideeffect of -imap4ClientForURL:password: is to create a cache entry */ -+- return [self entryForURL:_url]; -++ return [self entryForURL:_url]; -++ } -+ } -+ -+ /* client object */ -+Index: sope-mime/NGImap4/NSString+Imap4.m -+=================================================================== -+--- sope-mime/NGImap4/NSString+Imap4.m (revision 1660) -++++ sope-mime/NGImap4/NSString+Imap4.m (working copy) -+@@ -20,117 +20,86 @@ -+ 02111-1307, USA. -+ */ -+ -++#import -++ -+ #include -+ #include "imCommon.h" -+ -+-/* TODO: NOT UNICODE SAFE (uses cString) */ -+- -+-static void _encodeToModifiedUTF7(unsigned char *_buf, int encLen, -+- unsigned char **result_, -+- unsigned int *cntRes_); -+-static int _decodeOfModifiedUTF7(unsigned char *_target, unsigned _targetLen, -+- unsigned *usedBytes_ , -+- unsigned char **buffer_, -+- int *bufLen_, int maxBuf); -+- -+ @implementation NSString(Imap4) -+ -++static unsigned int _encodeToModifiedUTF7(unichar *_char, unsigned char *result_, -++ unsigned int *cntRes_); -++static unsigned int _decodeOfModifiedUTF7(unsigned char *_source, unichar *result_, -++ unsigned int *cntRes_ ); -++ -+ - (NSString *)stringByEncodingImap4FolderName { -+- // TBD: this is restricted to Latin1, should be fixed to UTF-8 -+- /* dude.d& --> dude.d&- */ -+- unsigned char *buf = NULL; -++ unichar *buf = NULL; -+ unsigned char *res = NULL; -+ unsigned int len = 0; -+ unsigned int cnt = 0; -+ unsigned int cntRes = 0; -+ NSString *result = nil; -+- NSData *data; -+ -+- len = [self cStringLength]; -+- buf = calloc(len + 3, sizeof(char)); -+- res = calloc((len * 6) + 3, sizeof(char)); -+- buf[len] = '\0'; -+- res[len * 6] = '\0'; -+- [self getCString:(char *)buf]; -++ len = [self length]; -++ buf = NSZoneMalloc(NULL, (len + 1) * sizeof(unichar)); -++ [self getCharacters: buf]; -++ buf[len] = 0; -+ -++ /* 1 * '&', 3 for the max bytes / char, 1 * '-' */ -++ res = NSZoneMalloc(NULL, ((len * 5) + 1) * sizeof(char)); -++ -+ while (cnt < len) { -+- int c = buf[cnt]; -++ unichar c = buf[cnt]; -+ if (((c > 31) && (c < 38)) || -+ ((c > 38) && (c < 127))) { -+ res[cntRes++] = c; -+- cnt++; -+ } -+ else { -+ if (c == '&') { -+ res[cntRes++] = '&'; -+ res[cntRes++] = '-'; -+- cnt++; -+ } -+ else { -+- int start; -+- -+- start = cnt; -+- -+- while (cnt < (len - 1)) { -+- int c = buf[cnt + 1]; -+- if (((c > 31) && (c < 38)) || -+- ((c > 38) && (c < 127)) || -+- (c == '&')) { -+- break; -+- } -+- else { -+- cnt++; -+- } -+- } -+- { -+- unsigned length; -+- -+- res[cntRes++] = '&'; -+- -+- length = cnt - start + 1; -+- -+- _encodeToModifiedUTF7(buf + start, length, &res, &cntRes); -+- -+- res[cntRes] = '-'; -+- cntRes++; -+- cnt++; -+- } -++ res[cntRes++] = '&'; -++ cnt += _encodeToModifiedUTF7(buf + cnt, res + cntRes, &cntRes); -++ res[cntRes++] = '-'; -+ } -+ } -++ cnt++; -+ } -+- if (buf != NULL) free(buf); buf = NULL; -++ if (buf != NULL) NSZoneFree(NULL, buf); -+ -+- data = [[NSData alloc] initWithBytesNoCopy:res length:cntRes -+- freeWhenDone:YES]; -+- result = [[NSString alloc] initWithData:data -+- encoding:NSISOLatin1StringEncoding]; -+- [data release]; data = nil; -+- -+- return [result autorelease]; -++ res[cntRes] = 0; -++ result = [NSString stringWithCString: (char *) res -++ encoding: NSISOLatin1StringEncoding]; -++ -++ return result; -+ } -+ -+ - (NSString *)stringByDecodingImap4FolderName { -+- // TBD: this is restricted to Latin1, should be fixed to UTF-8 -+- /* dude/d&- --> dude/d& */ -+ unsigned char *buf; -+- unsigned char *res; -++ unichar *res; -+ unsigned int len; -+ unsigned int cnt = 0; -+ unsigned int cntRes = 0; -+ NSString *result = nil; -+- NSData *data; -++// NSData *data; -+ -+- if ((len = [self cStringLength]) == 0) -++ if ((len = [self lengthOfBytesUsingEncoding: NSISOLatin1StringEncoding]) == 0) -+ return @""; -+- -+- buf = calloc(len + 3, sizeof(unsigned char)); -+- res = calloc(len + 3, sizeof(unsigned char)); -++ -++ buf = NSZoneMalloc(NULL, (len + 1) * sizeof(unsigned char)); -++ -++ if ([self getCString:(char *)buf maxLength: len + 1 -++ encoding: NSISOLatin1StringEncoding] == NO) { -++ NSZoneFree(NULL, buf); -++ return @""; -++ } -+ buf[len] = '\0'; -+- res[len] = '\0'; -+- -+- [self getCString:(char *)buf]; -+- -+- while (cnt < (len - 1)) { /* &- */ -++ -++ res = NSZoneMalloc(NULL, (len + 1) * sizeof(unichar)); -++ -++ while (cnt < len) { /* &- */ -+ unsigned char c; -+ -+ c = buf[cnt]; -+@@ -141,29 +110,7 @@ -+ cnt += 2; -+ } -+ else { -+- unsigned usedBytes = 0; -+- unsigned char *buffer; -+- int maxBuf, bufLen; -+- -+- cnt++; -+- maxBuf = 511; -+- bufLen = 0; -+- buffer = calloc(maxBuf + 3, sizeof(char)); -+- -+- if (_decodeOfModifiedUTF7(buf + cnt, len - cnt, &usedBytes , &buffer, -+- &bufLen, maxBuf) == 0) { -+- int cnt1; -+- -+- cnt1 = 0; -+- while (cnt1 < bufLen) { -+- res[cntRes++] = buffer[cnt1++]; -+- } -+- cnt += usedBytes; -+- } -+- else { -+- NSCAssert(NO, @"couldn't decode UTF-7 .."); -+- } -+- free(buffer); buffer = NULL; -++ cnt += _decodeOfModifiedUTF7(buf + cnt + 1, res + cntRes, &cntRes) + 1; -+ } -+ } -+ else { -+@@ -171,20 +118,129 @@ -+ cnt++; -+ } -+ } -+- if (cnt < len) -+- res[cntRes++] = buf[cnt++]; -+- -+- if (buf != NULL) free(buf); buf = NULL; -+ -+- data = [[NSData alloc] initWithBytesNoCopy:res length:cntRes -+- freeWhenDone:YES]; -+- result = [[NSString alloc] initWithData:data -+- encoding:NSISOLatin1StringEncoding]; -+- [data release]; data = nil; -+- -+- return [result autorelease]; -++ if (buf != NULL) NSZoneFree(NULL, buf); -++ -++ res[cntRes] = 0; -++ result = [NSString stringWithCharacters: res length: cntRes]; -++ -++ return result; -+ } -+ -++/* check metamail output for correctness */ -++ -++static unsigned char basis_64[] = -++ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -++ -++static char index_64[128] = { -++ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -++ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -++ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, -++ 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1, -++ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, -++ 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, -++ -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, -++ 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 -++}; -++ -++#define char64(c) (((c) < 0 || (c) > 127) ? -1 : index_64[(c)]) -++ -++static unsigned int _encodeToModifiedUTF7(unichar *_char, unsigned char *result_, -++ unsigned int *cntRes_) -++{ -++ unsigned int processedSrc, processedDest, cycle; -++ unichar c; -++ char leftover; -++ -++ processedSrc = 0; -++ processedDest = 0; -++ cycle = 0; -++ leftover = 0; -++ -++ c = *_char; -++ while (c > 126 || (c > 0 && c < 32)) { -++ if (cycle == 0) { -++ *(result_ + processedDest) = basis_64[(c >> 10) & 0x3f]; -++ *(result_ + processedDest + 1) = basis_64[(c >> 4) & 0x3f]; -++ leftover = (c << 2); -++ processedDest += 2; -++ cycle = 1; -++ } -++ else if (cycle == 1) { -++ *(result_ + processedDest) = basis_64[(leftover | (c >> 14)) & 0x3f]; -++ *(result_ + processedDest + 1) = basis_64[(c >> 8) & 0x3f]; -++ *(result_ + processedDest + 2) = basis_64[(c >> 2) & 0x3f]; -++ leftover = (c << 4); -++ processedDest += 3; -++ cycle = 2; -++ } -++ else if (cycle == 2) { -++ *(result_ + processedDest) = basis_64[(leftover | (c >> 12)) & 0x3f]; -++ *(result_ + processedDest + 1) = basis_64[(c >> 6) & 0x3f]; -++ *(result_ + processedDest + 2) = basis_64[c & 0x3f]; -++ leftover = 0; -++ processedDest += 3; -++ cycle = 0; -++ } -++ processedSrc++; -++ c = *(_char + processedSrc); -++ } -++ if (leftover) { -++ *(result_ + processedDest) = basis_64[leftover & 0x3f]; -++ processedDest++; -++ } -++ processedSrc--; -++ *cntRes_ += processedDest; -++ -++ return processedSrc; -++} -++ -++static unsigned int _decodeOfModifiedUTF7(unsigned char *_source, unichar *result_, -++ unsigned int *cntRes_) -++{ -++ unsigned int processedSrc, processedDest; -++ unsigned char c, decoded; -++ unichar currentRes; -++ int shift; -++ -++ processedSrc = 0; -++ processedDest = 0; -++ shift = 10; -++ currentRes = 0; -++ -++ c = *_source; -++ while (c != 0 && c != '-') { -++ decoded = index_64[c]; -++ if (shift < 0) { -++ currentRes |= (decoded >> (shift * -1)); -++ *(result_ + processedDest) = currentRes; -++ processedDest++; -++ shift += 16; -++ currentRes = (decoded << shift); -++ } else { -++ currentRes |= (decoded << shift); -++ if (shift == 0) { -++ *(result_ + processedDest) = currentRes; -++ processedDest++; -++ currentRes = 0; -++ shift = 16; -++ } -++ } -++ shift -= 6; -++ processedSrc++; -++ c = *(_source + processedSrc); -++ } -++ if (shift != 10) { -++ *(result_ + processedDest) = currentRes; -++ } -++ if (c == '-') -++ processedSrc++; -++ -++ *cntRes_ += processedDest; -++ -++ return processedSrc; -++} -++ -+ - (NSString *)stringByEscapingImap4Password { -+ // TODO: perf -+ unichar *buffer; -+@@ -193,12 +249,12 @@ -+ NSString *s; -+ -+ len = [self length]; -+- chars = calloc(len + 2, sizeof(unichar)); -++ chars = NSZoneCalloc(NULL, len + 2, sizeof(unichar)); -+ [self getCharacters:chars]; -+- -+- buffer = calloc(len * 2 + 2, sizeof(unichar)); -++ -++ buffer = NSZoneCalloc(NULL, len * 2 + 2, sizeof(unichar)); -+ buffer[len * 2] = '\0'; -+- -++ -+ for (i = 0, j = 0; i < len; i++, j++) { -+ BOOL conv = NO; -+ -+@@ -224,209 +280,11 @@ -+ } -+ buffer[j] = chars[i]; -+ } -+- if (chars != NULL) free(chars); chars = NULL; -++ if (chars != NULL) NSZoneFree(NULL, chars); -+ -+ s = [NSString stringWithCharacters:buffer length:j]; -+- if (buffer != NULL) free(buffer); buffer = NULL; -++ -+ return s; -+ } -+ -+ @end /* NSString(Imap4) */ -+- -+-static void writeChunk(int _c1, int _c2, int _c3, int _pads, -+- unsigned char **result_, -+- unsigned int *cntRes_); -+- -+-static int getChar(int _cnt, int *cnt_, unsigned char *_buf) { -+- int result; -+- -+- if ((_cnt % 2)) { -+- result = _buf[*cnt_]; -+- (*cnt_)++; -+- } -+- else { -+- result = 0; -+- } -+- return result; -+-} -+-static void _encodeToModifiedUTF7(unsigned char *_buf, int encLen, -+- unsigned char **result_, unsigned int *cntRes_) -+-{ -+- int c1, c2, c3; -+- int cnt, cntAll; -+- -+- cnt = 0; -+- cntAll = 0; -+- -+- while (cnt < encLen) { -+- c1 = getChar(cntAll++, &cnt, _buf); -+- if (cnt == encLen) { -+- writeChunk(c1, 0, 0, 2, result_, cntRes_); -+- } -+- else { -+- c2 = getChar(cntAll++, &cnt, _buf); -+- if (cnt == encLen) { -+- writeChunk(c1, c2, 0, 1, result_, cntRes_); -+- } -+- else { -+- c3 = getChar(cntAll++, &cnt, _buf); -+- writeChunk(c1, c2, c3, 0, result_, cntRes_); -+- } -+- } -+- } -+-} -+- -+-/* check metamail output for correctness */ -+- -+-static unsigned char basis_64[] = -+- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -+- -+-static void writeChunk(int c1, int c2, int c3, int pads, unsigned char **result_, -+- unsigned int *cntRes_) { -+- unsigned char c; -+- -+- c = basis_64[c1>>2]; -+- (*result_)[*cntRes_] = c; -+- (*cntRes_)++; -+- -+- c = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)]; -+- -+- (*result_)[*cntRes_] = c; -+- (*cntRes_)++; -+- -+- -+- if (pads == 2) { -+- ; -+- } -+- else if (pads) { -+- c = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)]; -+- (*result_)[*cntRes_] = c; -+- (*cntRes_)++; -+- } -+- else { -+- c = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)]; -+- -+- (*result_)[*cntRes_] = c; -+- (*cntRes_)++; -+- -+- c = basis_64[c3 & 0x3F]; -+- (*result_)[*cntRes_] = c; -+- (*cntRes_)++; -+- } -+-} -+- -+-static char index_64[128] = { -+- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -+- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -+- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, -+- 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1, -+- -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, -+- 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, -+- -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, -+- 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 -+-}; -+- -+-#define char64(c) (((c) < 0 || (c) > 127) ? -1 : index_64[(c)]) -+- -+-static int _decodeOfModifiedUTF7(unsigned char *_target, unsigned _targetLen, -+- unsigned *usedBytes_ , unsigned char **buffer_, -+- int *bufLen_, int maxBuf) -+-{ -+- int c1, c2, c3, c4; -+- unsigned int cnt; -+- -+- for (cnt = 0; cnt < _targetLen; ) { -+- c1 = '='; -+- c2 = '='; -+- c3 = '='; -+- c4 = '='; -+- -+- c1 = _target[cnt++]; -+- -+- if (c1 == '-') { -+- (*usedBytes_)++; -+- return 0; -+- } -+- if (cnt < _targetLen) -+- c2 = _target[cnt++]; -+- -+- if (c2 == '-') { -+- (*usedBytes_)+=2; -+- return 0; -+- } -+- -+- (*usedBytes_) += 2; -+- -+- if (cnt < _targetLen) { -+- c3 = _target[cnt++]; -+- (*usedBytes_)++; -+- } -+- -+- if (cnt < _targetLen) { -+- c4 = _target[cnt++]; -+- if (c3 != '-') -+- (*usedBytes_)++; -+- } -+- -+- if (c2 == -1 || c3 == -1 || c4 == -1) { -+- fprintf(stderr, "Warning: base64 decoder saw premature EOF!\n"); -+- return 0; -+- } -+- -+- if (c1 == '=' || c2 == '=') { -+- continue; -+- } -+- -+- c1 = char64(c1); -+- c2 = char64(c2); -+- -+- if (*bufLen_ < maxBuf) { -+- unsigned char c; -+- -+- c = ((c1<<2) | ((c2&0x30)>>4)); -+- -+- if (c) { -+- (*buffer_)[*bufLen_] = c; -+- *bufLen_ = *bufLen_ + 1; -+- } -+- } -+- if (c3 == '-') { -+- return 0; -+- } -+- else if (c3 == '=') { -+- continue; -+- } else { -+- -+- c3 = char64(c3); -+- -+- if (*bufLen_ < maxBuf) { -+- unsigned char c; -+- c = (((c2&0XF) << 4) | ((c3&0x3C) >> 2)); -+- if (c) { -+- (*buffer_)[*bufLen_] = c; -+- *bufLen_ = *bufLen_ + 1; -+- } -+- } -+- -+- if (c4 == '-') { -+- return 0; -+- } -+- else if (c4 == '=') { -+- continue; -+- } else { -+- c4 = char64(c4); -+- -+- if (*bufLen_ < maxBuf) { -+- unsigned char c; -+- -+- c = (((c3&0x03) <<6) | c4); -+- if (c) { -+- (*buffer_)[*bufLen_] = c; -+- (*bufLen_) = (*bufLen_) + 1; -+- } -+- } -+- } -+- } -+- } -+- return 0; -+-} -+Index: sope-mime/NGImap4/NGImap4Functions.h -+=================================================================== -+--- sope-mime/NGImap4/NGImap4Functions.h (revision 1660) -++++ sope-mime/NGImap4/NGImap4Functions.h (working copy) -+@@ -58,4 +58,6 @@ -+ id_folder); -+ BOOL _createSubFolderWithName(id self, NSString *_name, BOOL _app); -+ -++NSString *SaneFolderName(NSString *folderName); -++ -+ #endif /* __NGMime_NGImap4_NGImap4Functions_H__ */ -+Index: sope-mime/NGMail/NGMailAddressParser.h -+=================================================================== -+--- sope-mime/NGMail/NGMailAddressParser.h (revision 1660) -++++ sope-mime/NGMail/NGMailAddressParser.h (working copy) -+@@ -24,7 +24,9 @@ -+ -+ #import -+ -+-@class NSData, NSString, NSArray; -++#import -++ -++@class NSData, NSArray; -+ @class NGMailAddressList; -+ -+ /* -+@@ -34,16 +36,16 @@ -+ @interface NGMailAddressParser : NSObject -+ { -+ @private -+- unsigned char *data; -+- int dataPos; -+- int errorPos; -+- int maxLength; -++ unichar *data; -++ int dataPos; -++ int errorPos; -++ int maxLength; -+ } -+ -+ + (id)mailAddressParserWithString:(NSString *)_string; -+ + (id)mailAddressParserWithData:(NSData *)_data; -+-+ (id)mailAddressParserWithCString:(char *)_cString; -+-- (id)initWithCString:(const unsigned char *)_cstr length:(int unsigned)_len; -+++ (id)mailAddressParserWithCString:(const char *)_cString; -++- (id)initWithString:(NSString *)_str; -+ -+ /* parsing */ -+ -+Index: sope-mime/NGMail/NGMimeMessageGenerator.m -+=================================================================== -+--- sope-mime/NGMail/NGMimeMessageGenerator.m (revision 1660) -++++ sope-mime/NGMail/NGMimeMessageGenerator.m (working copy) -+@@ -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/NGMail/NGMailAddressParser.m -+=================================================================== -+--- sope-mime/NGMail/NGMailAddressParser.m (revision 1660) -++++ sope-mime/NGMail/NGMailAddressParser.m (working copy) -+@@ -52,9 +52,9 @@ -+ StrClass = [NSString class]; -+ } -+ -+-static inline NSString *mkStrObj(const unsigned char *s, unsigned int l) { -++static inline NSString *mkStrObj(const unichar *s, unsigned int l) { -+ // TODO: unicode -+- return [(NSString *)[StrClass alloc] initWithCString:(char *)s length:l]; -++ return [(NSString *)[StrClass alloc] initWithCharacters:s length:l]; -+ } -+ -+ static inline id parseWhiteSpaces(NGMailAddressParser *self, BOOL _guessMode) { -+@@ -84,7 +84,7 @@ -+ int keepPos = self->dataPos; // keep reference for backtracking -+ id returnValue = nil; -+ BOOL isAtom = YES; -+- unsigned char text[self->maxLength + 2]; // token text -++ unichar text[self->maxLength + 2]; // token text -+ int length = 0; // token text length -+ BOOL done = NO; -+ -+@@ -94,7 +94,7 @@ -+ done = YES; -+ } -+ else { -+- register unsigned char c = self->data[self->dataPos]; -++ register unichar c = self->data[self->dataPos]; -+ -+ switch (c) { -+ case '(' : case ')': case '<': case '>': -+@@ -162,7 +162,7 @@ -+ int keepPos = self->dataPos; // keep reference for backtracking -+ id returnValue = nil; -+ BOOL isQText = YES; -+- unsigned char text[self->maxLength + 4]; // token text -++ unichar text[self->maxLength + 4]; // token text -+ int length = 0; // token text length -+ BOOL done = YES; -+ -+@@ -172,9 +172,9 @@ -+ done = YES; -+ } -+ else { -+- register char c = self->data[self->dataPos]; -++ register unichar c = self->data[self->dataPos]; -+ -+- switch ((int)c) { -++ switch (c) { -+ case '"' : -+ case '\\': -+ case 13 : -+@@ -215,7 +215,7 @@ -+ int keepPos = self->dataPos; // keep reference for backtracking -+ id returnValue = nil; -+ BOOL isDText = YES; -+- unsigned char text[self->maxLength]; // token text -++ unichar text[self->maxLength]; // token text -+ int length = 0; // token text length -+ BOOL done = YES; -+ -+@@ -225,9 +225,9 @@ -+ done = YES; -+ } -+ else { -+- register char c = self->data[self->dataPos]; -++ register unichar c = self->data[self->dataPos]; -+ -+- switch ((int)c) { -++ switch (c) { -+ case '[': case ']': -+ case '\\': case 13: -+ isDText = (length > 0); -+@@ -320,42 +320,47 @@ -+ /* constructors */ -+ -+ + (id)mailAddressParserWithData:(NSData *)_data { -+- return [[(NGMailAddressParser *)[self alloc] -+- initWithCString:[_data bytes] -+- length:[_data length]] autorelease]; -++ NSString *uniString; -++ -++ uniString = [NSString stringWithCharacters:(unichar *)[_data bytes] -++ length:([_data length] / sizeof(unichar))]; -++ -++ return [(NGMailAddressParser *)self mailAddressParserWithString:uniString]; -+ } -++ -+ + (id)mailAddressParserWithCString:(char *)_cString { -+- return [[(NGMailAddressParser *)[self alloc] -+- initWithCString:(unsigned char *)_cString -+- length:strlen(_cString)] autorelease]; -++ NSString *nsCString; -++ -++ nsCString = [NSString stringWithCString:_cString]; -++ -++ return [(NGMailAddressParser *)self mailAddressParserWithString:nsCString]; -+ } -+-- (id)initWithCString:(const unsigned char *)_cstr length:(int unsigned)_len { -++ -+++ (id)mailAddressParserWithString:(NSString *)_string { -++ return [[(NGMailAddressParser *)[self alloc] initWithString:_string] -++ autorelease]; -++} -++ -++- (id)initWithString:(NSString *)_str { -+ if ((self = [super init])) { -+ // TODO: remember some string encoding? -+- self->data = (unsigned char *)_cstr; -+- self->maxLength = _len; -++ self->maxLength = [_str length]; -++ self->data = malloc(self->maxLength*sizeof(unichar)); -++ [_str getCharacters:self->data]; -+ self->dataPos = 0; -+ self->errorPos = -1; -+ } -+ return self; -+ } -+ -+-- (id)initWithString:(NSString *)_str { -+- // TODO: unicode -+- return [self initWithCString:(unsigned char *)[_str cString] -+- length:[_str cStringLength]]; -+-} -+- -+ - (id)init { -+- return [self initWithCString:NULL length:0]; -++ return [self initWithString:nil]; -+ } -+ -+-+ (id)mailAddressParserWithString:(NSString *)_string { -+- return [[(NGMailAddressParser *)[self alloc] initWithString:_string] -+- autorelease]; -+-} -+- -+ - (void)dealloc { -++ if (self->data != NULL) { -++ free(self->data); -++ } -+ self->data = NULL; -+ self->maxLength = 0; -+ self->dataPos = 0; -+Index: sope-mime/NGMime/NGMimeRFC822DateHeaderFieldParser.m -+=================================================================== -+--- sope-mime/NGMime/NGMimeRFC822DateHeaderFieldParser.m (revision 1660) -++++ sope-mime/NGMime/NGMimeRFC822DateHeaderFieldParser.m (working copy) -+@@ -19,88 +19,45 @@ -+ 02111-1307, USA. -+ */ -+ -++#ifdef HAVE_STRNDUP -++#define _GNU_SOURCE 1 -++#endif -++ -++#include -++ -+ #include "NGMimeHeaderFieldParser.h" -+ #include "NGMimeHeaderFields.h" -+ #include "NGMimeUtilities.h" -+ #include "common.h" -+-#include -+ -++#ifndef HAVE_STRNDUP -++char *strndup(const char *str, size_t len) -++{ -++ char *dup = (char *)malloc(len+1); -++ if (dup) { -++ strncpy(dup,str,len); -++ dup[len]= '\0'; -++ } -++ return dup; -++} -++#endif -++ -+ @implementation NGMimeRFC822DateHeaderFieldParser -+ -+-static Class CalDateClass = Nil; -+-static NSTimeZone *gmt = nil; -+-static NSTimeZone *gmt01 = nil; -+-static NSTimeZone *gmt02 = nil; -+-static NSTimeZone *gmt03 = nil; -+-static NSTimeZone *gmt04 = nil; -+-static NSTimeZone *gmt05 = nil; -+-static NSTimeZone *gmt06 = nil; -+-static NSTimeZone *gmt07 = nil; -+-static NSTimeZone *gmt08 = nil; -+-static NSTimeZone *gmt09 = nil; -+-static NSTimeZone *gmt10 = nil; -+-static NSTimeZone *gmt11 = nil; -+-static NSTimeZone *gmt12 = nil; -+-static NSTimeZone *gmt0530 = nil; -+-static NSTimeZone *gmtM01 = nil; -+-static NSTimeZone *gmtM02 = nil; -+-static NSTimeZone *gmtM03 = nil; -+-static NSTimeZone *gmtM04 = nil; -+-static NSTimeZone *gmtM05 = nil; -+-static NSTimeZone *gmtM06 = nil; -+-static NSTimeZone *gmtM07 = nil; -+-static NSTimeZone *gmtM08 = nil; -+-static NSTimeZone *gmtM09 = nil; -+-static NSTimeZone *gmtM10 = nil; -+-static NSTimeZone *gmtM11 = nil; -+-static NSTimeZone *gmtM12 = nil; -+-static NSTimeZone *gmtM13 = nil; -+-static NSTimeZone *gmtM14 = nil; -+-static NSTimeZone *met = nil; -++static NSTimeZone *gmt = nil; -++static NSTimeZone *met = nil; -+ -+ + (int)version { -+ return 2; -+ } -++ -+ + (void)initialize { -+ static BOOL didInit = NO; -+- Class TzClass; -+ if (didInit) return; -+ didInit = YES; -+ -+- CalDateClass = [NSCalendarDate class]; -+- -+- /* timezones which were actually used in a maillist mailbox */ -+- TzClass = [NSTimeZone class]; -+- gmt = [[TzClass timeZoneWithName:@"GMT"] retain]; -+- met = [[TzClass timeZoneWithName:@"MET"] retain]; -+- gmt01 = [[TzClass timeZoneForSecondsFromGMT: 1 * (60 * 60)] retain]; -+- gmt02 = [[TzClass timeZoneForSecondsFromGMT: 2 * (60 * 60)] retain]; -+- gmt03 = [[TzClass timeZoneForSecondsFromGMT: 3 * (60 * 60)] retain]; -+- gmt04 = [[TzClass timeZoneForSecondsFromGMT: 4 * (60 * 60)] retain]; -+- gmt05 = [[TzClass timeZoneForSecondsFromGMT: 5 * (60 * 60)] retain]; -+- gmt06 = [[TzClass timeZoneForSecondsFromGMT: 6 * (60 * 60)] retain]; -+- gmt07 = [[TzClass timeZoneForSecondsFromGMT: 7 * (60 * 60)] retain]; -+- gmt08 = [[TzClass timeZoneForSecondsFromGMT: 8 * (60 * 60)] retain]; -+- gmt09 = [[TzClass timeZoneForSecondsFromGMT: 9 * (60 * 60)] retain]; -+- gmt10 = [[TzClass timeZoneForSecondsFromGMT: 10 * (60 * 60)] retain]; -+- gmt11 = [[TzClass timeZoneForSecondsFromGMT: 11 * (60 * 60)] retain]; -+- gmt12 = [[TzClass timeZoneForSecondsFromGMT: 12 * (60 * 60)] retain]; -+- gmtM01 = [[TzClass timeZoneForSecondsFromGMT: -1 * (60 * 60)] retain]; -+- gmtM02 = [[TzClass timeZoneForSecondsFromGMT: -2 * (60 * 60)] retain]; -+- gmtM03 = [[TzClass timeZoneForSecondsFromGMT: -3 * (60 * 60)] retain]; -+- gmtM04 = [[TzClass timeZoneForSecondsFromGMT: -4 * (60 * 60)] retain]; -+- gmtM05 = [[TzClass timeZoneForSecondsFromGMT: -5 * (60 * 60)] retain]; -+- gmtM06 = [[TzClass timeZoneForSecondsFromGMT: -6 * (60 * 60)] retain]; -+- gmtM07 = [[TzClass timeZoneForSecondsFromGMT: -7 * (60 * 60)] retain]; -+- gmtM08 = [[TzClass timeZoneForSecondsFromGMT: -8 * (60 * 60)] retain]; -+- gmtM09 = [[TzClass timeZoneForSecondsFromGMT: -9 * (60 * 60)] retain]; -+- gmtM10 = [[TzClass timeZoneForSecondsFromGMT:-10 * (60 * 60)] retain]; -+- gmtM11 = [[TzClass timeZoneForSecondsFromGMT:-11 * (60 * 60)] retain]; -+- gmtM12 = [[TzClass timeZoneForSecondsFromGMT:-12 * (60 * 60)] retain]; -+- gmtM13 = [[TzClass timeZoneForSecondsFromGMT:-13 * (60 * 60)] retain]; -+- gmtM14 = [[TzClass timeZoneForSecondsFromGMT:-14 * (60 * 60)] retain]; -+- -+- gmt0530 = [[TzClass timeZoneForSecondsFromGMT:5 * (60*60) + (30*60)] retain]; -++ gmt = [[NSTimeZone timeZoneWithName:@"GMT"] retain]; -++ met = [[NSTimeZone timeZoneWithName:@"MET"] retain]; -+ } -+ -+ /* -+@@ -111,7 +68,7 @@ -+ TODO: use an own parser for that. -+ */ -+ -+-static int parseMonthOfYear(unsigned char *s, unsigned int len) { -++static int parseMonthOfYear(char *s, unsigned int len) { -+ /* -+ This one is *extremely* forgiving, it only checks what is -+ necessary for the set below. This should work for both, English -+@@ -147,162 +104,110 @@ -+ } -+ } -+ -+-static NSTimeZone *parseTimeZone(unsigned char *s, unsigned int len) { -++static int offsetFromTZAbbreviation(const char **p) { -++ NSString *abbreviation; -++ NSTimeZone *offsetTZ; -++ unsigned int length; -++ -++ length = 0; -++ while (isalpha(*(*p+length))) -++ length++; -++ abbreviation = [[NSString alloc] initWithBytes: *p -++ length: length - 1 -++ encoding: NSISOLatin1StringEncoding]; -++ offsetTZ = [NSTimeZone timeZoneWithAbbreviation: abbreviation]; -++ [abbreviation release]; -++ *p += length; -++ -++ return [offsetTZ secondsFromGMT]; -++} -++ -++static inline char *digitsString(const char *string) { -++ const char *p; -++ unsigned int len; -++ -++ p = string; -++ while (!isdigit(*p)) -++ p++; -++ len = 0; -++ while (isdigit(*(p + len))) -++ len++; -++ -++ return strndup(p, len); -++} -++ -++static NSTimeZone *parseTimeZone(const char *s, unsigned int len) { -+ /* -+ WARNING: failed to parse RFC822 timezone: '+0530' \ -+ (value='Tue, 13 Jul 2004 21:39:28 +0530') -+ TODO: this is because libFoundation doesn't accept 'GMT+0530' as input. -+ */ -+- char *p = (char *)s; -++ char *newString, *digits; -++ const char *p; -+ NSTimeZone *tz; -+- NSString *ts; -+- -+- if (len == 0) -+- return nil; -+- -+- if (*s == '+' || *s == '-') { -+- if (len == 3) { -+- if (p[1] == '0' && p[2] == '0') // '+00' or '-00' -+- return gmt; -+- if (*s == '+') { -+- if (p[1] == '0' && p[2] == '1') // '+01' -+- return gmt01; -+- if (p[1] == '0' && p[2] == '2') // '+02' -+- return gmt02; -+- } -+- } -+- else if (len == 5) { -+- if (p[3] == '0' && p[4] == '0' && p[1] == '0') { // '?0x00' -+- if (p[2] == '0') // '+0000' -+- return gmt; -+- -+- if (*s == '+') { -+- if (p[2] == '1') return gmt01; // '+0100' -+- if (p[2] == '2') return gmt02; // '+0200' -+- if (p[2] == '3') return gmt03; // '+0300' -+- if (p[2] == '4') return gmt04; // '+0400' -+- if (p[2] == '5') return gmt05; // '+0500' -+- if (p[2] == '6') return gmt06; // '+0600' -+- if (p[2] == '7') return gmt07; // '+0700' -+- if (p[2] == '8') return gmt08; // '+0800' -+- if (p[2] == '9') return gmt09; // '+0900' -+- } -+- else if (*s == '-') { -+- if (p[2] == '1') return gmtM01; // '-0100' -+- if (p[2] == '2') return gmtM02; // '-0200' -+- if (p[2] == '3') return gmtM03; // '-0300' -+- if (p[2] == '4') return gmtM04; // '-0400' -+- if (p[2] == '5') return gmtM05; // '-0500' -+- if (p[2] == '6') return gmtM06; // '-0600' -+- if (p[2] == '7') return gmtM07; // '-0700' -+- if (p[2] == '8') return gmtM08; // '-0800' -+- if (p[2] == '9') return gmtM09; // '-0900' -+- } -+- } -+- else if (p[3] == '0' && p[4] == '0' && p[1] == '1') { // "?1x00" -+- if (*s == '+') { -+- if (p[2] == '0') return gmt10; // '+1000' -+- if (p[2] == '1') return gmt11; // '+1100' -+- if (p[2] == '2') return gmt12; // '+1200' -+- } -+- else if (*s == '-') { -+- if (p[2] == '0') return gmtM10; // '-1000' -+- if (p[2] == '1') return gmtM11; // '-1100' -+- if (p[2] == '2') return gmtM12; // '-1200' -+- if (p[2] == '3') return gmtM13; // '-1300' -+- if (p[2] == '4') return gmtM14; // '-1400' -+- } -+- } -+- -+- /* special case for GMT+0530 */ -+- if (strncmp((char *)s, "+0530", 5) == 0) -+- return gmt0530; -+- } -+- else if (len == 7) { -+- /* -+- "MultiMail" submits timezones like this: -+- "Tue, 9 Mar 2004 9:43:00 -05-500", -+- don't know what the "-500" trailer is supposed to mean? Apparently -+- Thunderbird just uses the "-05", so do we. -+- */ -+- -+- if (isdigit(p[1]) && isdigit(p[2]) && (p[3] == '-'||p[3] == '+')) { -+- unsigned char tmp[8]; -+- -+- strncpy((char *)tmp, p, 3); -+- tmp[3] = '0'; -+- tmp[4] = '0'; -+- tmp[5] = '\0'; -+- return parseTimeZone(tmp, 5); -+- } -+- } -++ unsigned int hours, minutes, seconds, remaining; -++ int sign; -++ -++ sign = 1; -++ hours = 0; -++ minutes = 0; -++ seconds = 0; -++ -++ newString = strndup(s, len); -++ p = newString; -++ -++ if (isalpha(*p)) -++ seconds = offsetFromTZAbbreviation(&p); -++ while (isspace(*p)) -++ p++; -++ while (*p == '+' || *p == '-') { -++ if (*p == '-') -++ sign = -sign; -++ p++; -+ } -+- else if (*s == '0') { -+- if (len == 2) { // '00' -+- if (p[1] == '0') return gmt; -+- if (p[1] == '1') return gmt01; -+- if (p[1] == '2') return gmt02; -+- } -+- else if (len == 4) { -+- if (p[2] == '0' && p[3] == '0') { // '0x00' -+- if (p[1] == '0') return gmt; -+- if (p[1] == '1') return gmt01; -+- if (p[1] == '2') return gmt02; -+- } -+- } -++ digits = digitsString(p); -++ p = digits; -++ remaining = strlen(p); -++ switch(remaining) { -++ case 6: /* hhmmss */ -++ seconds += (10 * (*(p + remaining - 2) - 48) -++ + *(p + remaining - 1) - 48); -++ case 4: /* hhmm */ -++ hours += 10 * (*p - 48); -++ p++; -++ case 3: /* hmm */ -++ hours += (*p - 48); -++ p++; -++ minutes += 10 * (*p - 48) + *(p + 1) - 48; -++ break; -++ case 2: /* hh */ -++ hours += 10 * (*p - 48) + *(p + 1) - 48; -++ break; -++ default: -++ NSLog (@"parseTimeZone: cannot parse time notation '%s'", newString); -+ } -+- else if (len == 3) { -+- if (strcasecmp((char *)s, "GMT") == 0) return gmt; -+- if (strcasecmp((char *)s, "UTC") == 0) return gmt; -+- if (strcasecmp((char *)s, "MET") == 0) return met; -+- if (strcasecmp((char *)s, "CET") == 0) return met; -+- } -+- -+- if (isalpha(*s)) { -+- ts = [[NSString alloc] initWithCString:(char *)s length:len]; -+- } -+- else { -+- char buf[len + 5]; -+- -+- buf[0] = 'G'; buf[1] = 'M'; buf[2] = 'T'; -+- if (*s == '+' || *s == '-') { -+- strcpy(&(buf[3]), (char *)s); -+- } -+- else { -+- buf[3] = '+'; -+- strcpy(&(buf[4]), (char *)s); -+- } -+- ts = [[NSString alloc] initWithCString:buf]; -+- } -+-#if 1 -+- NSLog(@"%s: RFC822 TZ Parser: expensive: '%@'", __PRETTY_FUNCTION__, ts); -+-#endif -+- tz = [NSTimeZone timeZoneWithAbbreviation:ts]; -+- [ts release]; -++ free(digits); -++ -++ seconds += sign * (3600 * hours + 60 * minutes); -++ tz = [NSTimeZone timeZoneForSecondsFromGMT: seconds]; -++ free(newString); -++ -+ return tz; -+ } -+ -+ - (id)parseValue:(id)_data ofHeaderField:(NSString *)_field { -+ // TODO: use UNICODE -+ NSCalendarDate *date = nil; -+- unsigned char buf[256]; -+- unsigned char *bytes = buf, *pe; -++ 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 lengthOfBytesUsingEncoding: NSUTF8StringEncoding]; -++ bytes = [_data cStringUsingEncoding: NSUTF8StringEncoding]; -++ -+ /* remove leading chars (skip to first digit, the day of the month) */ -+ while (length > 0 && (!isdigit(*bytes))) { -+ bytes++; -+@@ -312,7 +217,7 @@ -+ if (length == 0) { -+ NSLog(@"WARNING(%s): empty value for header field %@ ..", -+ __PRETTY_FUNCTION__, _field); -+- return [CalDateClass date]; -++ return [NSCalendarDate date]; -+ } -+ -+ // TODO: should be a category on NSCalendarDate -+@@ -435,7 +340,8 @@ -+ for (pe = bytes; isalnum(*pe) || *pe == '-' || *pe == '+'; pe++) -+ ; -+ *pe = '\0'; -+- if ((tz = parseTimeZone(bytes, (pe - bytes))) == nil) { -++ if (pe == bytes -++ || (tz = parseTimeZone((const char *) bytes, (pe - bytes))) == nil) { -+ [self logWithFormat: -+ @"WARNING: failed to parse RFC822 timezone: '%s' (value='%@')", -+ bytes, _data]; -+@@ -444,9 +350,9 @@ -+ -+ /* construct and return */ -+ finished: -+- date = [CalDateClass dateWithYear:year month:monthOfYear day:dayOfMonth -+- hour:hour minute:minute second:second -+- timeZone:tz]; -++ date = [NSCalendarDate dateWithYear:year month:monthOfYear day:dayOfMonth -++ hour:hour minute:minute second:second -++ timeZone:tz]; -+ if (date == nil) goto failed; -+ -+ #if 0 -+Index: sope-mime/NGMime/NGMimeMultipartBodyParser.m -+=================================================================== -+--- sope-mime/NGMime/NGMimeMultipartBodyParser.m (revision 1660) -++++ sope-mime/NGMime/NGMimeMultipartBodyParser.m (working copy) -+@@ -428,6 +428,7 @@ -+ NSString *boundary = nil; -+ NSArray *rawBodyParts = nil; -+ BOOL foundError = NO; -++ NSData *boundaryBytes; -+ -+ contentType = [_part contentType]; -+ boundary = [contentType valueOfParameter:@"boundary"]; -+@@ -437,9 +438,10 @@ -+ -+ *(&foundError) = NO; -+ -++ boundaryBytes = [boundary dataUsingEncoding:NSISOLatin1StringEncoding]; -+ *(&rawBodyParts) = [self _parseBody:_body part:_part data:_data -+- boundary:[boundary cString] -+- length:[boundary cStringLength] -++ boundary:[boundaryBytes bytes] -++ length:[boundary length] -+ delegate:_d]; -+ -+ if (rawBodyParts) { -+Index: sope-mime/NGMime/NGMimeHeaderFieldGeneratorSet.m -+=================================================================== -+--- sope-mime/NGMime/NGMimeHeaderFieldGeneratorSet.m (revision 1660) -++++ sope-mime/NGMime/NGMimeHeaderFieldGeneratorSet.m (working copy) -+@@ -77,6 +77,7 @@ -+ [rfc822Set setGenerator:gen forField:@"bcc"]; -+ [rfc822Set setGenerator:gen forField:Fields->from]; -+ [rfc822Set setGenerator:gen forField:@"reply-to"]; -++ [rfc822Set setGenerator:gen forField:@"in-reply-to"]; -+ [rfc822Set setGenerator:gen forField:@"Disposition-Notification-To"]; -+ } -+ -+Index: sope-mime/NGMime/NGMimeType.m -+=================================================================== -+--- sope-mime/NGMime/NGMimeType.m (revision 1660) -++++ sope-mime/NGMime/NGMimeType.m (working copy) -+@@ -120,30 +120,30 @@ -+ -+ /* some unsupported, but known encoding */ -+ else if ([charset isEqualToString:@"ks_c_5601-1987"]) { -+- encoding = [NSString defaultCStringEncoding]; -++ encoding = NSISOLatin1StringEncoding; -+ foundUnsupported = YES; -+ } -+ else if ([charset isEqualToString:@"euc-kr"]) { -+- encoding = [NSString defaultCStringEncoding]; -+- foundUnsupported = YES; -++ encoding = NSKoreanEUCStringEncoding; -+ } -+ else if ([charset isEqualToString:@"big5"]) { -+- encoding = [NSString defaultCStringEncoding]; -+- foundUnsupported = YES; -++ encoding = NSBIG5StringEncoding; -+ } -+ else if ([charset isEqualToString:@"iso-2022-jp"]) { -+- encoding = [NSString defaultCStringEncoding]; -+- foundUnsupported = YES; -++ encoding = NSISO2022JPStringEncoding; -+ } -+ else if ([charset isEqualToString:@"gb2312"]) { -+- encoding = [NSString defaultCStringEncoding]; -+- foundUnsupported = YES; -++ encoding = NSGB2312StringEncoding; -+ } -+ else if ([charset isEqualToString:@"koi8-r"]) { -+- encoding = [NSString defaultCStringEncoding]; -+- foundUnsupported = YES; -++ encoding = NSKOI8RStringEncoding; -+ } -+- -++ else if ([charset isEqualToString:@"windows-1250"]) { -++ encoding = NSWindowsCP1250StringEncoding; -++ } -++ else if ([charset isEqualToString:@"windows-1251"]) { -++ encoding = NSWindowsCP1251StringEncoding; -++ } -+ else if ([charset isEqualToString:@"windows-1252"]) { -+ encoding = NSWindowsCP1252StringEncoding; -+ } -+@@ -152,7 +152,7 @@ -+ } -+ else if ([charset isEqualToString:@"x-unknown"] || -+ [charset isEqualToString:@"unknown"]) { -+- encoding = NSASCIIStringEncoding; -++ encoding = NSISOLatin1StringEncoding; -+ } -+ /* ISO Latin 9 */ -+ #if !(NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY) -+@@ -166,7 +166,7 @@ -+ else { -+ [self logWithFormat:@"%s: unknown charset '%@'", -+ __PRETTY_FUNCTION__, _s]; -+- encoding = [NSString defaultCStringEncoding]; -++ encoding = NSISOLatin1StringEncoding; -+ } -+ return encoding; -+ } -+@@ -385,23 +385,26 @@ -+ } -+ -+ - (BOOL)valueNeedsQuotes:(NSString *)_parameterValue { -+- unsigned len = [_parameterValue cStringLength]; -+- char buf[len + 15]; -+- char *cstr; -++ NSData *stringData; -++ const char *cstr; -++ unsigned int count, max; -++ BOOL needsQuote; -+ -+- cstr = &(buf[0]); -++ needsQuote = NO; -+ -+- [_parameterValue getCString:cstr]; cstr[len] = '\0'; -+- while (*cstr) { -+- if (isMime_SpecialByte(*cstr)) -+- return YES; -++ stringData = [_parameterValue dataUsingEncoding:NSUTF8StringEncoding]; -++ cstr = [stringData bytes]; -++ max = [stringData length]; -++ count = 0; -++ while (!needsQuote && count < max) { -++ if (isMime_SpecialByte(*(cstr + count)) -++ || *(cstr + count) == 32) -++ needsQuote = YES; -++ else -++ count++; -++ } -+ -+- if (*cstr == 32) -+- return YES; -+- -+- cstr++; -+- } -+- return NO; -++ return needsQuote; -+ } -+ -+ - (NSString *)stringValue { -+Index: sope-mime/NGMime/NGMimeBodyPart.m -+=================================================================== -+--- sope-mime/NGMime/NGMimeBodyPart.m (revision 1660) -++++ sope-mime/NGMime/NGMimeBodyPart.m (working copy) -+@@ -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/ChangeLog -+=================================================================== -+--- sope-mime/NGMime/ChangeLog (revision 1660) -++++ sope-mime/NGMime/ChangeLog (working copy) -+@@ -1,3 +1,25 @@ -++2008-09-08 Wolfgang Sourdeau -++ -++ * NGMimeRFC822DateHeaderFieldParser.m ([NGMimeRFC -++ -parseValue:ofHeaderField:]): don't parse timezone with a length -++ of 0. -++ -++2008-09-01 Wolfgang Sourdeau -++ -++ * NGMimeRFC822DateHeaderFieldParser.m ([NGMimeRFC -++ -parseValue:ofHeaderField:]): use an 8-bit safe encoding when -++ parsing dates. Since we only consider 7-bits characters, we ensure -++ that bad user-agents can be handled more properly. -++ -++ * NGMimeType.m ([NGMimeType +stringEncodingForCharset:]): -++ x-unknown encoding is now translated to an 8-bit safe encoding -++ (NSISOLatin1StringEncoding). -++ -++ * NGMimeAddressHeaderFieldGenerator.m -++ ([NGMimeAddressHeaderFieldGenerator -++ -generateDataForHeaderFieldNamed:value:]): encode resulting string -++ in an 8-bit safe encoding (NSISOLatin1StringEncoding). -++ -+ 2008-01-29 Albrecht Dress -+ -+ * fixes for OGo bug #789 (reply-to QP encoding) -+Index: sope-mime/NGMime/NGMimeContentTypeHeaderFieldGenerator.m -+=================================================================== -+--- sope-mime/NGMime/NGMimeContentTypeHeaderFieldGenerator.m (revision 1660) -++++ sope-mime/NGMime/NGMimeContentTypeHeaderFieldGenerator.m (working copy) -+@@ -36,8 +36,7 @@ -+ NGMimeType *type = nil; // only one content-type field -+ NSString *tmp = nil; -+ NSMutableData *data = nil; -+- unsigned char *ctmp = NULL; -+- unsigned len = 0; -++ NSData *valueData; -+ -+ type = _value; -+ -+@@ -59,21 +58,15 @@ -+ -+ tmp = [type type]; -+ NSAssert(tmp, @"type should not be nil"); -+- len = [tmp length]; -+- ctmp = malloc(len + 4); -+- [tmp getCString:(char *)ctmp]; ctmp[len] = '\0'; -+- [data appendBytes:ctmp length:len]; -+- free(ctmp); -++ valueData = [tmp dataUsingEncoding: NSISOLatin1StringEncoding]; -++ [data appendData: valueData]; -++ -++ [data appendBytes:"/" length:1]; -+ -+- [data appendBytes:"//" length:1]; -+- -+ tmp = [type subType]; -+ if (tmp != nil) { -+- len = [tmp length]; -+- ctmp = malloc(len + 4); -+- [tmp getCString:(char *)ctmp]; ctmp[len] = '\0'; -+- [data appendBytes:ctmp length:len]; -+- free(ctmp); -++ valueData = [tmp dataUsingEncoding: NSISOLatin1StringEncoding]; -++ [data appendData:valueData]; -+ } -+ else -+ [data appendBytes:"*" length:1]; -+@@ -91,12 +84,9 @@ -+ continue; -+ } -+ [data appendBytes:"; " length:2]; -+- -+- len = [name cStringLength]; -+- ctmp = malloc(len + 1); -+- [name getCString:(char *)ctmp]; ctmp[len] = '\0'; -+- [data appendBytes:ctmp length:len]; -+- free(ctmp); -++ -++ valueData = [name dataUsingEncoding: NSUTF8StringEncoding]; -++ [data appendData: valueData]; -+ -+ /* -+ this confuses GroupWise: "= \"" (a space) -+@@ -105,66 +95,30 @@ -+ -+ /* check for encoding */ -+ { -+- unsigned cnt; -++ unsigned cnt, max; -++ const char *dataBytes; -+ BOOL doEnc; -+ -+- len = [value cStringLength]; -+- ctmp = malloc(len + 4); -+- [value getCString:(char *)ctmp]; ctmp[len] = '\0'; -+- cnt = 0; -++ valueData = [value dataUsingEncoding:NSUTF8StringEncoding]; -++ dataBytes = [valueData bytes]; -++ max = [valueData length]; -++ -+ doEnc = NO; -+- while (cnt < len) { -+- if ((unsigned char)ctmp[cnt] > 127) { -++ cnt = 0; -++ while (!doEnc && cnt < max) { -++ if ((unsigned char)dataBytes[cnt] > 127) -+ doEnc = YES; -+- break; -+- } -+- cnt++; -++ else -++ cnt++; -+ } -+ if (doEnc) { -+- unsigned char iso[] = "=?iso-8859-15?q?"; -+- unsigned isoLen = 16; -+- unsigned char isoEnd[] = "?="; -+- unsigned isoEndLen = 2; -+- unsigned desLen; -+- unsigned char *des; -+- -+- if (ctmp) free(ctmp); -+- { -+- NSData *data; -+- -+-#if APPLE_Foundation_LIBRARY || NeXT_Foundation_LIBRARY -+- data = [value dataUsingEncoding:NSISOLatin1StringEncoding]; -+-#else -+- data = [value dataUsingEncoding:NSISOLatin9StringEncoding]; -+-#endif -+- -+- len = [data length]; -+- ctmp = malloc(len + 10); -+- [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(ctmp + cnt, len - cnt, -+- 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 { -+- NSLog(@"WARNING: An error occour during quoted-printable decoding"); -+- } -+- if (des) free(des); -++ [data appendBytes:"=?utf-8?q?" length:10]; -++ [data appendData: [valueData dataByEncodingQuotedPrintable]]; -++ [data appendBytes:"?=" length:2]; -+ } -+ else { -+- [data appendBytes:ctmp length:len]; -++ [data appendData: valueData]; -+ } -+- free(ctmp); -+ } -+ [data appendBytes:"\"" length:1]; -+ } -+Index: sope-mime/NGMime/NGMimePartGenerator.m -+=================================================================== -+--- sope-mime/NGMime/NGMimePartGenerator.m (revision 1660) -++++ sope-mime/NGMime/NGMimePartGenerator.m (working copy) -+@@ -155,8 +155,9 @@ -+ BOOL isMultiValue, isFirst; -+ -+ /* get field name and strip leading spaces */ -+- fcname = (const unsigned char *)[_field cString]; -+- for (len = [_field cStringLength]; len > 0; fcname++, len--) { -++ fcname = (const unsigned char *)[_field cStringUsingEncoding:NSISOLatin1StringEncoding]; -++ for (len = [_field lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding]; -++ len > 0; fcname++, len--) { -+ if (*fcname != ' ') -+ break; -+ } -+@@ -328,7 +329,7 @@ -+ if ([body isKindOfClass:[NSData class]]) -+ data = body; -+ else if ([body isKindOfClass:[NSString class]]) -+- data = [body dataUsingEncoding:[NSString defaultCStringEncoding]]; -++ data = [body dataUsingEncoding: NSISOLatin1StringEncoding]; -+ else -+ data = nil; -+ -+Index: sope-mime/NGMime/NGMimeBodyParser.m -+=================================================================== -+--- sope-mime/NGMime/NGMimeBodyParser.m (revision 1660) -++++ sope-mime/NGMime/NGMimeBodyParser.m (working copy) -+@@ -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]]; -+ -+@@ -88,10 +91,20 @@ -+ NSStringEncoding encoding; -+ -+ encoding = [NGMimeType stringEncodingForCharset:charset]; -+- -++ -++ // If we nave no encoding here, let's not simply return nil. -++ // We SHOULD try at least UTF-8 and after, Latin1. -++ if (!encoding) -++ encoding = NSUTF8StringEncoding; -++ -+ body = [[[NSString alloc] -+- initWithData:_data -++ initWithData:_data -+ encoding:encoding] autorelease]; -++ -++ if (!body) -++ body = [[[NSString alloc] initWithData:_data -++ encoding:NSISOLatin1StringEncoding] -++ autorelease]; -+ } -+ return body; -+ } -+Index: sope-mime/NGMime/NGMimePartParser.h -+=================================================================== -+--- sope-mime/NGMime/NGMimePartParser.h (revision 1660) -++++ sope-mime/NGMime/NGMimePartParser.h (working copy) -+@@ -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 (revision 1660) -++++ sope-mime/NGMime/NGMimePartParser.m (working copy) -+@@ -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/NGMimeAddressHeaderFieldGenerator.m -+=================================================================== -+--- sope-mime/NGMime/NGMimeAddressHeaderFieldGenerator.m (revision 1660) -++++ sope-mime/NGMime/NGMimeAddressHeaderFieldGenerator.m (working copy) -+@@ -105,10 +105,10 @@ -+ } -+ -+ tmp = [obj displayName]; -+- bufLen = [tmp cStringLength]; -++ bufLen = [tmp lengthOfBytesUsingEncoding: NSUTF8StringEncoding]; -+ -+- buffer = calloc(bufLen + 10, sizeof(char)); -+- [tmp getCString:buffer]; -++ buffer = calloc(bufLen, sizeof(char)); -++ [tmp getCString: buffer maxLength: bufLen encoding: NSUTF8StringEncoding]; -+ -+ cnt = 0; -+ doEnc = NO; -+@@ -117,11 +117,11 @@ -+ /* must encode chars outside ASCII 33..60, 62..126 ranges [RFC 2045, Sect. 6.7] -+ * RFC 2047, Sect. 4.2 also requires chars 63 and 95 to be encoded -+ * For spaces, quotation is fine */ -+- if ((unsigned char)buffer[cnt] < 32 || -+- (unsigned char)buffer[cnt] == 61 || -+- (unsigned char)buffer[cnt] == 63 || -+- (unsigned char)buffer[cnt] == 95 || -+- (unsigned char)buffer[cnt] > 126) { -++ if ((unichar)buffer[cnt] < 32 || -++ (unichar)buffer[cnt] == 61 || -++ (unichar)buffer[cnt] == 63 || -++ (unichar)buffer[cnt] == 95 || -++ (unichar)buffer[cnt] > 126) { -+ doEnc = YES; -+ break; -+ } -+@@ -130,8 +130,13 @@ -+ -+ if (doEnc) { -+ /* FIXME - better use UTF8 encoding! */ -++#if NeXT_Foundation_LIBRARY -+ unsigned char iso[] = "=?iso-8859-15?q?"; -+ unsigned isoLen = 16; -++#else -++ unsigned char iso[] = "=?utf-8?q?"; -++ unsigned isoLen = 10; -++#endif -+ unsigned char isoEnd[] = "?="; -+ unsigned isoEndLen = 2; -+ unsigned desLen; -+@@ -141,10 +146,10 @@ -+ { -+ NSData *data; -+ -+-#if APPLE_Foundation_LIBRARY || NeXT_Foundation_LIBRARY -++#if NeXT_Foundation_LIBRARY -+ data = [tmp dataUsingEncoding:NSISOLatin1StringEncoding]; -+ #else -+- data = [tmp dataUsingEncoding:NSISOLatin9StringEncoding]; -++ data = [tmp dataUsingEncoding:NSUTF8StringEncoding]; -+ #endif -+ -+ bufLen = [data length]; -+@@ -162,8 +167,9 @@ -+ des + isoLen, desLen - isoLen); -+ if ((int)desLen != -1) { -+ memcpy(des + isoLen + desLen, isoEnd, isoEndLen); -+- tmp = [NSString stringWithCString:(char *)des -+- length:(isoLen + desLen + isoEndLen)]; -++ tmp = [[NSString alloc] initWithData: [NSData dataWithBytes:(char *)des length:(isoLen + desLen + isoEndLen)] -++ encoding: NSISOLatin1StringEncoding]; -++ [tmp autorelease]; -+ } -+ else { -+ [self warnWithFormat: -+@@ -190,11 +196,7 @@ -+ } -+ } -+ -+-#if APPLE_Foundation_LIBRARY || NeXT_Foundation_LIBRARY -+ data = [result dataUsingEncoding:NSISOLatin1StringEncoding]; -+-#else -+- data = [result dataUsingEncoding:NSISOLatin9StringEncoding]; -+-#endif -+ [result release]; -+ -+ return data; -+Index: sope-mime/NGMime/NGMimeContentDispositionHeaderFieldGenerator.m -+=================================================================== -+--- sope-mime/NGMime/NGMimeContentDispositionHeaderFieldGenerator.m (revision 1660) -++++ sope-mime/NGMime/NGMimeContentDispositionHeaderFieldGenerator.m (working copy) -+@@ -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/NGExtensions/NSString+Ext.h -+=================================================================== -+--- sope-core/NGExtensions/NGExtensions/NSString+Ext.h (revision 1660) -++++ sope-core/NGExtensions/NGExtensions/NSString+Ext.h (working copy) -+@@ -30,6 +30,7 @@ -+ -+ @interface NSString(GSAdditions) -+ -++#if !GNUSTEP -+ - (NSString *)stringWithoutPrefix:(NSString *)_prefix; -+ - (NSString *)stringWithoutSuffix:(NSString *)_suffix; -+ -+@@ -39,6 +40,7 @@ -+ - (NSString *)stringByTrimmingLeadSpaces; -+ - (NSString *)stringByTrimmingTailSpaces; -+ - (NSString *)stringByTrimmingSpaces; -++#endif /* !GNUSTEP */ -+ -+ /* the following are not available in gstep-base 1.6 ? */ -+ - (NSString *)stringByTrimmingLeadWhiteSpaces; -+@@ -47,6 +49,8 @@ -+ -+ @end /* NSString(GSAdditions) */ -+ -++#if !GNUSTEP -++ -+ @interface NSMutableString(GNUstepCompatibility) -+ -+ - (void)trimLeadSpaces; -+@@ -55,6 +59,8 @@ -+ -+ @end /* NSMutableString(GNUstepCompatibility) */ -+ -++#endif /* !GNUSTEP */ -++ -+ #endif -+ -+ /* specific to libFoundation */ -+Index: sope-core/NGExtensions/FdExt.subproj/NSString+Ext.m -+=================================================================== -+--- sope-core/NGExtensions/FdExt.subproj/NSString+Ext.m (revision 1660) -++++ sope-core/NGExtensions/FdExt.subproj/NSString+Ext.m (working copy) -+@@ -39,18 +39,6 @@ -+ : (NSString *)[[self copy] autorelease]; -+ } -+ -+-- (NSString *)stringByReplacingString:(NSString *)_orignal -+- withString:(NSString *)_replacement -+-{ -+- /* very slow solution .. */ -+- -+- if ([self rangeOfString:_orignal].length == 0) -+- return [[self copy] autorelease]; -+- -+- return [[self componentsSeparatedByString:_orignal] -+- componentsJoinedByString:_replacement]; -+-} -+- -+ - (NSString *)stringByTrimmingLeadWhiteSpaces -+ { -+ // should check 'whitespaceAndNewlineCharacterSet' .. -+@@ -96,6 +84,25 @@ -+ return [[self copy] autorelease]; -+ } -+ -++- (NSString *)stringByTrimmingWhiteSpaces -++{ -++ return [[self stringByTrimmingTailWhiteSpaces] -++ stringByTrimmingLeadWhiteSpaces]; -++} -++ -++#ifndef GNUSTEP -++- (NSString *)stringByReplacingString:(NSString *)_orignal -++ withString:(NSString *)_replacement -++{ -++ /* very slow solution .. */ -++ -++ if ([self rangeOfString:_orignal].length == 0) -++ return [[self copy] autorelease]; -++ -++ return [[self componentsSeparatedByString:_orignal] -++ componentsJoinedByString:_replacement]; -++} -++ -+ - (NSString *)stringByTrimmingLeadSpaces -+ { -+ unsigned len; -+@@ -117,6 +124,7 @@ -+ else -+ return [[self copy] autorelease]; -+ } -++ -+ - (NSString *)stringByTrimmingTailSpaces -+ { -+ unsigned len; -+@@ -139,19 +147,17 @@ -+ return [[self copy] autorelease]; -+ } -+ -+-- (NSString *)stringByTrimmingWhiteSpaces -+-{ -+- return [[self stringByTrimmingTailWhiteSpaces] -+- stringByTrimmingLeadWhiteSpaces]; -+-} -+ - (NSString *)stringByTrimmingSpaces -+ { -+ return [[self stringByTrimmingTailSpaces] -+ stringByTrimmingLeadSpaces]; -+ } -++#endif -+ -+ @end /* NSString(GSAdditions) */ -+ -++#if !GNUSTEP -++ -+ @implementation NSMutableString(GNUstepCompatibility) -+ -+ - (void)trimLeadSpaces -+@@ -169,6 +175,8 @@ -+ -+ @end /* NSMutableString(GNUstepCompatibility) */ -+ -++#endif /* !GNUSTEP */ -++ -+ @implementation NSString(lfNSURLUtilities) -+ -+ - (BOOL)isAbsoluteURL -+Index: sope-core/NGExtensions/FdExt.subproj/NSString+Encoding.m -+=================================================================== -+--- sope-core/NGExtensions/FdExt.subproj/NSString+Encoding.m (revision 1660) -++++ sope-core/NGExtensions/FdExt.subproj/NSString+Encoding.m (working copy) -+@@ -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-core/NGExtensions/EOExt.subproj/EOGlobalID+Ext.m -+=================================================================== -+--- sope-core/NGExtensions/EOExt.subproj/EOGlobalID+Ext.m (revision 1660) -++++ sope-core/NGExtensions/EOExt.subproj/EOGlobalID+Ext.m (working copy) -+@@ -19,6 +19,7 @@ -+ 02111-1307, USA. -+ */ -+ -++#import -+ #import -+ #import -+ -+Index: sope-core/NGStreams/GNUmakefile.preamble -+=================================================================== -+--- sope-core/NGStreams/GNUmakefile.preamble (revision 1660) -++++ sope-core/NGStreams/GNUmakefile.preamble (working copy) -+@@ -1,6 +1,7 @@ -+ # compilation settings -+ -+-MACHCPU = $(shell echo $$MACHTYPE | cut -f 1 -d '-') -++# MACHCPU = $(shell echo $$MACHTYPE | cut -f 1 -d '-') -++MACHCPU = $(shell uname -m) -+ -+ libNGStreams_INCLUDE_DIRS += \ -+ -I$(GNUSTEP_TARGET_CPU)/$(GNUSTEP_TARGET_OS) \ -+Index: sope-xml/libxmlSAXDriver/libxmlHTMLSAXDriver.h -+=================================================================== -+--- sope-xml/libxmlSAXDriver/libxmlHTMLSAXDriver.h (revision 1660) -++++ sope-xml/libxmlSAXDriver/libxmlHTMLSAXDriver.h (working copy) -+@@ -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 (revision 1660) -++++ sope-xml/libxmlSAXDriver/libxmlHTMLSAXDriver.m (working copy) -+@@ -200,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; -+ } -+ -+Index: sope-appserver/mod_ngobjweb/GNUmakefile -+=================================================================== -+--- sope-appserver/mod_ngobjweb/GNUmakefile (revision 1660) -++++ sope-appserver/mod_ngobjweb/GNUmakefile (working copy) -+@@ -82,7 +82,7 @@ -+ -+ CFLAGS = -Wall -I. -fPIC \ -+ $(APXS_CFLAGS) $(APR_CFLAGS) \ -+- $(APXS_INCLUDE_DIRS) $(APR_INCLUDE_DIRS) -++ $(APXS_INCLUDE_DIRS) $(APR_INCLUDE_DIRS) -O0 -ggdb -+ -+ LDFLAGS = $(APXS_LDFLAGS) $(APR_LDFLAGS) -shared -fPIC -+ LDLIBS = $(APXS_LIBS) $(APR_LIBS) -+@@ -111,8 +111,7 @@ -+ apache-dir : -+ $(MKDIRS) $(GNUSTEP_INSTALLATION_DIR) -+ -+-install :: apache-dir all -+- $(INSTALL_PROGRAM) $(product) $(GNUSTEP_INSTALLATION_DIR) -++install :: -+ -+ install-usr-libexec :: all -+ $(INSTALL_PROGRAM) $(product) /usr/libexec/httpd/ -+Index: sope-appserver/NGObjWeb/GNUmakefile.postamble -+=================================================================== -+--- sope-appserver/NGObjWeb/GNUmakefile.postamble (revision 1660) -++++ sope-appserver/NGObjWeb/GNUmakefile.postamble (working copy) -+@@ -23,14 +23,20 @@ -+ -+ # install makefiles -+ -+-after-install :: -+- $(MKDIRS) $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/Additional/ -+- $(INSTALL_DATA) ngobjweb.make $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/Additional/ngobjweb.make -++after-install :: $(DESTDIR)/$(GNUSTEP_MAKEFILES)/Additional/ngobjweb.make -+ -+ ifneq ($(GNUSTEP_MAKE_VERSION),1.3.0) -+-after-install :: -++after-install :: $(DESTDIR)/$(GNUSTEP_MAKEFILES)/woapp.make $(DESTDIR)/$(GNUSTEP_MAKEFILES)/wobundle.make -++endif -++ -++$(DESTDIR)/$(GNUSTEP_MAKEFILES)/Additional/ngobjweb.make: ngobjweb.make -++ $(MKDIRS) $(DESTDIR)/$(GNUSTEP_MAKEFILES)/Additional/ -++ $(INSTALL_DATA) ngobjweb.make $(DESTDIR)/$(GNUSTEP_MAKEFILES)/Additional/ngobjweb.make -++ -++$(DESTDIR)/$(GNUSTEP_MAKEFILES)/woapp.make: woapp-gs.make -+ $(INSTALL_DATA) woapp-gs.make \ -+- $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/woapp.make -++ $(DESTDIR)/$(GNUSTEP_MAKEFILES)/woapp.make -++ -++$(DESTDIR)/$(GNUSTEP_MAKEFILES)/wobundle.make: wobundle-gs.make -+ $(INSTALL_DATA) wobundle-gs.make \ -+- $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/wobundle.make -+-endif -++ $(DESTDIR)/$(GNUSTEP_MAKEFILES)/wobundle.make -+Index: sope-appserver/NGObjWeb/WOMessage+XML.m -+=================================================================== -+--- sope-appserver/NGObjWeb/WOMessage+XML.m (revision 1660) -++++ sope-appserver/NGObjWeb/WOMessage+XML.m (working copy) -+@@ -84,7 +84,7 @@ -+ id builder; -+ -+ builder = [[[NSClassFromString(@"DOMSaxBuilder") alloc] init] autorelease]; -+- dom = [[builder buildFromData:data] retain]; -++ dom = [builder buildFromData:data]; -+ } -+ -+ /* cache DOM structure */ -+Index: sope-appserver/NGObjWeb/ChangeLog -+=================================================================== -+--- sope-appserver/NGObjWeb/ChangeLog (revision 1660) -++++ sope-appserver/NGObjWeb/ChangeLog (working copy) -+@@ -1,3 +1,33 @@ -++2009-10-26 Wolfgang Sourdeau -++ -++ * WOMessage+XML.m (-contentAsDOMDocument): do not retain "dom" as -++ it will be assigned to self->domCache, to avoid a leak. -++ -++2009-10-21 Wolfgang Sourdeau -++ -++ * WebDAV/SoObjectResultEntry.m (-valueForKey:): we now take -++ WOUseRelativeURLs into account when the "href" key is requested, -++ to work around a bug in iCal 4. -++ -++2009-07-02 Wolfgang Sourdeau -++ -++ * WOMessage.m (-setHeaders:, -setHeader:forKey:, headerForKey:, -++ -appendHeader:forKey:, -appendHeaders:forKey:, setHeaders:forKey:, -++ -headersForKey:): convert the specified header key to lowercase, -++ to ensure they are managed case-insensitively. -++ * WOHttpAdaptor/WOHttpTransaction.m -++ (-deliverResponse:toRequest:onStream:): if the content-type is -++ specified and already has "text/plain" as prefix, we don't -++ override it. -++ -++2009-07-01 Wolfgang Sourdeau -++ -++ * WOHttpAdaptor/WOHttpTransaction.m -++ (-deliverResponse:toRequest:onStream:): we test the content-length -++ and impose a content-type of text/plain when 0. This work-arounds -++ a bug in Mozilla clients where empty responses with a content-type -++ set to X/xml will trigger an exception. -++ -+ 2009-06-10 Helge Hess -+ -+ * DAVPropMap.plist: mapped {DAV:}current-user-principal (v4.9.37) -+Index: sope-appserver/NGObjWeb/DAVPropMap.plist -+=================================================================== -+--- sope-appserver/NGObjWeb/DAVPropMap.plist (revision 1660) -++++ sope-appserver/NGObjWeb/DAVPropMap.plist (working copy) -+@@ -157,6 +157,7 @@ -+ "{urn:ietf:params:xml:ns:caldav}supported-calendar-data" = -+ davSupportedCalendarDataTypes; -+ "{urn:ietf:params:xml:ns:caldav}calendar-description" = davDescription; -++ "{urn:ietf:params:xml:ns:caldav}calendar-timezone" = davCalendarTimeZone; -+ -+ /* CardDAV */ -+ "{urn:ietf:params:xml:ns:carddav}addressbook-home-set" = davAddressbookHomeSet; -+Index: sope-appserver/NGObjWeb/WebDAV/SoObjectResultEntry.m -+=================================================================== -+--- sope-appserver/NGObjWeb/WebDAV/SoObjectResultEntry.m (revision 1660) -++++ sope-appserver/NGObjWeb/WebDAV/SoObjectResultEntry.m (working copy) -+@@ -25,7 +25,14 @@ -+ @implementation SoObjectResultEntry -+ -+ static BOOL debugOn = NO; -++static BOOL useRelativeURLs = NO; -+ -+++ (void) initialize -++{ -++ useRelativeURLs = [[NSUserDefaults standardUserDefaults] -++ boolForKey: @"WOUseRelativeURLs"]; -++} -++ -+ - (id)initWithURI:(NSString *)_href object:(id)_o values:(NSDictionary *)_d { -+ if ((self = [super init])) { -+ if (debugOn) { -+@@ -85,10 +92,36 @@ -+ return YES; -+ } -+ -++- (NSString *)_relativeHREF { -++ NSString *newHREF; -++ NSRange hostRange; -++ -++ if ([self->href hasPrefix: @"/"]) -++ return self->href; -++ else { -++ hostRange = [self->href rangeOfString: @"://"]; -++ if (hostRange.length > 0) { -++ newHREF = [self->href substringFromIndex: NSMaxRange (hostRange)]; -++ hostRange = [newHREF rangeOfString: @"/"]; -++ if (hostRange.length > 0) { -++ newHREF = [newHREF substringFromIndex: hostRange.location]; -++ } -++ } else { -++ newHREF = self->href; -++ } -++ -++ return newHREF; -++ } -++} -++ -+ - (id)valueForKey:(NSString *)_key { -+- if ([_key isEqualToString:@"{DAV:}href"]) -+- return self->href; -+- -++ if ([_key isEqualToString:@"{DAV:}href"]) { -++ if (useRelativeURLs) -++ return [self _relativeHREF]; -++ else -++ return self->href; -++ } -++ -+ if ([_key isEqualToString:@"{DAV:}status"]) -+ return nil; -+ -+Index: sope-appserver/NGObjWeb/WebDAV/SoWebDAVRenderer.m -+=================================================================== -+--- sope-appserver/NGObjWeb/WebDAV/SoWebDAVRenderer.m (revision 1660) -++++ sope-appserver/NGObjWeb/WebDAV/SoWebDAVRenderer.m (working copy) -+@@ -49,6 +49,8 @@ -+ #define XMLNS_INTTASK \ -+ @"{http://schemas.microsoft.com/mapi/id/{00062003-0000-0000-C000-000000000046}/}" -+ -++static Class NSURLKlass = Nil; -++ -+ @interface SoWebDAVRenderer(Privates) -+ - (BOOL)renderStatusResult:(id)_object withDefaultStatus:(int)_defStatus -+ inContext:(WOContext *)_ctx; -+@@ -79,6 +81,8 @@ -+ -+ if ((debugOn = [ud boolForKey:@"SoRendererDebugEnabled"])) -+ NSLog(@"enabled debugging in SoWebDAVRenderer (SoRendererDebugEnabled)"); -++ -++ NSURLKlass = [NSURL class]; -+ } -+ -+ + (id)sharedRenderer { -+@@ -616,16 +620,19 @@ -+ [r appendContentString:s]; -+ } -+ else { -++ s = [self stringForValue:value ofProperty:_key prefixes:nsToPrefix]; -+ [r appendContentCharacter:'<']; -+ [r appendContentString:extName]; -+- [r appendContentCharacter:'>']; -+- -+- s = [self stringForValue:value ofProperty:_key prefixes:nsToPrefix]; -+- [r appendContentString:s]; -+- -+- [r appendContentString:@""]; -++ if ([s length] > 0) { -++ [r appendContentCharacter:'>']; -++ [r appendContentString:s]; -++ [r appendContentString:@""]; -++ } -++ else { -++ [r appendContentString:@"/>"]; -++ } -+ if (formatOutput) [r appendContentCharacter:'\n']; -+ } -+ } -+@@ -694,8 +701,13 @@ -+ } -+ -+ /* tidy href */ -+- href = [self tidyHref:href baseURL:baseURL]; -+- -++ if (useRelativeURLs) { -++ if ([href isKindOfClass: NSURLKlass]) -++ href = [href path]; -++ } -++ else -++ href = [self tidyHref:href baseURL:baseURL]; -++ -+ /* tidy status */ -+ stat = [self tidyStatus:stat]; -+ } -+Index: sope-appserver/NGObjWeb/WODirectAction.m -+=================================================================== -+--- sope-appserver/NGObjWeb/WODirectAction.m (revision 1660) -++++ sope-appserver/NGObjWeb/WODirectAction.m (working copy) -+@@ -46,7 +46,7 @@ -+ } -+ - (id)initWithContext:(WOContext *)_ctx { -+ if ((self = [self initWithRequest:[_ctx request]])) { -+- self->context = [_ctx retain]; -++ self->context = _ctx; -+ } -+ return self; -+ } -+@@ -54,16 +54,16 @@ -+ return [self initWithRequest:nil]; -+ } -+ -+-- (void)dealloc { -+- [self->context release]; -+- [super dealloc]; -+-} -++// - (void)dealloc { -++// [self->context release]; -++// [super dealloc]; -++// } -+ -+ /* accessors */ -+ -+ - (WOContext *)context { -+ if (self->context == nil) -+- self->context = [[[WOApplication application] context] retain]; -++ self->context = [[WOApplication application] context]; -+ return self->context; -+ } -+ -+Index: sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.m -+=================================================================== -+--- sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.m (revision 1660) -++++ sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.m (working copy) -+@@ -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 (revision 1660) -++++ sope-appserver/NGObjWeb/DynamicElements/_WOComplexHyperlink.m (working copy) -+@@ -41,6 +41,7 @@ -+ WOAssociation *string; -+ WOAssociation *target; -+ WOAssociation *disabled; -++ WOAssociation *isAbsolute; -+ WOElement *template; -+ -+ /* new in WO4: */ -+@@ -360,6 +361,7 @@ -+ { -+ if ((self = [super initWithName:_name hyperlinkInfo:_info template:_t])) { -+ self->href = _info->href; -++ self->isAbsolute = _info->isAbsolute; -+ } -+ return self; -+ } -+@@ -375,8 +377,11 @@ -+ // TODO: we need a binding to disable rewriting! -+ NSRange r; -+ -++ if ([[self->isAbsolute valueInContext:_ctx] boolValue] == YES) -++ return NO; -++ -+ r.length = [_s length]; -+- -++ -+ /* do not rewrite pure fragment URLs */ -+ if (r.length > 0 && [_s characterAtIndex:0] == '#') -+ return NO; -+Index: sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.h -+=================================================================== -+--- sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.h (revision 1660) -++++ sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.h (working copy) -+@@ -41,7 +41,8 @@ -+ WOAssociation *pageName; -+ WOAssociation *actionClass; -+ WOAssociation *directActionName; -+- -++ WOAssociation *isAbsolute; -++ -+ BOOL sidInUrl; -+ -+ /* 'ivar' associations */ -+Index: sope-appserver/NGObjWeb/WOMessage.m -+=================================================================== -+--- sope-appserver/NGObjWeb/WOMessage.m (revision 1660) -++++ sope-appserver/NGObjWeb/WOMessage.m (working copy) -+@@ -182,7 +182,7 @@ -+ NSString *key; -+ -+ keys = [_headers keyEnumerator]; -+- while ((key = [keys nextObject])) { -++ while ((key = [[keys nextObject] lowercaseString])) { -+ id value; -+ -+ value = [_headers objectForKey:key]; -+@@ -198,34 +198,39 @@ -+ } -+ -+ - (void)setHeader:(NSString *)_header forKey:(NSString *)_key { -+- [self->header setObject:[_header stringValue] forKey:_key]; -++ [self->header setObject:[_header stringValue] -++ forKey:[_key lowercaseString]]; -+ } -+ - (NSString *)headerForKey:(NSString *)_key { -+- return [[self->header objectEnumeratorForKey:_key] nextObject]; -++ return [[self->header objectEnumeratorForKey:[_key lowercaseString]] -++ nextObject]; -+ } -+ -+ - (void)appendHeader:(NSString *)_header forKey:(NSString *)_key { -+- [self->header addObject:_header forKey:_key]; -++ [self->header addObject:_header forKey:[_key lowercaseString]]; -+ } -+ - (void)appendHeaders:(NSArray *)_headers forKey:(NSString *)_key { -+- [self->header addObjects:_headers forKey:_key]; -++ [self->header addObjects:_headers forKey:[_key lowercaseString]]; -+ } -+ -+ - (void)setHeaders:(NSArray *)_headers forKey:(NSString *)_key { -+ NSEnumerator *e; -+ id value; -++ NSString *lowerKey; -+ -++ lowerKey = [_key lowercaseString]; -+ e = [_headers objectEnumerator]; -+ -+- [self->header removeAllObjectsForKey:_key]; -++ [self->header removeAllObjectsForKey:lowerKey]; -+ -+ while ((value = [e nextObject])) -+- [self->header addObject:value forKey:_key]; -++ [self->header addObject:value forKey:lowerKey]; -+ } -+ - (NSArray *)headersForKey:(NSString *)_key { -+ NSEnumerator *values; -+ -+- if ((values = [self->header objectEnumeratorForKey:_key])) { -++ if ((values -++ = [self->header objectEnumeratorForKey:[_key lowercaseString]])) { -+ NSMutableArray *array = nil; -+ id value = nil; -+ -+@@ -243,17 +248,14 @@ -+ NSEnumerator *values; -+ -+ if ((values = [self->header keyEnumerator])) { -+- NSMutableArray *array = nil; -++ NSMutableArray *array; -+ id name = nil; -+- array = [[NSMutableArray alloc] init]; -+- -++ array = [NSMutableArray array]; -++ -+ while ((name = [values nextObject])) -+ [array addObject:name]; -+ -+- name = [array copy]; -+- [array release]; -+- -+- return [name autorelease]; -++ return array; -+ } -+ return nil; -+ } -+Index: sope-appserver/NGObjWeb/SoObjects/SoObject.m -+=================================================================== -+--- sope-appserver/NGObjWeb/SoObjects/SoObject.m (revision 1660) -++++ sope-appserver/NGObjWeb/SoObjects/SoObject.m (working copy) -+@@ -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 (revision 1660) -++++ sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpTransaction.m (working copy) -+@@ -696,7 +696,7 @@ -+ *(&out) = nil; -+ -+ [self _httpValidateResponse:_response]; -+- -++ -+ out = [(NGCTextStream *)[NGCTextStream alloc] initWithSource:_out]; -+ -+ NS_DURING { -+@@ -705,6 +705,7 @@ -+ id body; -+ BOOL doZip; -+ BOOL isok = YES; -++ int length; -+ -+ doZip = [_response shouldZipResponseToRequest:_request]; -+ -+@@ -738,7 +739,11 @@ -+ -+ /* add content length header */ -+ -+- snprintf((char *)buf, sizeof(buf), "%d", [body length]); -++ if ((length = [body length]) == 0 -++ && ![[_response headerForKey: @"content-type"] hasPrefix:@"text/plain"]) { -++ [_response setHeader:@"text/plain" forKey:@"content-type"]; -++ } -++ snprintf((char *)buf, sizeof(buf), "%d", length); -+ t1 = [[NSString alloc] initWithCString:(char *)buf]; -+ [_response setHeader:t1 forKey:@"content-length"]; -+ [t1 release]; t1 = nil; -+@@ -766,7 +771,7 @@ -+ NSString *value; -+ -+ if (!hasConnectionHeader) { -+- if ([fieldName caseInsensitiveCompare:@"connection"]==NSOrderedSame) -++ if ([fieldName isEqualToString:@"connection"]) -+ hasConnectionHeader = YES; -+ } -+ ---- sope-4.9.r1660.orig/debian/patches/debian-build-mysql.diff -+++ sope-4.9.r1660/debian/patches/debian-build-mysql.diff +--- sope-4.9.r1664.orig/debian/patches/debian-build-mysql.diff ++++ sope-4.9.r1664/debian/patches/debian-build-mysql.diff @@ -0,0 +1,13 @@ +diff -durpN sope-4.9.r1660.orig/configure sope-4.9.r1660/configure +--- configure.orig 2009-10-28 09:46:53.000000000 -0400