diff --git a/Documentation/SOGoInstallationGuide.asciidoc b/Documentation/SOGoInstallationGuide.asciidoc index 24d79d0cc..bd7ecab75 100644 --- a/Documentation/SOGoInstallationGuide.asciidoc +++ b/Documentation/SOGoInstallationGuide.asciidoc @@ -1832,6 +1832,9 @@ Supported formats are: `smtp://domain:port`, `smtps://domain`, `tls=YES` will enforce using STARTTLS smtp connections. Thus, `smtp://localhost:587/?tls=YES` would use the default MUA port on localhost with STARTTLS enforced. +To disable TLS verification for localhost domains, passing +`tlsVerifyMode=allowInsecureLocalhost` will such connections: +`smtp://localhost:587/?tls=YES&tlsVerifyMode=allowInsecureLocalhost`. |D |SOGoSMTPAuthenticationType |Activate SMTP authentication and specifies which type is in use. @@ -1919,6 +1922,7 @@ URL with a fully qualified domain name, such as: [options="compact"] * `imaps://mail.acme.com:993` * `imap://mail.acme.com:143/?tls=YES` +* `imap://127.0.0.1:143/?tls=YES&tlsVerifyMode=allowInsecureLocalhost` |D |SOGoSieveServer |Parameter used to set the DNS name or IP address of the Sieve @@ -1933,6 +1937,7 @@ qualified domain name, such as: [options="compact"] * `sieve://mail.acme.com:4190/?tls=YES` +* `sieve://127.0.0.14190/?tls=YES&tlsVerifyMode=allowInsecureLocalhost` Note that TLS is supported but SSL is not. diff --git a/SoObjects/Mailer/SOGoMailAccount.h b/SoObjects/Mailer/SOGoMailAccount.h index bc250cbde..5bc43d8a9 100644 --- a/SoObjects/Mailer/SOGoMailAccount.h +++ b/SoObjects/Mailer/SOGoMailAccount.h @@ -28,7 +28,7 @@ SOGoMailAccount Parent object: SOGoMailAccounts Child objects: SOGoMailFolder - + The SOGoMailAccount represents a single IMAP4 mail account (host, login, password, etc) */ @@ -91,6 +91,7 @@ typedef enum { - (NSDictionary *) identityForEmail: (NSString *) email; - (NSString *) signature; - (NSString *) encryption; +- (NSString *) tlsVerifyMode; /* folder pathes */ - (NSArray *) toManyRelationshipKeysWithNamespaces: (BOOL) withNSs; diff --git a/SoObjects/Mailer/SOGoMailAccount.m b/SoObjects/Mailer/SOGoMailAccount.m index c955db2fa..2f6f6b544 100644 --- a/SoObjects/Mailer/SOGoMailAccount.m +++ b/SoObjects/Mailer/SOGoMailAccount.m @@ -92,7 +92,7 @@ static NSString *inboxFolderName = @"INBOX"; [otherUsersFolderName release]; [sharedFoldersName release]; [subscribedFolders release]; - [super dealloc]; + [super dealloc]; } - (BOOL) isInDraftsFolder @@ -135,7 +135,7 @@ static NSString *inboxFolderName = @"INBOX"; if (namespace) { [self _appendNamespace: namespace toFolders: folders]; - ASSIGN(otherUsersFolderName, [folders lastObject]); + ASSIGN(otherUsersFolderName, [folders lastObject]); } namespace = [namespaceDict objectForKey: @"shared"]; @@ -275,7 +275,7 @@ static NSString *inboxFolderName = @"INBOX"; SOGoDomainDefaults *dd; id inboxQuota, infos; float quota; - + inboxQuota = nil; if ([self supportsQuotas]) { @@ -744,6 +744,17 @@ static NSString *inboxFolderName = @"INBOX"; return encryption; } +- (NSString *) tlsVerifyMode +{ + NSString *verifyMode; + + verifyMode = [[self _mailAccount] objectForKey: @"tlsVerifyMode"]; + if (!verifyMode || ![verifyMode length]) + verifyMode = @"default"; + + return verifyMode; +} + - (NSMutableString *) imap4URLString { NSMutableString *imap4URLString; @@ -829,7 +840,7 @@ static NSString *inboxFolderName = @"INBOX"; NSEnumerator *e; NSString *guid; id currentFolder; - + BOOL hasAnnotatemore; ud = [[context activeUser] userDefaults]; @@ -854,7 +865,7 @@ static NSString *inboxFolderName = @"INBOX"; result = [client annotation: @"*" entryName: @"/comment" attributeName: @"value.priv"]; else result = [client lstatus: @"*" flags: [NSArray arrayWithObjects: @"x-guid", nil]]; - + e = [folderList objectEnumerator]; while ((currentFolder = [[e nextObject] substringFromIndex: 1])) @@ -863,7 +874,7 @@ static NSString *inboxFolderName = @"INBOX"; guid = [[[[result objectForKey: @"FolderList"] objectForKey: currentFolder] objectForKey: @"/comment"] objectForKey: @"value.priv"]; else guid = [[[result objectForKey: @"status"] objectForKey: currentFolder] objectForKey: @"x-guid"]; - + if (!guid || ![guid isNotNull]) { // Don't generate a GUID for "Other users" and "Shared" namespace folders - user foldername instead @@ -881,7 +892,7 @@ static NSString *inboxFolderName = @"INBOX"; guid = [NSString stringWithFormat: @"%@", currentFolder]; else { - // If folder doesn't exists - ignore it. + // If folder doesn't exists - ignore it. nresult = [client status: currentFolder flags: [NSArray arrayWithObject: @"UIDVALIDITY"]]; if (![[nresult valueForKey: @"result"] boolValue]) @@ -897,10 +908,10 @@ static NSString *inboxFolderName = @"INBOX"; guid = [NSString stringWithFormat: @"%@", currentFolder]; } } - + [folders setObject: [NSString stringWithFormat: @"folder%@", guid] forKey: [NSString stringWithFormat: @"folder%@", currentFolder]]; } - + return folders; } @@ -964,7 +975,7 @@ static NSString *inboxFolderName = @"INBOX"; } else obj = [super lookupName: _key inContext: _ctx acquire: NO]; - + /* return 404 to stop acquisition */ if (!obj) obj = [NSException exceptionWithHTTPStatus: 404 /* Not Found */]; @@ -1224,7 +1235,7 @@ static NSString *inboxFolderName = @"INBOX"; if (delegateUser) [delegateUser removeMailDelegator: owner]; } - + [self _setDelegates: delegates]; } } diff --git a/SoObjects/Mailer/SOGoMailBaseObject.m b/SoObjects/Mailer/SOGoMailBaseObject.m index 8ef975e87..f02e70acd 100644 --- a/SoObjects/Mailer/SOGoMailBaseObject.m +++ b/SoObjects/Mailer/SOGoMailBaseObject.m @@ -93,7 +93,7 @@ container]; folder = nil; } - + return folder; } @@ -211,7 +211,7 @@ // use the container's one. // login = [[[self context] activeUser] login]; - + if (!login) login = [[[[self container] context] activeUser] login]; @@ -265,7 +265,7 @@ [self warnWithFormat:@"container does not implement -imap4URL!"]; url = nil; } - + return url; } @@ -285,6 +285,7 @@ { SOGoMailAccount *account; NSString *urlString; + NSString *useTls = @"NO"; /* this could probably be handled better from NSURL but it's buggy in GNUstep */ @@ -292,10 +293,11 @@ { account = [self mailAccountFolder]; if ([[account encryption] isEqualToString: @"tls"]) - urlString = [NSString stringWithFormat: @"%@?tls=YES", - [self imap4URLString]]; - else - urlString = [self imap4URLString]; + { + useTls = @"YES"; + } + urlString = [NSString stringWithFormat: @"%@?tls=%@&tlsVerifyMode=%@", + [self imap4URLString], useTls, [account tlsVerifyMode]]; imap4URL = [[NSURL alloc] initWithString: urlString]; } diff --git a/SoObjects/SOGo/SOGoUser.m b/SoObjects/SOGo/SOGoUser.m index 9c77a612f..d9b13d2c4 100644 --- a/SoObjects/SOGo/SOGoUser.m +++ b/SoObjects/SOGo/SOGoUser.m @@ -23,6 +23,7 @@ #import #import #import +#import #import #import @@ -136,7 +137,7 @@ _defaults = nil; _settings = nil; - + uid = nil; realUID = nil; domain = nil; @@ -166,7 +167,7 @@ domain = nil; } } - + newLogin = [newLogin stringByReplacingString: @"%40" withString: @"@"]; if (b) @@ -312,7 +313,7 @@ return allEmails; } -// +// // We always return the last object among our list of email addresses. This value // is always added in SOGoUserManager: -_fillContactMailRecords: // @@ -396,7 +397,7 @@ format = [ud timeFormat]; if (format) [dateFormatter setTimeFormat: format]; - + return dateFormatter; } @@ -475,7 +476,7 @@ - (SOGoUserSettings *) userSettings { - if (!_settings) + if (!_settings) { _settings = [SOGoUserSettings settingsForUser: login]; [_settings retain]; @@ -643,8 +644,9 @@ - (void) _appendSystemMailAccountWithDelegatedIdentities: (BOOL) appendDeletegatedIdentities { NSString *fullName, *imapLogin, *imapServer, *cImapServer, - *encryption, *scheme, *action, *query, *customEmail, *sieveServer; + *encryption, *scheme, *action, *queryTls, *customEmail, *sieveServer, *tlsVerifyMode; NSMutableDictionary *mailAccount, *identity, *mailboxes, *receipts, *security, *mailSettings; + NSDictionary *queryComponents; NSNumber *port; NSMutableArray *identities, *mails; NSArray *delegators, *delegates; @@ -692,36 +694,43 @@ // 3. port & encryption scheme = [cUrl scheme] ? [cUrl scheme] : [url scheme]; - query = [cUrl query] ? [cUrl query] : [url query]; - + queryComponents = [cUrl query] ? [cUrl queryComponents] : [url queryComponents]; + queryTls = [queryComponents valueForKey: @"tls"]; + tlsVerifyMode = [queryComponents valueForKey: @"tlsVerifyMode"]; + + if (!tlsVerifyMode) + tlsVerifyMode = @"default"; + if (scheme && [scheme caseInsensitiveCompare: @"imaps"] == NSOrderedSame) { - if (query && [query caseInsensitiveCompare: @"tls=YES"] == NSOrderedSame) - { - defaultPort = 143; - encryption = @"tls"; - } + if (queryTls && [queryTls caseInsensitiveCompare: @"YES"] == NSOrderedSame) + { + defaultPort = 143; + encryption = @"tls"; + } else - { - encryption = @"ssl"; - defaultPort = 993; - } + { + encryption = @"ssl"; + defaultPort = 993; + } } else { - if (query && [query caseInsensitiveCompare: @"tls=YES"] == NSOrderedSame) + if (queryTls && [queryTls caseInsensitiveCompare: @"YES"] == NSOrderedSame) encryption = @"tls"; else encryption = @"none"; - + defaultPort = 143; } + port = [cUrl port] ? [cUrl port] : [url port]; if ([port intValue] == 0) /* port is nil or intValue == 0 */ port = [NSNumber numberWithInt: defaultPort]; [mailAccount setObject: port forKey: @"port"]; [mailAccount setObject: encryption forKey: @"encryption"]; + [mailAccount setObject: tlsVerifyMode forKey: @"tlsVerifyMode"]; // 4. Sieve server sieveServer = [self _fetchFieldForUser: @"c_sievehostname"]; @@ -730,7 +739,7 @@ { [mailAccount setObject: sieveServer forKey: @"sieveServerName"]; } - + // 5. Identities identities = [NSMutableArray new]; [identities addObjectsFromArray: [_defaults mailIdentities]]; @@ -1048,11 +1057,11 @@ - (SOGoContactFolder *) personalContactsFolderInContext: (WOContext *) context { SOGoContactFolders *folders; - + folders = [[self homeFolderInContext: context] lookupName: @"Contacts" inContext: context acquire: NO]; - + return [folders lookupPersonalFolder: @"personal" ignoringRights: YES]; } @@ -1113,7 +1122,7 @@ id authValue; authValue = [self _fetchFieldForUser: @"canAuthenticate"]; - + return [authValue boolValue]; } @@ -1158,9 +1167,9 @@ - (int) numberOfSimultaneousBookings { NSNumber *v; - + v = [self _fetchFieldForUser: @"numberOfSimultaneousBookings"]; - + if (v) return [v intValue]; diff --git a/Tests/Unit/GNUmakefile b/Tests/Unit/GNUmakefile index 199e11952..f9ee44791 100644 --- a/Tests/Unit/GNUmakefile +++ b/Tests/Unit/GNUmakefile @@ -30,7 +30,9 @@ $(TEST_TOOL)_OBJC_FILES += \ TestNSString+Crypto.m \ TestNSString+URLEscaping.m \ TestNSString+Utilities.m \ + TestNSURL+misc.m \ TestNGMailAddressParser.m \ + TestNGInternetSocketAddress.m \ \ # I don't know how to link against -l:SOGoBackend \ undefined reference to `__objc_class_name_SOGoMailFolder' @@ -42,7 +44,7 @@ $(TEST_TOOL)_CPPFLAGS += \ ADDITIONAL_LIB_DIRS += \ -L../../SoObjects/SOGo/SOGo.framework/Versions/Current/sogo -L../../SOPE/NGCards/obj -L../../SOPE/GDLContentStore/obj -lSOGo -lNGMime -lNGCards -lGDLContentStore -lNGExtensions -lSBJson -lobjc \ - -L/usr/local/lib -lSaxObjC \ + -L/usr/local/lib -lSaxObjC -lNGStreams \ -Wl,-rpath,../../SoObjects/SOGo/SOGo.framework/Versions/Current/sogo -Wl,-rpath,../../SOPE/NGCards/obj -Wl,-rpath,../../SOPE/GDLContentStore/obj ADDITIONAL_LDFLAGS += -Wl,--no-as-needed diff --git a/Tests/Unit/TestNGInternetSocketAddress.m b/Tests/Unit/TestNGInternetSocketAddress.m new file mode 100644 index 000000000..b092ae39c --- /dev/null +++ b/Tests/Unit/TestNGInternetSocketAddress.m @@ -0,0 +1,56 @@ +/* TestNGInternetSocketAddress.m - this file is part of SOGo + * + * Copyright (C) 2020 Nicolas Höft + * + * Author: Nicolas Höft + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import + +#import "SOGoTest.h" + +@interface TestNGInternetSocketAddress : SOGoTest +@end + +@implementation TestNGInternetSocketAddress + +- (void) test_isLocalhost +{ + // @"localhost6", + NSString *addrStr[] = { @"127.0.0.1", @"127.1.2.3", @"localhost", @"something.localhost", @"localhost.", nil }; + NSString **curHost; + BOOL is_localhost; + NSString *error; + + curHost = addrStr; + while (*curHost) + { + NGInternetSocketAddress *addr; + addr = [NGInternetSocketAddress addressWithPort: 1234 + onHost: *curHost]; + is_localhost = [addr isLocalhost]; + + error = [NSString stringWithFormat: + @"expected '%@' to be a localhost address", *curHost]; + testWithMessage(is_localhost, error); + + curHost++; + } +} + +@end diff --git a/Tests/Unit/TestNGMimeType.m b/Tests/Unit/TestNGMimeType.m index 8cfe534b6..c50962304 100644 --- a/Tests/Unit/TestNGMimeType.m +++ b/Tests/Unit/TestNGMimeType.m @@ -61,10 +61,6 @@ [NSNumber numberWithInt: NSISOLatin1StringEncoding], [NSNumber numberWithInt: 0], - @"nil", - [NSNumber numberWithInt: [NSString defaultCStringEncoding]], - [NSNumber numberWithInt: 0], - @"", [NSNumber numberWithInt: [NSString defaultCStringEncoding]], [NSNumber numberWithInt: 0], diff --git a/Tests/Unit/TestNSURL+misc.m b/Tests/Unit/TestNSURL+misc.m new file mode 100644 index 000000000..a830e0cf2 --- /dev/null +++ b/Tests/Unit/TestNSURL+misc.m @@ -0,0 +1,64 @@ +/* TestNSURL+miscm - this file is part of SOGo + * + * Copyright (C) 2020 Nicolas Höft + * + * Author: Nicolas Höft + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#import + +#import +#import + +#import "SOGoTest.h" + +@interface TestNSURL_plusmisc : SOGoTest +@end + +@implementation TestNSURL_plusmisc + +- (void) test_queryComponents +{ + NSString *urlStr; + NSString *error; + NSURL *url; + NSDictionary *queryComp; + + urlStr = @"http://domain/path?key=value"; + + url = [NSURL URLWithString: urlStr]; + queryComp = [url queryComponents]; + + error = [NSString stringWithFormat: + @"expected '%@' to have 1 entry", urlStr]; + testWithMessage([queryComp count] == 1, error); + + test([[queryComp valueForKey: @"key"] isEqualToString: @"value"]); + + urlStr = @"http://domain/path?key1=value123&key2=val2&"; + url = [NSURL URLWithString: urlStr]; + queryComp = [url queryComponents]; + error = [NSString stringWithFormat: + @"expected '%@' to have 2 entries, got %d", urlStr, [queryComp count]]; + testWithMessage([queryComp count] == 2, error); + + test([[queryComp valueForKey: @"key1"] isEqualToString: @"value123"]); + test([[queryComp valueForKey: @"key2"] isEqualToString: @"val2"]); + +} + +@end