Compare commits

..
3 Commits
Author SHA1 Message Date
Hivert Quentin 99b76ec9ca again 2025-03-26 10:42:39 +01:00
Hivert Quentin 5d0ee3f49b cast correct type 2025-03-26 09:59:47 +01:00
Hivert Quentin ecfc54a0af add logs 2025-03-26 09:26:35 +01:00
323 changed files with 1071 additions and 62414 deletions

No files matched your search

-1
View File
@@ -10,7 +10,6 @@
*/obj/
.scss-lint-config.yml_
ActiveSync/ActiveSync.SOGo
API/API.SOGo
Documentation/*.docbook
Documentation/*.pdf
Documentation/*.html
+1 -1
View File
@@ -1,3 +1,3 @@
[submodule "UI/WebServerResources/angular-material"]
path = UI/WebServerResources/angular-material
url = https://github.com/Alinto/material.git
url = https://github.com/angular/material
+1 -1
View File
@@ -1,6 +1,6 @@
[main]
host = https://www.transifex.com
lang_map = fr: French, pt_BR: BrazilianPortuguese, ru: Russian, sk: Slovak, es_AR: SpanishArgentina, fi: Finnish, id_ID: Indonesian, lt: Lithuanian, tr_TR: TurkishTurkey, zh_TW: ChineseTaiwan, ar: Arabic, cs: Czech, nn_NO: NorwegianNynorsk, sl_SI: Slovenian, uk: Ukrainian, zh_CN: ChineseChina, sv: Swedish, bg_BG: Bulgarian, cy: Welsh, is: Icelandic, sr: Serbian, lv: Latvian, pl: Polish, ro_RO: Romanian, bs_BA: Bosnian,ca: Catalan, he: Hebrew, kk: Kazakh, pt: Portuguese, es_ES: SpanishSpain, eu: Basque, hu: Hungarian, ja: Japanese, mk_MK: Macedonian, nb_NO: NorwegianBokmal, nl: Dutch, sr@latin: SerbianLatin, da_DK: Danish, de: German, hr_HR: Croatian, it: Italian, sr_ME@latin: Montenegrin, gl_ES: Galician, ko: Korean, th: Thai, el: Greek
lang_map = fr: French, pt_BR: BrazilianPortuguese, ru: Russian, sk: Slovak, es_AR: SpanishArgentina, fi: Finnish, id_ID: Indonesian, lt: Lithuanian, tr_TR: TurkishTurkey, zh_TW: ChineseTaiwan, ar: Arabic, cs: Czech, nn_NO: NorwegianNynorsk, sl_SI: Slovenian, uk: Ukrainian, zh_CN: ChineseChina, sv: Swedish, bg_BG: Bulgarian, cy: Welsh, is: Icelandic, sr: Serbian, lv: Latvian, pl: Polish, ro_RO: Romanian, bs_BA: Bosnian,ca: Catalan, he: Hebrew, kk: Kazakh, pt: Portuguese, es_ES: SpanishSpain, eu: Basque, hu: Hungarian, ja: Japanese, mk_MK: Macedonian, nb_NO: NorwegianBokmal, nl: Dutch, sr@latin: SerbianLatin, da_DK: Danish, de: German, hr_HR: Croatian, it: Italian, sr_ME@latin: Montenegrin, gl_ES: Galician
[o:alinto:p:sogo:r:soobjects-appointments]
source_file = SoObjects/Appointments/English.lproj/Localizable.strings
-28
View File
@@ -1,28 +0,0 @@
# GNUstep makefile
include common.make
BUNDLE_NAME = API
API_PRINCIPAL_CLASS = SOGoAPIProduct
API_OBJC_FILES = \
SOGoAPIProduct.m \
SOGoAPI.m \
SOGoAPIVersion.m \
SOGoAPIUserFolder.m \
SOGoAPIDispatcher.m
API_RESOURCE_FILES += \
product.plist
API_LANGUAGES = $(SOGO_LANGUAGES)
API_LOCALIZED_RESOURCE_FILES = Localizable.strings
ADDITIONAL_INCLUDE_DIRS += -I../SOPE/ -I../SoObjects/
-include GNUmakefile.preamble
include $(GNUSTEP_MAKEFILES)/bundle.make
-include GNUmakefile.postamble
-16
View File
@@ -1,16 +0,0 @@
# compile settings
ADDITIONAL_CPPFLAGS += \
-DSOGO_MAJOR_VERSION=$(MAJOR_VERSION) \
-DSOGO_MINOR_VERSION=$(MINOR_VERSION) \
-DSOGO_PATCH_VERSION=$(SUBMINOR_VERSION) \
-DSOGO_LIBDIR="@\"$(SOGO_LIBDIR)\""
ADDITIONAL_INCLUDE_DIRS += \
-D_GNU_SOURCE -I../SOPE/ -I../SoObjects/
ADDITIONAL_LIB_DIRS += \
-L../SoObjects/SOGo/SOGo.framework/sogo -lSOGo \
-L../SOPE/GDLContentStore/$(GNUSTEP_OBJ_DIR)/ -lGDLContentStore \
-L../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ -lNGCards \
-lEOControl -lNGStreams -lNGMime -lNGExtensions -lNGObjWeb -lWEExtensions
-35
View File
@@ -1,35 +0,0 @@
/*
Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of SOPE.
SOPE is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
SOPE 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 Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with SOPE; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
#import <Foundation/Foundation.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSArray.h>
@class WOContext;
@interface SOGoAPI : NSObject
- (NSArray *) methodAllowed;
- (BOOL) needAuth;
- (NSArray *) paramNeeded;
- (NSDictionary *) action: (WOContext*) ctx withParam: (NSDictionary *) param;
@end
-52
View File
@@ -1,52 +0,0 @@
/*
Copyright (C) todo...
*/
#import <SOGoAPI.h>
@implementation SOGoAPI
- (id) init
{
[super init];
return self;
}
- (void) dealloc
{
[super dealloc];
}
- (NSArray *) methodAllowed
{
NSArray *result;
result = [NSArray arrayWithObjects:@"GET",nil];
return result;
}
- (BOOL) needAuth
{
return YES;
}
- (NSArray *) paramNeeded
{
return nil;
}
- (NSDictionary *) action: (WOContext*) ctx withParam: (NSDictionary *) param
{
NSDictionary* result;
result = [[NSDictionary alloc] initWithObjectsAndKeys:
@"API not defined", @"error",
nil];
[result autorelease];
return result;
}
@end /* SOGoAPI */
View File
Whitespace-only changes.
-54
View File
@@ -1,54 +0,0 @@
/*
Copyright (c) 2014-2015, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Foundation/NSObject.h>
#import <SOGo/WORequest+SOGo.h>
#import <SOGo/WOResponse+SOGo.h>
@class NSCalendarDate;
@class NSException;
@class NSMutableDictionary;
@class NSURL;
@class NSNumber;
static volatile BOOL apiShouldTerminate = NO;
@interface SOGoAPIDispatcher : NSObject
{
id context;
BOOL debugOn;
}
- (NSException *) dispatchRequest: (WORequest*) theRequest
inResponse: (WOResponse*) theResponse
context: (id) theContext;
@end
-399
View File
@@ -1,399 +0,0 @@
/*
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Inverse inc. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "SOGoAPIDispatcher.h"
#import <Foundation/NSAutoreleasePool.h>
#import <NGObjWeb/NSException+HTTP.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <NGObjWeb/WOCoreApplication.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSString+misc.h>
#import <NGExtensions/NSString+Encoding.h>
#import <SOGo/SOGoSystemDefaults.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserManager.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/WORequest+SOGo.h>
#import <SOGo/WOResponse+SOGo.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoOpenIdSession.h>
void handle_api_terminate(int signum)
{
NSLog(@"Forcing termination of API loop.");
apiShouldTerminate = YES;
[[WOCoreApplication application] terminateAfterTimeInterval: 1];
}
@implementation SOGoAPIDispatcher
- (id) init
{
[super init];
debugOn = [[SOGoSystemDefaults sharedSystemDefaults] apiDebugEnabled];
apiShouldTerminate = NO;
signal(SIGTERM, handle_api_terminate);
return self;
}
- (void) dealloc
{
[super dealloc];
}
- (void) _sendAPIErrorResponse: (WOResponse* ) response
withMessage: (NSString *) message
withStatus: (unsigned int) status
{
NSDictionary *msg;
msg = [[NSDictionary alloc] initWithObjectsAndKeys:
message, @"error",
nil];
[response setStatus: status];
[response setContent: [msg jsonRepresentation]];
}
- (NSString *) _getActionFromUri: (NSString *) _uri
{
/*
_uri always start with /SOGo/SOGoAPI
full _uri example /SOGo/SOGoAPI/Action/subaction1?param1&param2
*/
NSString *uriWithoutParams, *action, *prefix;
NSArray *uriSplits;
prefix = @"/SOGo/SOGoAPI";
action = @"";
uriWithoutParams = [_uri urlWithoutParameters];
if(![uriWithoutParams hasPrefix: prefix])
{
[self errorWithFormat: @"Uri for API request does not start with /SOGo/SOGoAPI: %@", uriWithoutParams];
return nil;
}
else
{
uriWithoutParams = [uriWithoutParams substringFromIndex:[prefix length]];
}
//remove first and last '/'' if needed
if([uriWithoutParams hasPrefix: @"/"])
{
uriWithoutParams = [uriWithoutParams substringFromIndex:1];
}
if([uriWithoutParams hasSuffix: @"/"])
{
uriWithoutParams = [uriWithoutParams substringToIndex:([uriWithoutParams length] -1)];
}
if([uriWithoutParams length] == 0)
{
[self warnWithFormat: @"Uri for API request has no action, make Version instead: %@", uriWithoutParams];
return @"SOGoAPIVersion";
}
else
{
uriSplits = [uriWithoutParams componentsSeparatedByString: @"/"];
action = [@"SOGoAPI" stringByAppendingString: [uriSplits objectAtIndex: 0]];
if(debugOn)
[self logWithFormat: @"API request, action made is %@", action];
}
return action;
}
- (NSDictionary *) _authBasicCheck: (NSString *) auth
{
NSDictionary *user;
NSRange rng;
NSString *decodeCred, *domain, *login, *pwd;
SOGoUserManager *lm;
SOGoPasswordPolicyError perr;
int expire, grace;
BOOL rc;
user = nil;
decodeCred = [[auth substringFromIndex:5] stringByTrimmingLeadWhiteSpaces];
decodeCred = [decodeCred stringByDecodingBase64];
rng = [decodeCred rangeOfString:@":"];
login = [decodeCred substringToIndex:rng.location];
pwd = [decodeCred substringFromIndex:(rng.location + rng.length)];
domain = nil;
perr = PolicyNoError;
rc = ([[SOGoUserManager sharedUserManager]
checkLogin: [login stringByReplacingString: @"%40"
withString: @"@"]
password: pwd
domain: &domain
perr: &perr
expire: &expire
grace: &grace
additionalInfo: nil]
&& perr == PolicyNoError);
if(rc)
{
//Fecth user info
lm = [SOGoUserManager sharedUserManager];
user = [lm contactInfosForUserWithUIDorEmail: login];
}
else
user = nil;
return user;
}
- (NSDictionary *) _authOpenId: (NSString *) auth withDomain: (NSString *) domain
{
NSDictionary *user;
NSString *token, *login;
SOGoOpenIdSession *openIdSession;
SOGoUserManager *lm;
user = nil;
token = [[auth substringFromIndex:6] stringByTrimmingLeadWhiteSpaces];
openIdSession = [SOGoOpenIdSession OpenIdSession: domain];
if(![openIdSession sessionIsOk])
{
[self errorWithFormat: @"API - OpenId server not found or has unexpected behavior, contact your admin."];
return nil;
}
[openIdSession setAccessToken: token];
login = [openIdSession login: @""];
if(login && ![login isEqualToString: @"anonymous"])
{
//Fecth user info
lm = [SOGoUserManager sharedUserManager];
user = [lm contactInfosForUserWithUIDorEmail: login];
}
else
user = nil;
return user;
}
- (NSException *) dispatchRequest: (WORequest*) theRequest
inResponse: (WOResponse*) theResponse
context: (id) theContext
{
NSAutoreleasePool *pool;
id activeUser;
NSString *method, *action, *error;
NSDictionary *form;
NSArray *paramNeeded;
NSMutableDictionary *paramInjected, *ret;
NSBundle *bundle;
id classAction;
Class clazz;
pool = [[NSAutoreleasePool alloc] init];
ASSIGN(context, theContext);
//Get the api action, check it
action = [self _getActionFromUri: [theRequest uri]];
if(!action)
{
error = [NSString stringWithFormat: @"No actions found for request to API: %@", [theRequest uri]];
[self errorWithFormat: error];
[self _sendAPIErrorResponse: theResponse withMessage: error withStatus: 400];
RELEASE(context);
RELEASE(pool);
return nil;
}
//Get the class for this action, check it
bundle = [NSBundle bundleForClass: NSClassFromString(@"SOGoAPIProduct")];
clazz = [bundle classNamed: action];
if(!clazz)
{
error = [NSString stringWithFormat: @"No backend API found for action: %@", action];
[self errorWithFormat: error];
[self _sendAPIErrorResponse: theResponse withMessage: error withStatus: 400];
RELEASE(context);
RELEASE(pool);
return nil;
}
//try to instantiate the class
NS_DURING
{
classAction = [[clazz alloc] init];
}
NS_HANDLER
{
error = [NSString stringWithFormat: @"Can't alloc and init class: %@", classAction];
[self errorWithFormat: error];
[self _sendAPIErrorResponse: theResponse withMessage: error withStatus: 500];
RELEASE(context);
RELEASE(pool);
return nil;
}
NS_ENDHANDLER;
//Check method
method = [theRequest method];
if(![[classAction methodAllowed] containsObject: method])
{
error = [NSString stringWithFormat: @"Method %@ not allowed for action %@", method, action];
[self errorWithFormat: error];
[self _sendAPIErrorResponse: theResponse withMessage: error withStatus: 400];
RELEASE(context);
RELEASE(pool);
return nil;
}
paramInjected = [NSMutableDictionary dictionary];
//Check parameters
if((paramNeeded = [classAction paramNeeded]) && [paramNeeded count] >= 1)
{
NSDictionary* formAndQuery = [theRequest formValues];
for(NSString *param in paramNeeded)
{
id value;
if((value = [formAndQuery objectForKey: param]))
{
NSString* trueValue;
if ([value isKindOfClass: [NSArray class]])
trueValue = [value lastObject];
else
trueValue = value;
[paramInjected setObject: trueValue forKey: param];
}
else
{
error = [NSString stringWithFormat: @"Missing param %@ for action %@", param, action];
[self errorWithFormat: error];
[self _sendAPIErrorResponse: theResponse withMessage: error withStatus: 400];
RELEASE(context);
RELEASE(pool);
return nil;
}
}
}
if([classAction needAuth])
{
if(debugOn)
[self logWithFormat: @"Check auth for action %@", action];
//check auth
NSString *auth = [theRequest headerForKey: @"authorization"];
if(auth)
{
NSDictionary* user;
if([[auth lowercaseString] hasPrefix: @"basic"])
{
//basic auth
user = [self _authBasicCheck: auth];
}
else if([[auth lowercaseString] hasPrefix: @"bearer"])
{
//openid auth, we may need to know the user-domain to know which openid server to fetch
NSString *domain = [theRequest headerForKey: @"user-domain"];
user = [self _authOpenId: auth withDomain: domain];
}
else
{
error = [NSString stringWithFormat: @"Authorization method incorrect: %@ for action %@", auth, action];
[self errorWithFormat: error];
[self _sendAPIErrorResponse: theResponse withMessage: error withStatus: 401];
RELEASE(context);
RELEASE(pool);
return nil;
}
//add current user in paramInjected
if(user){
if(debugOn)
[self logWithFormat: @"User authenticated %@", user];
[paramInjected setObject: user forKey: @"user"];
}
else
{
error = [NSString stringWithFormat: @"User wrong login or not found for action %@", action];
[self errorWithFormat: error];
[self _sendAPIErrorResponse: theResponse withMessage: error withStatus: 401];
RELEASE(context);
RELEASE(pool);
return nil;
}
}
else
{
error = [NSString stringWithFormat: @"No authorization header found for action %@", action];
[self errorWithFormat: error];
[self _sendAPIErrorResponse: theResponse withMessage: error withStatus: 401];
RELEASE(context);
RELEASE(pool);
return nil;
}
}
//Execute action
NS_DURING
{
ret = [classAction action: context withParam: paramInjected];
}
NS_HANDLER
{
error = [NSString stringWithFormat: @"Internal error during: %@", action];
[self errorWithFormat: error];
[self _sendAPIErrorResponse: theResponse withMessage: error withStatus: 500];
RELEASE(context);
RELEASE(pool);
return nil;
}
NS_ENDHANDLER;
//Make the response
[theResponse setContent: [ret jsonRepresentation]];
RELEASE(context);
RELEASE(pool);
return nil;
}
@end
-11
View File
@@ -1,11 +0,0 @@
/*
Copyright (C) todo...
*/
#import <Foundation/NSObject.h>
@interface SOGoAPIProduct : NSObject
@end
@implementation SOGoAPIProduct
@end /* SOGoAPIProduct */
-33
View File
@@ -1,33 +0,0 @@
/*
Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of SOPE.
SOPE is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
SOPE 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 Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with SOPE; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
#import <Foundation/Foundation.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSArray.h>
#import <SOGoAPI.h>
@class WOContext;
@interface SOGoAPIUserFolder : SOGoAPI
- (NSArray *) methodAllowed;
- (NSDictionary *) action: (WOContext*) ctx withParam: (NSDictionary *) param;
@end
-110
View File
@@ -1,110 +0,0 @@
/*
Copyright (C) todo...
*/
#import <SOGoAPIUserFolder.h>
#import <GDLContentStore/GCSFolderManager.h>
#import <GDLContentStore/GCSFolder.h>
@implementation SOGoAPIUserFolder
- (id) init
{
[super init];
return self;
}
- (void) dealloc
{
[super dealloc];
}
/**
{
"calendar":[
{
"name":"DidyShared",
"url":"http://127.0.0.1/SOGo/dav/sogo-tests1@example.org/Calendar/12509-67F67D00-1-3105AF40"
},
{
"name":"LocalDidy",
"url":"http://127.0.0.1/SOGo/dav/sogo-tests1@example.org/Calendar/1BC38-67B60000-1-6E4B6880"
},
{
"name":"Personal Calendar",
"url":"http://127.0.0.1/SOGo/dav/sogo-tests1@example.org/Calendar/personal"
}
],
"username":"sogo-tests1@example.org",
"contact":[
{
"name":"Personal Address Book",
"url":"http://127.0.0.1/SOGo/dav/sogo-tests1@example.org/Contacts/personal"
}
]
}
**/
- (NSDictionary *) action: (WOContext*) ctx withParam: (NSDictionary *) param
{
NSDictionary* result;
NSArray *folders;
NSMutableArray *cardavLinks, *caldavLinks;
NSString *serverUrl, *basePath, *c_uid, *url;
GCSFolderManager *fm;
GCSFolder *folder;
int max, i;
//Should be a user
c_uid = [[[param objectForKey: @"user"] objectForKey: @"emails"] objectAtIndex: 0];
//fetch folders
fm = [GCSFolderManager defaultFolderManager];
basePath = [NSString stringWithFormat: @"/Users/%@", c_uid];
folders = [fm listSubFoldersAndNamesAtPath: basePath recursive: YES];
//Generate dav link
max = [folders count];
serverUrl = [[ctx serverURL] absoluteString];
cardavLinks = [NSMutableArray array];
caldavLinks = [NSMutableArray array];
serverUrl = [[ctx serverURL] absoluteString];
for (i = 0; i < max; i++)
{
NSMutableDictionary *folderRet;
folderRet = [NSMutableDictionary dictionary];
folder = [folders objectAtIndex: i];
url = [NSString stringWithFormat: @"%@/SOGo/dav/%@/%@", serverUrl, c_uid, [folder objectForKey: @"path"]];
[folderRet setObject: url forKey: @"url"];
[folderRet setObject: [folder objectForKey: @"name"] forKey: @"name"];
if([url rangeOfString:@"/Calendar/"].location == NSNotFound)
{
//Contacts
[cardavLinks addObject: folderRet];
}
else
{
//Calendar
[caldavLinks addObject: folderRet];
}
}
result = [[NSDictionary alloc] initWithObjectsAndKeys:
c_uid, @"username",
cardavLinks, @"contact",
caldavLinks, @"calendar",
nil];
[result autorelease];
return result;
}
@end /* SOGoAPIVersion */
-33
View File
@@ -1,33 +0,0 @@
/*
Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of SOPE.
SOPE is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
SOPE 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 Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with SOPE; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
#import <Foundation/Foundation.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSArray.h>
#import <SOGoAPI.h>
@class WOContext;
@interface SOGoAPIVersion : SOGoAPI
- (NSArray *) methodAllowed;
- (NSDictionary *) action: (WOContext*) ctx withParam: (NSDictionary *) param;
@end
-42
View File
@@ -1,42 +0,0 @@
/*
Copyright (C) todo...
*/
#import <SOGoAPIVersion.h>
@implementation SOGoAPIVersion
- (id) init
{
[super init];
return self;
}
- (void) dealloc
{
[super dealloc];
}
- (BOOL) needAuth
{
return NO;
}
- (NSDictionary *) action: (WOContext*) ctx withParam: (NSDictionary *) param
{
NSDictionary* result;
result = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInt:SOGO_MAJOR_VERSION], @"major",
[NSNumber numberWithInt:SOGO_MINOR_VERSION], @"minor",
[NSNumber numberWithInt:SOGO_PATCH_VERSION], @"patch",
nil];
[result autorelease];
return result;
}
@end /* SOGoAPIVersion */
-34
View File
@@ -1,34 +0,0 @@
include ../config.make
include $(GNUSTEP_MAKEFILES)/common.make
include ../Version
NEEDS_GUI=no
BUNDLE_EXTENSION = .SOGo
BUNDLE_INSTALL_DIR = $(SOGO_LIBDIR)
WOBUNDLE_EXTENSION = $(BUNDLE_EXTENSION)
WOBUNDLE_INSTALL_DIR = $(BUNDLE_INSTALL_DIR)
# SYSTEM_LIB_DIR += -L/usr/local/lib -L/usr/lib
ADDITIONAL_INCLUDE_DIRS += \
-I.. \
-I../.. \
-I../../SOPE
ADDITIONAL_LIB_DIRS += \
-L../SoObjects/SOGo/SOGo.framework/Versions/Current/sogo \
-L../SoObjects/SOGo/$(GNUSTEP_OBJ_DIR)/ \
-L../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ \
-L/usr/local/lib \
-Wl,-rpath,$(SOGO_SYSLIBDIR)/sogo
BUNDLE_LIBS += \
-lSOGo \
-lGDLContentStore \
-lGDLAccess \
-lNGObjWeb \
-lNGCards -lNGMime -lNGLdap \
-lNGStreams -lNGExtensions -lEOControl \
-lDOM -lSaxObjC -lSBJson
ADDITIONAL_BUNDLE_LIBS += $(BUNDLE_LIBS)
-31
View File
@@ -1,31 +0,0 @@
{
requires = ( MAIN, Appointments, Contacts, Mailer );
publicResources = ();
factories = {};
classes = {
SOGoAPI = {
protectedBy = "<public>";
defaultRoles = {
"View" = ( "Authenticated", "PublicUser" );
};
};
};
categories = {
SOGoAPI = {
slots = {
};
methods = {
Version = {
protectedBy = "View";
pageName = "SOGoAPI";
actionName = "sogoVersion";
};
};
};
};
}
+19 -9
View File
@@ -2471,28 +2471,37 @@ void handle_eas_terminate(int signum)
[[o properties] setObject: processIdentifier forKey: @"PingRequest"];
[o save];
NSLog(@"EASLOG: processPing, load defaults");
defaults = [SOGoSystemDefaults sharedSystemDefaults];
defaultInterval = [defaults maximumPingInterval];
internalInterval = [defaults internalSyncInterval];
sleepInterval = (internalInterval < 5) ? 5 : internalInterval;
NSLog(@"EASLOG: processPing, defaults found defaultInterval: %d, internalInterval: %d, sleepInterval: %d", defaultInterval, internalInterval, sleepInterval);
if (theDocumentElement)
{
heartbeatInterval = [[[(id)[theDocumentElement getElementsByTagName: @"HeartbeatInterval"] lastObject] textValue] intValue];
NSLog(@"EASLOG: processPing, heartbeatInterval found in document: %d", heartbeatInterval);
}
else
{
heartbeatInterval = defaultInterval;
NSLog(@"EASLOG: processPing, heartbeatInterval NOT found in document: %d", heartbeatInterval);
}
if (heartbeatInterval > defaultInterval || heartbeatInterval == 0)
{
heartbeatInterval = defaultInterval;
status = 5;
}
{
heartbeatInterval = defaultInterval;
status = 5;
}
else
{
if (heartbeatInterval < internalInterval)
heartbeatInterval = internalInterval;
{
if (heartbeatInterval < internalInterval)
heartbeatInterval = internalInterval;
status = 1;
}
status = 1;
}
NSLog(@"EASLOG: processPing, heartbeatInterval final value: %d", heartbeatInterval);
// We build the list of folders to "ping". When the payload is empty, we use the list
// of "cached" folders.
@@ -2607,6 +2616,7 @@ void handle_eas_terminate(int signum)
{
// We check if we must break the current ping request since an other ping request
// has just arrived.
NSLog(@"EASLOG: processPing, while loop for changes start. total_sleep: %d, internalInterval: %d", total_sleep, internalInterval);
pingRequestInCache = [[self globalMetadataForDevice] objectForKey: @"PingRequest"];
if (pingRequestInCache && ![pingRequestInCache isEqualToString: processIdentifier])
{
+1 -1
View File
@@ -1627,7 +1627,7 @@ struct GlobalObjectId {
[map setObject: dateReceived forKey: @"date"];
messageId = [NSString generateMessageID: [identity objectForKey: @"email"]];
messageId = [NSString generateMessageID];
[map setObject: messageId forKey: @"message-id"];
attachmentKeys = [self fetchFileAttachmentKeys];
-208
View File
@@ -2,214 +2,6 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [5.12.6](https://github.com/Alinto/sogo/compare/SOGo-5.12.6...SOGo-5.12.6) (2026-03-19)
### Bug Fixes
* **totp:** new user can properly use totp ([623f08](https://github.com/Alinto/sogo/commit/623f083cd94842766c6d9430cf3bdad8fc4d5dbc))
### [5.12.5](https://github.com/Alinto/sogo/compare/SOGo-5.12.4...SOGo-5.12.5) (2026-03-03)
### Bug Fixes
* **db:** increase some column size ([f8638a3](https://github.com/Alinto/sogo/commit/f8638a3f4edd3487901c119b079dea2a3746cd7c))
* **encryptedUrl:** fix cache key data and expect uncrypted name for freebusy ([95efe73](https://github.com/Alinto/sogo/commit/95efe73e69abaae7fb8c1bdd0fe7ac660d59d7a4))
* **event:** also add jitsi url in the location as outlook doesn't support attach url ([7876013](https://github.com/Alinto/sogo/commit/7876013a4c1b0c7a1ac7153f2c61ff5f3de39632))
* **identity:** fix signatrue when changing identity ([71d865b](https://github.com/Alinto/sogo/commit/71d865b86a7c11dc6aa8612999ae515f14a3bdf2))
* **login:** prevent user search for login keyword ([6f91600](https://github.com/Alinto/sogo/commit/6f916008e9e30bceaf5942e0c8645006d07d3aec))
* **Mail:** correctly update quota when refreshing ([af984f5](https://github.com/Alinto/sogo/commit/af984f58c043cb7ddc8597f119c8a5b9737612b2))
* **mail:** use the correct replyTo when set to a non*default identity ([03fa91d](https://github.com/Alinto/sogo/commit/03fa91dc22b268ce230f78e7386234ca84814f27))
* **minsearch:** fix instance of minsearch ([d7e5165](https://github.com/Alinto/sogo/commit/d7e51655fb0a543cff68a9fbc10ceb67540e4bec))
* **tool:** rename-user properly change data in c_defaults and c_settings ([d69f55c](https://github.com/Alinto/sogo/commit/d69f55c265e0e5873032d5e14e49636ed6c36631))
* **trad:** typo in a transation key ([e2b8494](https://github.com/Alinto/sogo/commit/e2b8494a9cde046a8aad71808353366c328e0230))
* **ui:** prevent UI to search for users with empty string ([389e8e6](https://github.com/Alinto/sogo/commit/389e8e64c4933edbcc1f756f422594e70d70c903))
* **vulnerability:** prevent javascript njection with hint query ([e821b20](https://github.com/Alinto/sogo/commit/e821b20f87d1a9757f1d0aff7d1e31703f97054b))
* **vulnerability:** prevent sogo to execute scripts pass in theme query ([16ab99e](https://github.com/Alinto/sogo/commit/16ab99e7cf8db2c30b211f0d5e338d7f9e3a9efb))
* **vulnerability:** prevent xss with events, tasks and contacts categories ([e9b3f2a](https://github.com/Alinto/sogo/commit/e9b3f2a43d7557e8416f6749df4ab4f9128af2d1))
* **vulnerability:** properly change the totp code after disabling it ([83d4c52](https://github.com/Alinto/sogo/commit/83d4c522f87cfde0ba543837d9b24c3479083ec2))
### Localization
* **ca:** Update Catalan translations ([4a587fe](https://github.com/Alinto/sogo/commit/4a587fe00044bfce55186a7f75af64b5256399d7))
* **es_ES:** Update SpanishSpain translations ([6ce884b](https://github.com/Alinto/sogo/commit/6ce884b3e71cc08c1e3eedb722be00ade6d1263f))
* **pt_BR:** Update BrazilianPortuguese translations ([39b820a](https://github.com/Alinto/sogo/commit/39b820a5490eadbbadc6872f0814c23b7a6d4a13))
* **pt:** Add Greek (partial) ([1fdb4d3](https://github.com/Alinto/sogo/commit/1fdb4d30261f5d69d83eef7d8615ef62ddd94a7e))
* **pt:** Update Portuguese translations ([fbd056c](https://github.com/Alinto/sogo/commit/fbd056cc293a7c53f0ff0de4dd8a50a0d998291f))
* **ru:** Update Russian translations ([1be58da](https://github.com/Alinto/sogo/commit/1be58da745b875aeb12096ac8fb8c51c6df99068))
* **th:** Update Thai translations ([d993227](https://github.com/Alinto/sogo/commit/d993227b36b6fbddebf113a5b544aa3bebf01bcc))
## [5.12.4](https://github.com/Alinto/sogo/compare/SOGo-5.12.3...SOGo-5.13.0) (2025-10-06)
### Enhancements
* **mail:** Add a setting to show the attachments above the mail body ([5d61660](https://github.com/Alinto/sogo/commit/5d616600dfa6258e4eebcb5c065e393ebfae8f3a))
### Bug Fixes
* **calendar:** fetch our own iana timezone instead of trusting VTIMEZONE ([244d138](https://github.com/Alinto/sogo/commit/244d1388eb00c060c5302e5bfd997fd397820bc0))
* **curl:** properly close curl connection ([42f620e](https://github.com/Alinto/sogo/commit/42f620e56201e17391532507c02ae370f117c574))
* **eas:** use proper imap mech for EAS request when using openid ([a7f7950](https://github.com/Alinto/sogo/commit/a7f7950d91c83acd75c0b0d55dec74ab85d38d6c))
* **ldap:** don't try to fetch for a nobody user ([4d8cca1](https://github.com/Alinto/sogo/commit/4d8cca10f63aff9499b71d61d59d6f81570eb659))
* **label:** ensure the translation kept is not null ([0fb3db0](https://github.com/Alinto/sogo/commit/0fb3db060871a14e2abd8c6963ccd812cbacf200))
* **label:** missing trad ([23c8648](https://github.com/Alinto/sogo/commit/23c8648169b4893aaafa2e369259d94d56c76831))
* **ldap:** fetch the correct user source when creating an event with an attendee ([736d758](https://github.com/Alinto/sogo/commit/736d7589b16be4ebcf9fc6931a79187888c3b444))
* **login:** Only remember the login if the auth was successful ([9e20190](https://github.com/Alinto/sogo/commit/9e20190fad1a437f7e1307f0adcfe19a8d45184c))
* **log:** remove the ics from the log line ([86d94ab](https://github.com/Alinto/sogo/commit/86d94ab8fefaf3aa5ba99e13c28a0b24036d94ff))
* **mailer:** correctly show interline for p element ([666c979](https://github.com/Alinto/sogo/commit/666c979eeae6d51a6bc09684ebbab57ed9ce4b91))
* **mailer:** don't add mailto: to string starting with @ ([9817487](https://github.com/Alinto/sogo/commit/981748754a4857bab66a6fb94cdc5da4265ce003))
* **mail:** preoperly generates the message-id header value ([7bee193](https://github.com/Alinto/sogo/commit/7bee193413935ca03e4936a15791fbbb534493ec))
* **openid:** correcly type userTokenInterval ([4ef79e2](https://github.com/Alinto/sogo/commit/4ef79e219c289697eec0c2d100c8af1a5d0889b6))
* **password:** Correctly check the secret question answer when hitting enter instead of the button next ([ed7a136](https://github.com/Alinto/sogo/commit/ed7a136e0f2c5ecd35033aef6d0561d9fcf2ccdc))
* **task:** fix reminder for tasks ([a7651e5](https://github.com/Alinto/sogo/commit/a7651e56c53a4dc33892d86f404a132a2b8af83a))
* **ui:** add css class for login page ([a077512](https://github.com/Alinto/sogo/commit/a077512e34bf42933457b92613b348ff2e06642a))
* **calendar:** don't allocate and copy all children for each time we call _filterComponent: ([481a9bf](https://github.com/Alinto/sogo/commit/481a9bf1de1da8e07b5ed4c1786aac89d19ca6c3))
* **core:** store translations in a globally static NSMutableDictionary ([f48f27c](https://github.com/Alinto/sogo/commit/f48f27c812466ce806ab60b43ebe5a2bfc97ed0a))
### Localization
* **ar:** Update Arabic translations ([6c3e661](https://github.com/Alinto/sogo/commit/6c3e66142f50c68ee9b1435e5d6961573e7445d4))
* **bg_BG:** Update Bulgarian translations ([4585293](https://github.com/Alinto/sogo/commit/4585293d999152e6c7b54f00f73f925051a4ed64))
* **bs_BA:** Update Bosnian translations ([4461122](https://github.com/Alinto/sogo/commit/446112269705ae1565a21d3e39e4665c172fc3d4))
* **ca:** Update Catalan translations ([b77129e](https://github.com/Alinto/sogo/commit/b77129e2e7608f163eeeb1c4ed7e35fb5558c3cb))
* **cs:** Update Czech translations ([f252e0e](https://github.com/Alinto/sogo/commit/f252e0e2c271118cceb2979a828f5220350ba57f))
* **cy:** Update Welsh translations ([6425a62](https://github.com/Alinto/sogo/commit/6425a62e1ab8900dc83df5ca165eb92718856886))
* **da_DK:** Update Danish translations ([ae293ec](https://github.com/Alinto/sogo/commit/ae293ec3ce7488530199e82bd7bde50b2d506a43))
* **de:** Update German translations ([95e1937](https://github.com/Alinto/sogo/commit/95e19379f6fa698f54a09d7b7bcb4cfe16b4c358))
* **es_AR:** Update SpanishArgentina translations ([07e9d34](https://github.com/Alinto/sogo/commit/07e9d34865e57fa64f8db1b7ed4a5a67a0c1f234))
* **es_ES:** Update SpanishSpain translations ([c755ab3](https://github.com/Alinto/sogo/commit/c755ab3b4ea5ecad2c98d396d6bca6d873d0f1de))
* **eu:** Update Basque translations ([b96e531](https://github.com/Alinto/sogo/commit/b96e531f662a41802bd83e54e4a8055987d36164))
* **fi:** Update Finnish translations ([c0c506f](https://github.com/Alinto/sogo/commit/c0c506f9103b12ed5a39b340c4bccac500a2f6a8))
* **fr:** Update French translations ([a914fd4](https://github.com/Alinto/sogo/commit/a914fd4151e5565d4f168819c2664dc460587dec))
* **gl:** Update Galician translations ([f017fef](https://github.com/Alinto/sogo/commit/f017fef4b2a86f210f36bd5e62afe37c7bba3a18))
* **he:** Update Hebrew translations ([070705a](https://github.com/Alinto/sogo/commit/070705acfaa65ad435c1d2fa659e4b1f333b59a8))
* **hr_HR:** Update Croatian translations ([e662f10](https://github.com/Alinto/sogo/commit/e662f10389795e281abd283775f0d168a519e25d))
* **hu:** Update Hungarian translations ([799e8c2](https://github.com/Alinto/sogo/commit/799e8c21af79b2a8beb082a9d56a238aee30ae4a))
* **id_ID:** Update Indonesian translations ([56e4929](https://github.com/Alinto/sogo/commit/56e4929ca78c105dde470a6d26749b1328fad47f))
* **is:** Update Icelandic translations ([401e2f5](https://github.com/Alinto/sogo/commit/401e2f51cd3a69afa72fb76ffeb8fc91a5779cca))
* **it:** Update Italian translations ([1bb74c7](https://github.com/Alinto/sogo/commit/1bb74c75227f858d680035a5eccd7c5ef954f869))
* **ja:** Update Japanese translations ([9a295c5](https://github.com/Alinto/sogo/commit/9a295c5944886df95b941f716fc5e6b4c62525b7))
* **kk:** Update Kazakh translations ([6514fbb](https://github.com/Alinto/sogo/commit/6514fbb8f1c2745dd44be49c72da88937fcf107f))
* **ko:** Update Korean translations ([ed91211](https://github.com/Alinto/sogo/commit/ed9121175aa6164a73987a247cebe47e53676529))
* **lt:** Update Lithuanian translations ([2b455fe](https://github.com/Alinto/sogo/commit/2b455fe587f96700c4398538b995680251660ac1))
* **lv:** Update Latvian translations ([ad7dc8f](https://github.com/Alinto/sogo/commit/ad7dc8f45ee316c4d39ebf5fa71207947b4be2f8))
* **mk_MK:** Update Macedonian translations ([798bed5](https://github.com/Alinto/sogo/commit/798bed54e2eb0f76f26dc6c6c423285c565b9888))
* **nb_NO:** Update NorwegianBokmal translations ([eab6d9f](https://github.com/Alinto/sogo/commit/eab6d9f2492eccc2397c3ad49ab284223cb8771c))
* **nl:** Update Dutch translations ([b42882b](https://github.com/Alinto/sogo/commit/b42882b564ad9aa1b510b00d06da21688d959785))
* **nn_NO:** Update NorwegianNynorsk translations ([3a442f1](https://github.com/Alinto/sogo/commit/3a442f1b3d38350a5544f0fb62718f45b70c37ad))
* **pl:** Update Polish translations ([16fcb46](https://github.com/Alinto/sogo/commit/16fcb46c4be26497d12be712c5bacf0f0056a5b3))
* **pt_BR:** Update BrazilianPortuguese translations ([9d277c3](https://github.com/Alinto/sogo/commit/9d277c3f157e21183648609e5bee429559e87a63))
* **pt:** Update Portuguese translations ([972ffcc](https://github.com/Alinto/sogo/commit/972ffcc0c6f4f67da5557da28b6c5af87d632b96))
* **ro_RO:** Update Romanian translations ([ec4d780](https://github.com/Alinto/sogo/commit/ec4d7807d994836218c6ba9c5979283623a520d4))
* **ru:** Update Russian translations ([1a80fd6](https://github.com/Alinto/sogo/commit/1a80fd6284750cc9997e6cbbafcdf86765032405))
* **sk:** Update Slovak translations ([0f24c17](https://github.com/Alinto/sogo/commit/0f24c175163e2a000d6cace4d9302cae384f3b49))
* **sl_SI:** Update Slovenian translations ([645be8f](https://github.com/Alinto/sogo/commit/645be8fcc53cd2729dad9906e66a8f630cffaf52))
* **sr_ME@latin:** Update Montenegrin translations ([3dc9b90](https://github.com/Alinto/sogo/commit/3dc9b9099223940ec4a4f660d499ac450398d6cc))
* **sr@latin:** Update SerbianLatin translations ([c675dbf](https://github.com/Alinto/sogo/commit/c675dbfaa70755abf4300462f538dfa878c7c969))
* **sr:** Update Serbian translations ([dd367b0](https://github.com/Alinto/sogo/commit/dd367b0e94b74dccfc862d9ca07906cc16234965))
* **sv:** Update Swedish translations ([2ebb637](https://github.com/Alinto/sogo/commit/2ebb63767e53c39bc982d9959dabacfdce8b9b19))
* **th:** Update Thai translations ([f86a32d](https://github.com/Alinto/sogo/commit/f86a32d0a148b8465a01f098855dc58470c1a689))
* **tr_TR:** Update TurkishTurkey translations ([8c38c9c](https://github.com/Alinto/sogo/commit/8c38c9ce038e902d4b79f2093448ffec2c1f1d55))
* **uk:** Update Ukrainian translations ([5021376](https://github.com/Alinto/sogo/commit/5021376daf92c95a69afa2a2d99cf559f858d478))
* **zh_CN:** Update ChineseChina translations ([22ab0b3](https://github.com/Alinto/sogo/commit/22ab0b3c751f7c20c234c1824552aaa4c661f983))
* **zh_TW:** Update ChineseTaiwan translations ([c7a519c](https://github.com/Alinto/sogo/commit/c7a519ca124121e259a00473e6163cbd93e16aa4))
### [5.12.3](https://github.com/Alinto/sogo/compare/SOGo-5.12.2...SOGo-5.12.3) (2025-07-12)
### Bug Fixes
* **calendar:** fix recurrent event last date ([3ab8677](https://github.com/Alinto/sogo/commit/3ab86776e2ea826e043e3a0519097538853d82e3)), closes [#8766b7](https://bugs.sogo.nu/view.php?id=8766b7)
* **view:** automatically refresh view only if a number is set. ([55dbae6](https://github.com/Alinto/sogo/commit/55dbae6bee9bdd1aa00333320ba076617cbfb0fc))
## [5.12.2](https://github.com/Alinto/sogo/compare/SOGo-5.12.1...SOGo-5.12.2) (2025-06-25)
### Features
* **api:** add endpoint for caldav/cardav url ([51f1521](https://github.com/Alinto/sogo/commit/51f1521dba2d0ca8bdbadffadb16db26b975f668))
* **openid:** swicth to libcurl for http request ([a782424](https://github.com/Alinto/sogo/commit/a782424a30cfe8e9c6f2769a45bcdd3498679237))
### Bug Fixes
* **api:** add handler for internal error ([dabad1d](https://github.com/Alinto/sogo/commit/dabad1d0cf46263d78558d79d2af9fff528a9a89))
* **calendar:** properly evalute last occurance freebusy ([8766b7c](https://github.com/Alinto/sogo/commit/8766b7c6b32aedf37d7f8f350e461931b253a4fb))
* **openid:** add state in connection flow ([085fc4a](https://github.com/Alinto/sogo/commit/085fc4a9eb7d1e2a0f7b48baa1f09a8ba0d515e1))
* **openid:** allow expires_in param to be null ([9954c36](https://github.com/Alinto/sogo/commit/9954c3607bfda55424f5ac532a1075407235f345))
* **openid:** make end_session_endpoint optional ([c5fb348](https://github.com/Alinto/sogo/commit/c5fb3482e22f1bfc935213e8ed7208becd9bd1f4))
* **password:** put correct regex for special char ([e36d0d2](https://github.com/Alinto/sogo/commit/e36d0d219baed8c7d57af0069fadb8d1bf7df072))
* **session:** allow password/token to be longer than userkey ([516606b](https://github.com/Alinto/sogo/commit/516606baff5674c25da1e76f532ca2a9d7818ec0))
* **sieve:** add requirements for notify when doing a filter ([f53d7b7](https://github.com/Alinto/sogo/commit/f53d7b73db3d87a17a253bf98ee09a80052b3fd8))
* **view:** don't hide the option mailcomposer in small screen ([8a90773](https://github.com/Alinto/sogo/commit/8a907737de336f873047ecdc7d5c8ac07ae9e45b))
### Localization
* **nl:** Update Dutch translations ([031e117](https://github.com/Alinto/sogo/commit/031e117cbacf9d41caff43be19e639dee81b84dc))
* **pt:** Update Portuguese translations ([9649ae3](https://github.com/Alinto/sogo/commit/9649ae345f334d390860b0db70db8c3b621f513e))
* **tr_TR:** Update TurkishTurkey translations ([e9c03d3](https://github.com/Alinto/sogo/commit/e9c03d3cf7e668866ac757f72d87cdc4709c9d33))
## [5.12.1](https://github.com/Alinto/sogo/compare/SOGo-5.12.0...SOGo-5.12.1) (2025-04-30)
### Features
* **calendar:** Make the Jitsi meeting room prefix configurable ([4a68909](https://github.com/Alinto/sogo/commit/4a689090607bc6c4e6385202e9fdaef7308a94fe))
### Bug Fixes
* **calendar:** Fix typo Jisti × Jitsi ([ab9f0c1](https://github.com/Alinto/sogo/commit/ab9f0c1bf1f8930b9cbbf592efe69c68654f35c2))
* **calendar:** properly show the URL property in iCalendar ([eb7f551](https://github.com/Alinto/sogo/commit/eb7f551e88f2c6693354b6c3b617861d4b63a1bb))
* **calendar:** put valarm at the end of vevent block to satisfy outlook.office.com which is too lazy to follow the spec ([148fab9](https://github.com/Alinto/sogo/commit/148fab9409abf703f56ae8ed701547f23d685711))
* **db:** log an error if the table sogo_folder_info is malformed ([3616e56](https://github.com/Alinto/sogo/commit/3616e562894be63fcdb7007b5f172d99f5ff59eb))
* **folder:** fix folders sorting for special folders ([1f8dc12](https://github.com/Alinto/sogo/commit/1f8dc1292924672489a4a1c76f557a9f0bc65662))
* **iOS:** iOS 18.4 encodes caldav url and put %40 instead of @ for username. Fix that. ([9e431d0](https://github.com/Alinto/sogo/commit/9e431d08a273f519ff833b15189d2d3ebef4fc79))
* **ldap:** update baseDN with correct domain if needed ([ca425e6](https://github.com/Alinto/sogo/commit/ca425e6752fd28d68e35cc4c1b7dd6f7b3c43ab4))
* **sieve:** Correctly update default sieve filter for new users ([7c59e6b](https://github.com/Alinto/sogo/commit/7c59e6b3bf4684c2884e5f0bd39009719e5ba1b9))
* **smtp:** fix string type ([788ece4](https://github.com/Alinto/sogo/commit/788ece452b80a0270169178d5e596972797d0906))
* **tnef:** add protection against malformed winmail.dat ([7c6b674](https://github.com/Alinto/sogo/commit/7c6b6743e017effcc0078c9ebc026d628f4b71af))
* **typo:** Fix typo 'attachements' ([#367](https://bugs.sogo.nu/view.php?id=367)) ([4ec3d20](https://github.com/Alinto/sogo/commit/4ec3d2091a8f5c26b4c4463b4fa9c89ef26f63ba))
* **ui:** Change angular material to fix issues with Safari iOS 18+ ([068f1f3](https://github.com/Alinto/sogo/commit/068f1f315498c9bc5a0513e62c7a3fd250d974ad))
* **ui:** Change angular material to fix issues with Safari iOS 18+ ([07c7528](https://github.com/Alinto/sogo/commit/07c75280bb06816562b1fb5747acf8efa39cb4a0))
### Localization
* **ar:** Update Arabic translations ([5c48745](https://github.com/Alinto/sogo/commit/5c48745c94380bfa02f36c751b0389e06d676493))
* **bg_BG:** Update Bulgarian translations ([1d0b6a6](https://github.com/Alinto/sogo/commit/1d0b6a622c30b8f55ac17db0d131992a733d356b))
* **bs_BA:** Update Bosnian translations ([ade923d](https://github.com/Alinto/sogo/commit/ade923d21d409a9bffca609826a630480ea561dc))
* **ca:** Update Catalan translations ([8c4f56c](https://github.com/Alinto/sogo/commit/8c4f56c971002c4e0bb9764c0d7c24fc5cfd3a92))
* **cy:** Update Welsh translations ([54debcf](https://github.com/Alinto/sogo/commit/54debcf47d5896b58917705ff85bada391a26919))
* **es_AR:** Update SpanishArgentina translations ([4b397f6](https://github.com/Alinto/sogo/commit/4b397f68f51f00945cab57bdec77ab5d68e68753))
* **eu:** Update Basque translations ([54f8af3](https://github.com/Alinto/sogo/commit/54f8af30cefe4024d6697a35de25fb1d7896785e))
* **fi:** Update Finnish translations ([aae9e60](https://github.com/Alinto/sogo/commit/aae9e6065fda25803c37a43ca2e067df88aba166))
* **he:** Update Hebrew translations ([f4adf62](https://github.com/Alinto/sogo/commit/f4adf6267d271d91a75d2176424e98862662e9f8))
* **hr_HR:** Update Croatian translations ([961702d](https://github.com/Alinto/sogo/commit/961702de6706531817818063a4a96b40862aa05d))
* **hu:** Update Hungarian translations ([c31b3f4](https://github.com/Alinto/sogo/commit/c31b3f4592c6564c4f372cf4e63c4be3564c75bf))
* **id_ID:** Update Indonesian translations ([5f6168d](https://github.com/Alinto/sogo/commit/5f6168d5365450e40f6e289be694fab5585ba0cc))
* **is:** Update Icelandic translations ([c792ddb](https://github.com/Alinto/sogo/commit/c792ddb601830896cd936e21bb69c17f8d11e705))
* **it:** Update Italian translations ([d7fc5a5](https://github.com/Alinto/sogo/commit/d7fc5a52b8284594a6be4050b6c56cffc0fea472))
* **ja:** Update Japanese translations ([264f94b](https://github.com/Alinto/sogo/commit/264f94b64d5ea1147d45b7040fc5c3c0bc1d368b))
* **kk:** Update Kazakh translations ([1bf955d](https://github.com/Alinto/sogo/commit/1bf955d1930caf920c34be8ffb3e46b47eb0f232))
* **lt:** Update Lithuanian translations ([bbc0491](https://github.com/Alinto/sogo/commit/bbc04913b9a3219cd31cfcfc4b0ec79fbee76713))
* **lv:** Update Latvian translations ([1dcf475](https://github.com/Alinto/sogo/commit/1dcf475d3b0394474950eb73169fcfeab55305cf))
* **mk_MK:** Update Macedonian translations ([1ba83ec](https://github.com/Alinto/sogo/commit/1ba83ec57d7e65f6f28542a89222ec58570d5cef))
* **nl:** Update Dutch translations ([20e7c89](https://github.com/Alinto/sogo/commit/20e7c890dea0a52925b18be2a5ae09ccd4c23dd9))
* **nn_NO:** Update NorwegianNynorsk translations ([828f85c](https://github.com/Alinto/sogo/commit/828f85c7204c86951db4654f2d254a1361ad0d98))
* **ro_RO:** Update Romanian translations ([532b8dc](https://github.com/Alinto/sogo/commit/532b8dc2028b1a1db26566df7baa964daf9bd6b7))
* **sk:** Update Slovak translations ([d3e5a00](https://github.com/Alinto/sogo/commit/d3e5a004dbdd80c6bba405a34dcbbb28819a0fa7))
* **sl_SI:** Update Slovenian translations ([fdb88ab](https://github.com/Alinto/sogo/commit/fdb88abc1251a8763e4c8e5dacf38e3191180fba))
* **sr_ME@latin:** Update Montenegrin translations ([aa6575c](https://github.com/Alinto/sogo/commit/aa6575c42b54b27ca9510ea0f02426a7657ed519))
* **sr@latin:** Update SerbianLatin translations ([9f4440b](https://github.com/Alinto/sogo/commit/9f4440b07f142cc0c68561b4413e56fe5c5322ff))
* **sr:** Update Serbian translations ([3e61a52](https://github.com/Alinto/sogo/commit/3e61a52673092c38ce2f7a32ebd855afab61782a))
* **sv:** Update Swedish translations ([6d8d4f7](https://github.com/Alinto/sogo/commit/6d8d4f7c39fb1c1e9373a9268589b8e6546617ed))
* **zh_CN:** Update ChineseChina translations ([bbd17f5](https://github.com/Alinto/sogo/commit/bbd17f55310e5b2e163eeaaf2cb259ae042f8557))
* **zh_TW:** Update ChineseTaiwan translations ([3ac01f7](https://github.com/Alinto/sogo/commit/3ac01f70516a25f6db65ffa5c03d6d5a833479fe))
## [5.12.0](https://github.com/Alinto/sogo/compare/SOGo-5.11.2...SOGo-5.12.0) (2025-03-24)
+132 -275
View File
@@ -1,4 +1,5 @@
= Installation and Configuration Guide
Installation and Configuration Guide
====================================
////
This file is part of the SOGo project.
@@ -9,8 +10,8 @@
////
include::includes/global-attributes.asciidoc[]
== About this Guide
About this Guide
----------------
This guide will walk you through the installation and configuration of
the SOGo solution. It also covers the installation and configuration of
@@ -22,8 +23,8 @@ The instructions are based on version {release_version} of SOGo.
The latest version of this guide is available
at https://www.sogo.nu/support.html#/documentation.
== Introduction
Introduction
------------
SOGo is a free and modern scalable groupware server. It offers shared
calendars, address books, and emails through your favourite Web browser
@@ -47,8 +48,8 @@ device, or Outlook 2013/2016
SOGo is developed by a community of developers located mainly in North
America and Europe. More information can be found at https://sogo.nu/
=== Architecture and Compatibility
Architecture and Compatibility
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
image::images/architecture.png[System Architecture, 400, 964]
@@ -60,11 +61,11 @@ protocol are also supported.
To install and configure the Outlook CalDav Synchronizer, please refer
to the _Outlook Connector Configuration Guide_.
System Requirements
-------------------
== System Requirements
=== Assumptions
Assumptions
~~~~~~~~~~~
SOGo reuses many components in an infrastructure. Thus, it requires the
following:
@@ -103,8 +104,8 @@ components, together with version numbers:
More recent versions of the software mentioned above can also be used.
=== Minimum Hardware Requirements
Minimum Hardware Requirements
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The following table provides hardware recommendations for the server,
desktops and mobile devices:
@@ -156,8 +157,8 @@ Linux
Microsoft ActiveSync.
|=======================================================================
=== Operating System Requirements
Operating System Requirements
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The following operating systems are currently supported by SOGo:
@@ -181,19 +182,16 @@ directly to the SOGo website at https://sogo.nu/faq/installation.html.
Note that once the SOGo packages are installed under Debian and Ubuntu,
this guide can be followed in order to fully configure SOGo.
== Installation
Installation
------------
This section will guide you through the installation of SOGo together
with its dependencies. The steps described here apply to an RPM-based
installation for a Red Hat or CentOS 7 distribution. Most of these steps
should apply to all supported operating systems.
=== Software Downloads
**In order to access the community build, go to https://www.sogo.nu/download.html[SOGo website]
and select "Development (nightly builds)" at the bottom of the page.**
Software Downloads
~~~~~~~~~~~~~~~~~~
[NOTE]
In order to access the production builds, you need a proper support contract
@@ -254,8 +252,8 @@ sed -i '/enabled=1/a exclude=gnustep* ytnef*' /etc/yum.repos.d/epel.repo
For more information on EPEL, visit http://fedoraproject.org/wiki/EPEL/.
=== Software Installation
Software Installation
~~~~~~~~~~~~~~~~~~~~~
Once the yum configuration file has been created, you are now ready to
install SOGo and its dependencies. To do so, proceed with the following
@@ -275,8 +273,8 @@ for Oracle. The installation command will thus look like this:
Once completed, SOGo will be fully installed on your server. You are now
ready to configure it.
== Configuration
Configuration
-------------
In this section, you'll learn how to configure SOGo to use your existing
LDAP, SMTP and database servers. As previously mentioned, we assume that
@@ -284,8 +282,8 @@ those components run on the same server on which SOGo is being
installed. If this is not the case, please adjust the configuration
parameters to reflect those changes.
=== GNUstep Environment Overview
GNUstep Environment Overview
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SOGo makes use of the GNUstep environment. GNUstep is a free software
implementation of the OpenStep specification which provides many
@@ -318,8 +316,8 @@ wrapped within curly brackets `{ [configuration] }`. If SOGo refuses to
start due to syntax errors in its configuration file, `plparse` is helpful
for finding these, as it indicates the line containing the problem.
=== Preferences Hierarchy
Preferences Hierarchy
~~~~~~~~~~~~~~~~~~~~~
SOGo supports domain names segregation, meaning that you can separate
multiple groups of users within one installation of SOGo. A user
@@ -349,8 +347,8 @@ following abbreviations in the tables of this document:
Remember that the hierarchy paradigm allow the default value of a
parameter to be defined at a parent level.
=== General Preferences
General Preferences
~~~~~~~~~~~~~~~~~~~
The following table describes the general parameters that can be set:
@@ -492,12 +490,6 @@ the jitsi server used for creating the meeting link.
Defaults to `https://meet.jit.si` when unset.
|D |SOGoCalendarJitsiRoomPrefix
|Only used if SOGoCalendarEnableJitsiLink is set to YES. The string for
the Jitsi room prefix used for creating the meeting link.
Defaults to `SOGo_meeting/` when unset.
|S |SOGoSAML2PrivateKeyLocation
|The location of the SSL private key file on the filesystem that is used
by SOGo to sign and encrypt communications with the SAML2 identity
@@ -878,8 +870,8 @@ Obiously, if your users can connect without specifying a domain, let this parame
[[Secret-for-sensitive-data]]
=== Secret for sensitive data
Secret for sensitive data
~~~~~~~~~~~~~~~~~~~~~~~~~
_Since 5.10_
@@ -973,7 +965,8 @@ sogo-tool user-preferences unset default <user> AuxiliaryMailAccounts
user being the full mail address or just the username if domainless. After that, the user will have to set its auxiliary accounts again.
=== Authentication using LDAP
Authentication using LDAP
~~~~~~~~~~~~~~~~~~~~~~~~~
SOGo can use a LDAP server to authenticate users and, if desired, to
provide global address books. SOGo can also use an SQL backend for this
@@ -1383,8 +1376,8 @@ Defaults to `NO` when unset.
|=======================================================================
=== LDAP Attributes Indexing
LDAP Attributes Indexing
~~~~~~~~~~~~~~~~~~~~~~~~
To ensure proper performance of the SOGo application, the following LDAP
attributes must be fully indexed:
@@ -1400,8 +1393,8 @@ attributes must be fully indexed:
Please refer to the documentation of the software you use in order to
index those attributes.
=== LDAP Attributes Mapping
LDAP Attributes Mapping
~~~~~~~~~~~~~~~~~~~~~~~
Some LDAP attributes are mapped to contacts attributes in the SOGo UI.
The table below list most of them. It is possible to override these by
@@ -1461,8 +1454,8 @@ mapping = {
|Photo |photo
|===
=== Authenticating using C.A.S.
Authenticating using C.A.S.
~~~~~~~~~~~~~~~~~~~~~~~~~~~
SOGo natively supports C.A.S. authentication. For activating C.A.S.
authentication you need first to make sure that the
@@ -1565,8 +1558,8 @@ expected by pam_cas, otherwise it will fail to authenticate incoming
connection properly.
[[openid-section]]
=== Authenticating using OPENID
Authenticating using OPENID
~~~~~~~~~~~~~~~~~~~~~~~~~~~
SOGo natively supports OPENID authentication. For activating OpenId authentication you need first to make sure that
_SOGoAuthenticationType_ is set to `openid`,
@@ -1613,7 +1606,7 @@ Defaults to `0` when unset.
|S |SOGoOpenIdLogoutEnabled
|Allow user to end their openId with the webmail. Meaning that will disconnect them from
the others applicaitons as well. The openid server must have a end_session_endpoint.
the others applicaitons as well.
Defaults to `NO` when unset.
|=======================================================================
@@ -1639,8 +1632,8 @@ the session will stays in the table and be useless. That's why a new sogo-tool c
You can put it in a cron to do that periodicly. +
See _<<sogo-tool-clean-openid-sessions,sogo-tool clean-openid-sessions>>_.
=== Authenticating using SAML2
Authenticating using SAML2
~~~~~~~~~~~~~~~~~~~~~~~~~~
SOGo natively supports SAML2 authentication. Please refer to the documentation of your identity
provider and the SAML2 configuration keys that are listed above for proper setup. Once a SOGo
@@ -1673,7 +1666,8 @@ _NGImap4AuthMechanism_ is configured to use the `SAML` mechanism. If you
make use of the CrudeSAML PAM plugin, this value may be left empty.
=== Database Configuration
Database Configuration
~~~~~~~~~~~~~~~~~~~~~~
SOGo requires a relational database system in order to store
appointments, tasks and contacts information. It also uses the database
@@ -1920,9 +1914,10 @@ calendar.
Ensure the computer used for the test has emoji fonts installed.
[[Authentication-using-SQL]]
=== Authentication using SQL
Authentication using SQL
~~~~~~~~~~~~~~~~~~~~~~~~
SOGo can use a SQL-based database server for authentication. The
configuration is very similar to LDAP-based authentication.
@@ -2159,8 +2154,8 @@ or username@domain.tld
Note that groups are currently not supported for SQL-based
authentication sources.
=== SMTP Server Configuration
SMTP Server Configuration
~~~~~~~~~~~~~~~~~~~~~~~~~
SOGo makes use of a SMTP server to send emails from the Web interface,
iMIP/iTIP messages and various notifications.
@@ -2219,8 +2214,8 @@ Possible values are:
Defaults to `NO` when unset.
|=======================================================================
=== IMAP Server Configuration
IMAP Server Configuration
~~~~~~~~~~~~~~~~~~~~~~~~~
SOGo requires an IMAP server in order to let users consult their email
messages, manage their folders and more.
@@ -2388,8 +2383,8 @@ permissions for the individual user.
The default prefix is `$`. 
|=======================================================================
=== Web Interface Configuration
Web Interface Configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The following additional parameters only affect the Web interface
behaviour of SOGo.
@@ -2755,7 +2750,7 @@ Defaults to `inline` when unset.
|Show recipients or sender full email in mailboxes if set to `YES`. Default value is `NO`.
|U |SOGoMailHideInlineAttachments
|Hide attachments for inline images if set to `YES`. Default value is `NO`.
|Hide attachements for inline images if set to `YES`. Default value is `NO`.
|U |SOGoMailCustomFullName
@@ -2896,8 +2891,8 @@ SOGoMailJunkSettings = {
sent by SOGo. Defaults to `NO` when unset.
|=======================================================================
=== SOGo Configuration Summary
SOGo Configuration Summary
~~~~~~~~~~~~~~~~~~~~~~~~~~
The complete SOGo configuration file `/etc/sogo/sogo.conf` should look
like this:
@@ -2945,9 +2940,10 @@ like this:
}
----
[[multi-domains-configuration]]
=== Multi-domains Configuration
Multi-domains Configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you want your installation to isolate two groups of users, you must
define a distinct authentication source for each _domain_. Your domain keys
@@ -3043,8 +3039,8 @@ Defaults to an empty array, which means domains are isolated from each
other.
|=======================================================================
=== Apache Configuration
Apache Configuration
~~~~~~~~~~~~~~~~~~~~
The SOGo configuration for Apache is located in
`/etc/httpd/conf.d/SOGo.conf`.
@@ -3073,8 +3069,8 @@ The default configuration will use `mod_proxy` and `mod_headers` to
relay requests to the `sogod` parent process. This is suitable for small
to medium deployments.
=== Starting Services
Starting Services
~~~~~~~~~~~~~~~~~
Once SOGo if fully installed and configured, start the services using
the following command:
@@ -3090,8 +3086,8 @@ modules and configuration files were added:
Finally, you should also make sure that the `memcached` service is
started and that it is also automatically started at boot time.
=== _Cronjob_ — EMail reminders
_Cronjob_ — EMail reminders
~~~~~~~~~~~~~~~~~~~~~~~~~~~
SOGo allows you to set email-based reminders for events and tasks. To
enable this, you must enable the `SOGoEnableEMailAlarms` preference and
@@ -3118,8 +3114,8 @@ SOGoSMTPMasterUserUsername = "foo";
SOGoSMTPMasterUserPassword = "bar";
```
=== _Cronjob_ — Vacation messages activation and expiration
_Cronjob_ — Vacation messages activation and expiration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When vacation messages are enabled (see the parameter
_SOGoVacationEnabled_), users can set an activation or expiration date
@@ -3140,8 +3136,8 @@ The _cronjob_ should look like this:
0 0 * * * sogo /usr/sbin/sogo-tool update-autoreply -p /etc/sogo/sieve.creds
----
=== _Password_ — Force user password change at login
_Password_ — Force user password change at login
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The following commands force user to change his password at login :
@@ -3155,11 +3151,11 @@ Disable :
/usr/sbin/sogo-tool user-preferences unset settings [USER] ForceResetPassword
----
Managing User Accounts
----------------------
== Managing User Accounts
=== Creating the SOGo Administrative Account
Creating the SOGo Administrative Account
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
First, create the SOGo administrative account in your LDAP server. The
following LDIF file (`sogo.ldif`) can be used as an example:
@@ -3186,8 +3182,8 @@ administrative account using the following command:
ldappasswd -h 127.0.0.1 -x -w qwerty -D cn=Manager,dc=acme,dc=com uid=sogo,ou=users,dc=acme,dc=com -s qwerty
=== Creating a User Account
Creating a User Account
~~~~~~~~~~~~~~~~~~~~~~~
SOGo uses LDAP directories to authenticate users. Use the following LDIF
file (`jdoe.ldif`) as an example to create a SOGo user account:
@@ -3220,8 +3216,8 @@ easier. These GUI utilities can make use of templates to create and
pre-configure typical user accounts or any standardized LDAP record,
along with the correct object classes, fields and default values.
=== Microsoft Enterprise ActiveSync
Microsoft Enterprise ActiveSync
-------------------------------
SOGo supports the Microsoft ActiveSync protocol.
@@ -3357,8 +3353,8 @@ and send an email to iplicreq@microsoft.com
Alinto provides this software for free, but is not responsible for
anything related to its usage.
== Microsoft Enterprise ActiveSync Tuning
Microsoft Enterprise ActiveSync Tuning
--------------------------------------
First of all, it is important to know that most EAS devices will keep
HTTP connections open to SOGo (and thus, Apache) for a long time. This
@@ -3433,8 +3429,8 @@ SOGoMaximumSyncInterval = 3540;
SOGoInternalSyncInterval = 60;
----
== S/MIME Support in SOGo
S/MIME Support in SOGo
----------------------
SOGo supports S/MIME email signing and encryption. When receiving
S/MIME signed emails, SOGo automatically extracts the PKCS (Public-Key
@@ -3452,10 +3448,11 @@ please have a look at Actalis:
https://www.actalis.it/products/certificates-for-secure-electronic-mail.aspx
== Using SOGo
Using SOGo
----------
=== SOGo Web Interface
SOGo Web Interface
~~~~~~~~~~~~~~~~~~
To acces the SOGo Web Interface, point your Web browser, which is
running from the same server where SOGo was installed, to the following
@@ -3464,8 +3461,8 @@ URL: http://127.0.0.1/SOGo.
Log in using the "jdoe" user and the "qwerty" password. The underlying
database tables will automatically be created by SOGo.
=== Mozilla Thunderbird and Lightning
Mozilla Thunderbird and Lightning
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Alternatively, you can access SOGo with a GroupDAV and a CalDAV client.
A typical well-integrated setup is to use Mozilla Thunderbird and
@@ -3507,8 +3504,8 @@ To access your personal calendar:
`http://127.0.0.1/SOGo/dav/jdoe/Calendar/personal/`
* Click on Continue.
=== Apple Calendar (macOS, iOS, iPadOS)
Apple Calendar (macOS, iOS, iPadOS)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Apple Calendar on macOS, iOS, and iPadOS can also be used
as a client application for SOGo.
@@ -3526,8 +3523,8 @@ You have to add the profile to your device :
* Go to Settings / Profiles / Click on + / Continue
* Type your SOGo account password and click on install
=== Apple AddressBook
Apple AddressBook
~~~~~~~~~~~~~~~~~
Since Mac OS X 10.6 (Snow Leopard), Apple AddressBook can be configured
to use SOGo.
@@ -3575,8 +3572,8 @@ You have to add the profile to your device :
* Go to Settings / Profiles / Click on + / Continue
* Type your SOGo account password and click on install
=== Microsoft ActiveSync
Microsoft ActiveSync
~~~~~~~~~~~~~~~~~~~~
You can synchronize contacts, emails, events and tasks from SOGo with
any mobile devices that support Microsoft ActiveSync. Microsoft Outlook
@@ -3585,9 +3582,8 @@ any mobile devices that support Microsoft ActiveSync. Microsoft Outlook
The Microsoft ActiveSync server URL is generally something
like: `http://127.0.0.1/Microsoft-Server-ActiveSync`.
== Using sogo-tool
Using sogo-tool
---------------
The command _sogo-tool_ allows to do some operations on database and sieve filter. It is included with
the sogo package on Debian/Ubuntu but must be installed manually on RHEl/CentOS:
@@ -3596,8 +3592,8 @@ the sogo package on Debian/Ubuntu but must be installed manually on RHEl/CentOS:
*_WARNING_: Use sogo-tool with full awareness of what you are doing. This is an admin tool that can cause loss of data
or completely make the webmail unusable by a user.*
=== sogo-tool backup/restore
sogo-tool backup/restore
~~~~~~~~~~~~~~~~~~~~~~~~
The backup tool saves the information of a user into a file. The information saved are its preferences, its events and its contacts.
@@ -3678,8 +3674,8 @@ sogo-tool restore -F "Contacts/personal" tmp/backup user1
sogo-tool restore -F "Calendar/60C8-65323D80-7-4D9F7D80" tmp/backup user1
----
=== sogo-tool checkup
sogo-tool checkup
~~~~~~~~~~~~~~~~~
Check the events and contacts data's integrity of a user
@@ -3710,8 +3706,8 @@ sogo-tool checkup -d user1
[[sogo-tool-clean-openid-sessions]]
=== sogo-tool clean-openid-sessions
sogo-tool clean-openid-sessions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Obviously only usefull if you have set SOGo with openId authentication.
Will clean all expired openId sessions from the database.
@@ -3723,8 +3719,8 @@ Example:
sogo-tool clean-openid-sessions
----
=== sogo-tool cleanup
sogo-tool cleanup
~~~~~~~~~~~~~~~~~
Will purge all user's deleted events and contacts which the deletion is older than a number of days.
@@ -3748,7 +3744,8 @@ Purged 5 records from folder /Users/user2/Calendar/personal
----
=== sogo-tool create-folder
sogo-tool create-folder
~~~~~~~~~~~~~~~~~~~~~~~
Create a folder (Calendar or Address Book) for a user.
@@ -3765,7 +3762,8 @@ sogo-tool create-folder user2 Calendar Pro_Calendar
----
=== sogo-tool dump-defaults
sogo-tool dump-defaults
~~~~~~~~~~~~~~~~~~~~~~~
Output all current defaults value of GNUstep and SOGo (sogo.conf)
@@ -3782,8 +3780,8 @@ sogo-tool dump-defaults all
sogo-tool dump-defaults -f /tmp/foo/conf.xml
----
=== sogo-tool expire-sessions
sogo-tool expire-sessions
~~~~~~~~~~~~~~~~~~~~~~~~~
Expires user sessions from database without activity for specified number of minutes. When a user log in to sogo for
the first time, sogo will create a entry in database's table OCSSessionsFolderURL with the user's information. Sogo will
@@ -3802,8 +3800,8 @@ sogo-tool expire-sessions 160 #Will remove session which last activity is ol
sogo-tool expire-sessions 0 #Will remove session which last activity is older than 0 minutes.
----
=== sogo-tool manage-acl
sogo-tool manage-acl
~~~~~~~~~~~~~~~~~~~~
Manage access-control list (ACL) of a user for folders (Calendar and Address Book).
@@ -3874,8 +3872,8 @@ Example:
sogo-tool manage-acl unsubscribe sogo-tests1 Contacts/5E1D-653FC400-1-1A330C40 sogo-tests2
----
=== sogo-tool manage-eas
sogo-tool manage-eas
~~~~~~~~~~~~~~~~~~~~
Manage EAS folders
@@ -3905,8 +3903,8 @@ sogo-tool manage-eas mergevcard janedow androidc316986417 YES
sogo-tool manage-eas mergevevent janedow androidc316986417 YES
----
=== sogo-tool remove
sogo-tool remove
~~~~~~~~~~~~~~~~
Remove all folders (Calendar and Address Book) and Preference settings of a user. The personal Calendar and Address Book
will stay but be emptied of all entries. The Preferences will go back to defaults values as for a new user.
@@ -3930,8 +3928,8 @@ Deleting /Users/user2/Contacts/B087-65363400-7-374DA380
Deleting /Users/user2/Contacts/personal
----
=== sogo-tool remove-doubles
sogo-tool remove-doubles
~~~~~~~~~~~~~~~~~~~~~~~~
remove duplicate contacts from the specified user addressbook
@@ -3956,8 +3954,8 @@ sogo-tool remove-doubles user1 personal
sogo-tool remove-doubles user2 489-65376E80-1-2D2BA000
----
=== sogo-tool rename-user
sogo-tool rename-user
~~~~~~~~~~~~~~~~~~~~~
Update records pertaining to a user after a change of user id. Will change all folders path, subscriptions from others users,
all mention of user id in database.
@@ -3975,8 +3973,8 @@ Example:
sogo-tool rename-user old_username new_username
----
=== sogo-tool truncate-calendar
sogo-tool truncate-calendar
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Remove old calendar entries from the specified user calendar.
@@ -4006,7 +4004,8 @@ Removing 1 records...
Removed 1 records.
----
=== sogo-tool update-autoreply
sogo-tool update-autoreply
~~~~~~~~~~~~~~~~~~~~~~~~~~
_This command is only useful if your sieve server doesn't have the capabilities *date* or *relational*_
In that case this command will check vacation's setting of all the users. If vacation is enabled/disabled,
@@ -4026,7 +4025,8 @@ Example:
[[sogo-tool-update-secret]]
=== sogo-tool update-secret
sogo-tool update-secret
~~~~~~~~~~~~~~~~~~~~~~~
_New in 5.10_
@@ -4052,7 +4052,8 @@ If your data are already encrypted with 'oldSecret' but you want to decrypt them
sogo-tool update-secret -o oldSecret
----
=== sogo-tool user-preferences
sogo-tool user-preferences
~~~~~~~~~~~~~~~~~~~~~~~~~~
Get, set or unset user defaults / settings in the database.
@@ -4111,153 +4112,9 @@ sogo-tool user-preferences set defaults user1 Forward -f /path/filename -p cred
----
== API REST
Starting from 5.12.2, an API with two endpoints is available.
All response will be on json format. In case of errors, the message will be:
```json
{"error" : "message"}
```
with an explicit message
=== API server url
_<scheme>_://_<domain>_/SOGo/SOGoAPI/_<action>_
• *scheme* : https or http
• *domain* : domain or ip:port of SOGo's server
• *action* : name of the action. No action does the Version action described below.
=== Authentication
Somes actions needs to be authenticated. There is two ways to do so:
* *Basic auth*
Add a header *Auhtorization* with value:
'Basic <credentials>'
*credentials* is the sitrng '<uid>:<password>' base64 encoded.
* *OpenID*
**Only works if you configure sogo to use openid. See <<openid-section, the related section>>**
Add a header *Auhtorization* with value:
'Bearer <access_token>'
*acces_token* is from the https://connect2id.com/products/server/docs/api/token#url[openid protocol]. It has to be fetch with the scope `openid`.
=== Action - Version
* *URL*: <scheme>://<domain>/SOGo/SOGoAPI/Version
* *Authentication needed*: NO
* *HTTP method allowed*: GET
* *Description*: Return the current version of sogo
* *Answer structure*:
```json
{
"major":5,
"patch":1,
"minor":12
}
```
=== Action - UserFolder
* *URL*: <scheme>://<domain>/SOGo/SOGoAPI/UserFolder
* *Authentication needed*: YES
* *HTTP method allowed*: GET
* *Description*: Return the caldav and cardav links of the authenticated user, plus their names.
* *Answer structure*:
```json
{
"calendar":[
{
"name":"DidyShared",
"url":"http://127.0.0.1/SOGo/dav/sogo-tests1@example.org/Calendar/12509-67F67D00-1-3105AF40"
},
{
"name":"LocalDidy",
"url":"http://127.0.0.1/SOGo/dav/sogo-tests1@example.org/Calendar/1BC38-67B60000-1-6E4B6880"
},
{
"name":"Personal Calendar",
"url":"http://127.0.0.1/SOGo/dav/sogo-tests1@example.org/Calendar/personal"
}
],
"username":"sogo-tests1@example.org",
"contact":[
{
"name":"Personal Address Book",
"url":"http://127.0.0.1/SOGo/dav/sogo-tests1@example.org/Contacts/personal"
}
]
}
```
* *Note*:
The Answer has 3 primary keys:
** *username*: uid of the user.
** *calendar*: array of calendar's folder info.
** *contact*: array of calendar's folder info.
A folder info is a dict with:
** *name*: Name of the folder.
** *url*: Dav link of the folder.
=== Errors and messages
Complete http response is `{“error:” : <message>}`.
*<action>* is the one used for the request: `<scheme>://<domain>/SOGo/SOGoAPI/<action>`
[%header,cols="1,3,3"]
|===
|Status |Message |Description
|400
|No backend API found for action: <action>
|The requested action doesn't exist
|500
|Can't alloc and init class: <classAction>
|The requested action exist but failed to be instantiated. <classAction> is the class's name
|400
|Method <method> not allowed for action <action>
|Http method not allowed. <methos> is the one used (GET, PUT, POST...)
|400
|Missing param <param> for action <action>
|Required param for action not found. <param> is the name of the missing parameter.
|401
|No authorization header found for <action>
|No Authorization header found
|401
|Authorization method incorrect: <auth> for action <action>
|Value of Authorization's header doesn't start with Basic or Bearer. <auth> is the full value.
|401
|User wrong login or not found for action <action>
|Fail to authenticate the user. Various causes possible: wrong login, wrong password, expired token...
|500
|Internal error during: <action>
|Internal error during the action
|===
To have more logs you can add `SOGoAPIDebugEnabled = YES;` to your sogo.conf.
== Upgrading
Upgrading
---------
This section describes what needs to be done when upgrading to the
current version of SOGo from the previous release.
@@ -12,8 +12,8 @@
////
== Additional Information
Additional Information
----------------------
For more information, please consult the online FAQs (Frequently Asked
Questions) :
@@ -12,7 +12,8 @@
////
== Commercial Support and Contact Information
Commercial Support and Contact Information
------------------------------------------
For any questions or comments, do not hesitate to contact us by writing
an email to contact@sogo.nu.
-1
View File
@@ -9,7 +9,6 @@ SUBPROJECTS = \
SoObjects \
Main \
UI \
API \
Tools \
Tests/Unit \
+4 -15
View File
@@ -380,15 +380,10 @@ static BOOL debugLeaks;
request = [_ctx request];
login = [SOGoUser getDecryptedUsernameIfNeeded: _key request: request];
if([login isEqualToString: @"changeLanguage"] || [login isEqualToString: @"login"])
if([login isEqualToString: @"changeLanguage"])
user = nil;
else
{
//Here the user is expected to be name or name@domain.com
//However iOS 18.4 sanitize the caldav url and put %40 instead of @
login = [login stringByReplacingOccurrencesOfString: @"%40" withString: @"@"];
user = [SOGoUser userWithLogin: login roles: nil];
}
if (user)
userFolder = [$(@"SOGoUserFolder") objectWithName: login
inContainer: self];
@@ -417,7 +412,7 @@ static BOOL debugLeaks;
{
id obj;
WORequest *request;
BOOL isDAVRequest, isAPIRequest;
BOOL isDAVRequest;
SOGoSystemDefaults *sd;
/* put locale info into the context in case it's not there */
@@ -425,14 +420,8 @@ static BOOL debugLeaks;
sd = [SOGoSystemDefaults sharedSystemDefaults];
request = [_ctx request];
isAPIRequest = [[request requestHandlerKey] isEqualToString:@"SOGoAPI"];
isDAVRequest = [[request requestHandlerKey] isEqualToString:@"dav"];
if(isAPIRequest && ![_key isEqualToString:@"SOGo"] && ![_key isEqualToString:@"SOGoAPI"])
{
//The request will be handle by the API Dispatcher
obj = nil;
}
else if (isDAVRequest || [sd isWebAccessEnabled])
if (isDAVRequest || [sd isWebAccessEnabled])
{
if (isDAVRequest)
{
@@ -462,7 +451,7 @@ static BOOL debugLeaks;
{
obj = [self lookupUser: _key inContext: _ctx];
if (!obj && ![_key isEqualToString: @"public"])
obj = [self lookupUser: @"anonymous" inContext: _ctx];
obj = [self lookupUser: @"anonymous" inContext: _ctx];
}
}
}
-1
View File
@@ -76,7 +76,6 @@
- (BOOL)folderExistsAtPath:(NSString *)_path;
- (NSArray *)listSubFoldersAtPath:(NSString *)_path recursive:(BOOL)_flag;
- (NSArray *)listSubFoldersAndNamesAtPath:(NSString *)_path recursive:(BOOL)_recursive;
- (NSDictionary *) recordAtPath: (NSString *) _path;
- (GCSFolder *)folderAtPath:(NSString *)_path;
-87
View File
@@ -598,27 +598,6 @@ static BOOL _singleStoreMode = NO;
return sql;
}
- (NSString *)generateSQLPathAndNameFetchForInternalNames:(NSArray *)_names
exactMatch:(BOOL)_beExact orDirectSubfolderMatch:(BOOL)_directSubs
{
/* fetches the 'path' subset for a given quick-names */
NSMutableString *sql;
NSString *ws;
ws = [self generateSQLWhereForInternalNames:_names
exactMatch:_beExact orDirectSubfolderMatch:_directSubs];
if ([ws length] == 0)
return nil;
sql = [NSMutableString stringWithCapacity:256];
[sql appendString:@"SELECT c_path, c_foldername FROM "];
[sql appendString:[self folderInfoTableName]];
[sql appendString:@" WHERE "];
[sql appendString:ws];
if (debugSQLGen) [self logWithFormat:@"PathFetch-SQL: %@", sql];
return sql;
}
/* handling folder names */
- (BOOL)_isStandardizedPath:(NSString *)_path {
@@ -827,72 +806,6 @@ static BOOL _singleStoreMode = NO;
return result;
}
- (NSArray *)listSubFoldersAndNamesAtPath:(NSString *)_path recursive:(BOOL)_recursive{
NSMutableArray *result;
NSString *fname;
NSArray *fnames, *records;
NSString *sql;
unsigned i, count;
if ((fnames = [self internalNamesFromPath:_path]) == nil) {
[self debugWithFormat:@"got no internal names for path: '%@'", _path];
return nil;
}
sql = [self generateSQLPathAndNameFetchForInternalNames:fnames
exactMatch:NO orDirectSubfolderMatch:(_recursive ? NO : YES)];
if ([sql length] == 0) {
[self debugWithFormat:@"got no SQL for names: %@", fnames];
return nil;
}
if ((records = [self performSQL:sql]) == nil) {
[self logWithFormat:@"ERROR(%s): executing SQL failed: '%@'",
__PRETTY_FUNCTION__, sql];
return nil;
}
if ((count = [records count]) == 0)
return emptyArray;
result = [NSMutableArray arrayWithCapacity:(count > 128 ? 128 : count)];
fname = [self internalNameFromPath:_path];
fname = [fname stringByAppendingString:@"/"]; /* add slash */
for (i = 0; i < count; i++) {
NSDictionary *record, *folderInfo;
NSString *sname, *spath, *foldername;
record = [records objectAtIndex:i];
sname = [record objectForKey:GCSPathRecordName];
if (![sname hasPrefix:fname]) /* does not match at all ... */
continue;
/* strip prefix and following slash */
sname = [sname substringFromIndex:[fname length]];
spath = [self pathPartFromInternalName:sname];
if (_recursive) {
if ([spath length] > 0){
foldername = [record objectForKey: @"c_foldername"];
folderInfo = [NSDictionary dictionaryWithObjectsAndKeys: spath, @"path", foldername, @"name", nil];
[result addObject:folderInfo];
}
}
else {
/* direct children only, so exclude everything with a slash */
if ([sname rangeOfString:@"/"].length == 0 && [spath length] > 0)
{
foldername = [record objectForKey: @"c_foldername"];
folderInfo = [NSDictionary dictionaryWithObjectsAndKeys: spath, @"path", foldername, @"name", nil];
[result addObject:folderInfo];
}
}
}
return result;
}
- (NSDictionary *) recordAtPath: (NSString *) _path
{
NSMutableString *sql;
+1 -1
View File
@@ -303,8 +303,8 @@ static NSString *openIdFolderURLString = nil;
newRecord = [NSDictionary dictionaryWithObjectsAndKeys: _user_session, @"c_user_session",
_old_session, @"c_old_session",
[NSNumber numberWithInt:now], @"c_session_started",
[NSNumber numberWithInt:nowExpire] , @"c_access_token_expires_in",
_refresh_token, @"c_refresh_token",
[NSNumber numberWithInt:nowExpire] , @"c_access_token_expires_in",
[NSNumber numberWithInt:nowRefreshExpire] , @"c_refresh_token_expires_in",
nil];
record = [self recordForSession: _user_session useOldSession: NO];
+15 -15
View File
@@ -214,11 +214,11 @@
{
static NSString *sqlFolderFormat
= (@"CREATE TABLE %@ ("
@" c_user_session TEXT NOT NULL,"
@" c_old_session TEXT NULL,"
@" c_user_session VARCHAR(4096) NOT NULL,"
@" c_old_session VARCHAR(4096) NULL,"
@" c_session_started INT4 NOT NULL,"
@" c_refresh_token TEXT NULL,"
@" c_access_token_expires_in INT4 NULL,"
@" c_refresh_token VARCHAR(4096) NULL,"
@" c_access_token_expires_in INT4 NOT NULL,"
@" c_refresh_token_expires_in INT4 NULL)");
return [NSString stringWithFormat: sqlFolderFormat, tableName];
@@ -274,7 +274,7 @@
static NSString *sqlFolderFormat
= (@"CREATE TABLE %@ ("
@" c_id VARCHAR(255) PRIMARY KEY,"
@" c_value TEXT NOT NULL,"
@" c_value VARCHAR(4096) NOT NULL,"
@" c_creationdate INT4 NOT NULL,"
@" c_lastseen INT4 NOT NULL)");
@@ -375,11 +375,11 @@
{
static NSString *sqlFolderFormat
= (@"CREATE TABLE %@ ("
@" c_user_session TEXT NOT NULL,"
@" c_old_session TEXT NULL,"
@" c_user_session VARCHAR(4096) NOT NULL,"
@" c_old_session VARCHAR(4096) NULL,"
@" c_session_started INT4 NOT NULL,"
@" c_refresh_token TEXT NULL,"
@" c_access_token_expires_in INT4 NULL,"
@" c_refresh_token VARCHAR(4096) NULL,"
@" c_access_token_expires_in INT4 NOT NULL,"
@" c_refresh_token_expires_in INT4 NULL)");
return [NSString stringWithFormat: sqlFolderFormat, tableName];
@@ -435,7 +435,7 @@
static NSString *sqlFolderFormat
= (@"CREATE TABLE %@ ("
@" c_id VARCHAR(255) PRIMARY KEY,"
@" c_value TEXT NOT NULL,"
@" c_value VARCHAR(4096) NOT NULL,"
@" c_creationdate INT NOT NULL,"
@" c_lastseen INT NOT NULL)");
@@ -536,11 +536,11 @@
{
static NSString *sqlFolderFormat
= (@"CREATE TABLE %@ ("
@" c_user_session VARCHAR2(8192) NOT NULL,"
@" c_old_session VARCHAR2(8192) NULL,"
@" c_user_session VARCHAR2(4096) NOT NULL,"
@" c_old_session VARCHAR2(4096) NULL,"
@" c_session_started INTEGER NOT NULL,"
@" c_refresh_token VARCHAR2(8192) NULL,"
@" c_access_token_expires_in INTEGER NULL,"
@" c_refresh_token VARCHAR2(4096) NULL,"
@" c_access_token_expires_in INTEGER NOT NULL,"
@" c_refresh_token_expires_in INTEGER NULL)");
return [NSString stringWithFormat: sqlFolderFormat, tableName];
@@ -595,7 +595,7 @@
static NSString *sqlFolderFormat
= (@"CREATE TABLE %@ ("
@" c_id VARCHAR2(255) PRIMARY KEY,"
@" c_value VARCHAR2(8192) NOT NULL,"
@" c_value VARCHAR2(4096) NOT NULL,"
@" c_creationdate INTEGER NOT NULL,"
@" c_lastseen INTEGER NOT NULL)");
+9 -17
View File
@@ -55,7 +55,7 @@
- (NSArray *) recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
{
NSMutableArray *ranges;
NSCalendarDate *firStart, *firEnd, *startDate, *endDate, *currentStartDate, *currentEndDate;
NSCalendarDate *firStart, *startDate, *endDate, *currentStartDate, *currentEndDate;
iCalByDayMask *dayMask;
long i, count, repeatCount;
unsigned interval;
@@ -63,7 +63,6 @@
//[self logWithFormat: @"Recurrence rule is %@", rrule];
firStart = [firstRange startDate];
firEnd = [firstRange endDate];
startDate = [_r startDate];
endDate = [_r endDate];
dayMask = nil;
@@ -85,41 +84,34 @@
// If rule is bound, check the bounds
if (![rrule isInfinite])
{
NSCalendarDate *until, *lastStartDate, *lastEndDate;
NSCalendarDate *until, *lastDate;
lastStartDate = nil;
lastEndDate = nil;
lastDate = nil;
until = [rrule untilDate];
if (until)
{
lastStartDate = until;
lastEndDate = until;
lastDate = until;
}
else
{
repeatCount = [rrule repeatCount];
if (dayMask == nil)
{
// If there's no day mask, we can compute the date of the last
// occurrence of the recurrent rule.
lastStartDate = [firStart dateByAddingYears: 0 months: 0
lastDate = [firStart dateByAddingYears: 0 months: 0
days: (interval
* (repeatCount - 1))];
lastEndDate = [firEnd dateByAddingYears: 0 months: 0
days: (interval
* (repeatCount - 1))];
}
}
if (lastStartDate != nil && lastEndDate != nil)
if (lastDate != nil)
{
if ([lastEndDate compare: startDate] == NSOrderedAscending)
if ([lastDate compare: startDate] == NSOrderedAscending)
// Range starts after last occurrence
return nil;
if ([lastStartDate compare: endDate] == NSOrderedAscending)
if ([lastDate compare: endDate] == NSOrderedAscending)
// Range ends after last occurence; adjust end date
endDate = lastStartDate;
endDate = lastDate;
}
}
+2 -7
View File
@@ -86,13 +86,8 @@
tzId = [self value: 0 ofAttribute: @"tzid"];
if ([tzId length])
{
//Hack, get timezone inf from sogo and not from the event VTIMEZONE as it can be incorrect/incomplete
timeZone = [iCalTimeZone timeZoneForName: tzId];
if(!timeZone)
{
calendar = (iCalCalendar *) [self searchParentOfClass: [iCalCalendar class]];
timeZone = [calendar timeZoneWithId: tzId];
}
calendar = (iCalCalendar *) [self searchParentOfClass: [iCalCalendar class]];
timeZone = [calendar timeZoneWithId: tzId];
//if (!timeZone)
//[self logWithFormat: @"timezone '%@' not found in calendar", tzId];
}
+9 -15
View File
@@ -185,7 +185,7 @@ static inline unsigned iCalDoWForNSDoW (int dow)
// TODO: check whether this is OK for multiday-events!
NSMutableArray *ranges;
NSTimeZone *timeZone;
NSCalendarDate *eventStartDate, *eventEndDate, *rStart, *rEnd, *untilStart, *untilEnd, *referenceDate;
NSCalendarDate *eventStartDate, *rStart, *rEnd, *until, *referenceDate;
int eventDayOfMonth;
unsigned monthIdxInRange, numberOfMonthsInRange, interval, repeatCount;
int diff, count;
@@ -199,14 +199,12 @@ static inline unsigned iCalDoWForNSDoW (int dow)
iCalByDayMask *byDayMask;
eventStartDate = [firstRange startDate];
eventEndDate = [firstRange endDate];
eventDayOfMonth = [eventStartDate dayOfMonth];
timeZone = [eventStartDate timeZone];
rStart = [_r startDate];
rEnd = [_r endDate];
interval = [rrule repeatInterval];
untilStart = nil;
untilEnd = nil;
until = nil;
repeatCount = [rrule repeatCount];
byMonth = [rrule byMonth];
byMonthDay = [rrule byMonthDay];
@@ -220,28 +218,24 @@ static inline unsigned iCalDoWForNSDoW (int dow)
{
// When there's no BYxxx mask, we can find the date of the last
// occurrence.
untilStart = [eventStartDate dateByAddingYears: 0
until = [eventStartDate dateByAddingYears: 0
months: (interval * (repeatCount - 1))
days: 0];
untilEnd = [eventEndDate dateByAddingYears: 0
months: (interval * (repeatCount - 1))
days: 0];
}
else
{
untilStart = [rrule untilDate];
untilEnd = [rrule untilDate];
until = [rrule untilDate];
}
}
if (untilStart != nil && untilEnd != nil)
if (until != nil)
{
if ([untilEnd compare: rStart] == NSOrderedAscending)
if ([until compare: rStart] == NSOrderedAscending)
// Range starts after last occurrence
return nil;
if ([untilStart compare: rEnd] == NSOrderedAscending)
if ([until compare: rEnd] == NSOrderedAscending)
// Range ends after last occurence; adjust end date
rEnd = untilStart;
rEnd = until;
}
if (byMonth && [byMonth count] > 0)
@@ -447,7 +441,7 @@ static inline unsigned iCalDoWForNSDoW (int dow)
start = [cursor dateByAddingYears: 0 months: 0 days: (dom - 1)];
doCont = [self _addInstanceWithStartDate: start
limitDate: untilStart
limitDate: until
limitRange: _r
toArray: ranges];
//NSLog(@"*** MONTHLY [%i/%i] adding %@%@ (count = %i)", dom, numDaysInMonth, start, (doCont?@"":@" .. NOT!"), count);
+9 -19
View File
@@ -64,7 +64,7 @@
- (NSArray *) recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
{
NSMutableArray *ranges;
NSCalendarDate *firStart, *firEnd, *startDate, *endDate, *currentStartDate, *currentEndDate;
NSCalendarDate *firStart, *startDate, *endDate, *currentStartDate, *currentEndDate;
long i, repeatCount, count;
unsigned interval;
iCalByDayMask *dayMask;
@@ -73,7 +73,6 @@
//[self logWithFormat: @"Recurrence rule is %@", rrule];
firStart = [firstRange startDate];
firEnd = [firstRange endDate];
startDate = [_r startDate];
endDate = [_r endDate];
dayMask = nil;
@@ -96,40 +95,31 @@
// If rule is bound, check the bounds
if (![rrule isInfinite])
{
NSCalendarDate *until, *lastStartDate, *lastEndDate;
NSCalendarDate *until, *lastDate;
lastStartDate = nil;
lastEndDate = nil;
lastDate = nil;
until = [rrule untilDate];
if (until)
{
lastStartDate = until;
lastEndDate = until;
}
lastDate = until;
else
{
repeatCount = [rrule repeatCount];
if (dayMask == nil)
{
// When there's no BYxxx mask, we can find the date of the last
// occurrence.
lastStartDate = [firStart dateByAddingYears: 0 months: 0
lastDate = [firStart dateByAddingYears: 0 months: 0
days: (interval
* (repeatCount - 1) * 7)];
lastEndDate = [firEnd dateByAddingYears: 0 months: 0
days: (interval
* (repeatCount - 1) * 7)];
}
}
if (lastStartDate != nil && lastEndDate != nil)
if (lastDate != nil)
{
if ([lastEndDate compare: startDate] == NSOrderedAscending)
if ([lastDate compare: startDate] == NSOrderedAscending)
// Range starts after last occurrence
return nil;
if ([lastStartDate compare: endDate] == NSOrderedAscending)
if ([lastDate compare: endDate] == NSOrderedAscending)
// Range ends after last occurence; adjust end date
endDate = [lastStartDate addTimeInterval: [firstRange duration]];
endDate = [lastDate addTimeInterval: [firstRange duration]];
}
}
+9 -17
View File
@@ -44,14 +44,13 @@
{
NSMutableArray *ranges;
NSArray *byMonth;
NSCalendarDate *firStart, *firEnd, *lastStartDate, *lastEndDate, *rStart, *rEnd, *until, *referenceDate, *rTemp;
NSCalendarDate *firStart, *lastDate, *rStart, *rEnd, *until, *referenceDate, *rTemp;
NSInteger *hoursOfOffset;
iCalMonthlyRecurrenceCalculator *monthlyCalc;
unsigned j, yearIdxInRange, numberOfYearsInRange, count, interval, monthDiff;
int diff, repeatCount, currentMonth, origNbDaysInMonth;
firStart = [firstRange startDate];
firEnd = [firstRange endDate];
rStart = [_r startDate];
rEnd = [_r endDate];
interval = [rrule repeatInterval];
@@ -72,40 +71,33 @@
// If rule is bound, check the bounds
if (![rrule isInfinite])
{
lastStartDate = nil;
lastEndDate = nil;
lastDate = nil;
until = [rrule untilDate];
repeatCount = [rrule repeatCount];
if (until)
{
lastStartDate = until;
lastEndDate = until;
lastDate = until;
}
if (repeatCount > 0)
{
if (lastStartDate == nil && ![rrule hasByMask])
{
if (lastDate == nil && ![rrule hasByMask])
// When there's no BYxxx mask, we can find the date of the last
// occurrence.
lastStartDate = [firStart dateByAddingYears: (interval * (repeatCount - 1))
lastDate = [firStart dateByAddingYears: (interval * (repeatCount - 1))
months: 0
days: 0];
lastEndDate = [firEnd dateByAddingYears: (interval * (repeatCount - 1))
months: 0
days: 0];
}
referenceDate = firStart;
}
if (lastStartDate != nil)
if (lastDate != nil)
{
if ([lastEndDate compare: rStart] == NSOrderedAscending)
if ([lastDate compare: rStart] == NSOrderedAscending)
// Range starts after last occurrence
return nil;
if ([lastStartDate compare: rEnd] == NSOrderedAscending)
if ([lastDate compare: rEnd] == NSOrderedAscending)
// Range ends after last occurence; adjust end date
rEnd = [lastStartDate addTimeInterval: [firstRange duration]];
rEnd = [lastDate addTimeInterval: [firstRange duration]];
}
}
+5 -5
View File
@@ -142,7 +142,7 @@ CREATE TABLE sogo_quick_contact (
CREATE TABLE sogo_sessions_folder (
c_id varchar(255) NOT NULL,
c_value text NOT NULL,
c_value varchar(4096) NOT NULL,
c_creationdate int(11) NOT NULL,
c_lastseen int(11) NOT NULL,
PRIMARY KEY (c_id)
@@ -166,11 +166,11 @@ CREATE TABLE sogo_admin (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
CREATE TABLE sogo_opend_id (
c_user_session text NOT NULL,
c_old_session text DEFAULT '',
c_user_session varchar(4096) NOT NULL,
c_old_session varchar(4096) DEFAULT '',
c_session_started int(11) NOT NULL,
c_refresh_token text DEFAULT '',
c_access_token_expires_in int(11) DEFAULT '',
c_refresh_token varchar(4096) DEFAULT '',
c_access_token_expires_in int(11) NOT NULL,
c_refresh_token_expires_in int(11) DEFAULT NULL,
PRIMARY KEY (c_user_session)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
@@ -1,82 +0,0 @@
"Inviting the following persons is prohibited:" = "Inviting the following persons is prohibited:";
"Personal Calendar" = "Personal Calendar";
vevent_class0 = "(Public event)";
vevent_class1 = "(Private event)";
vevent_class2 = "(Confidential event)";
vtodo_class0 = "(Public task)";
vtodo_class1 = "(Private task)";
vtodo_class2 = "(Confidential task)";
/* Receipts */
"The event \"%{Summary}\" was created" = "The event \"%{Summary}\" was created";
"The event \"%{Summary}\" was deleted" = "The event \"%{Summary}\" was deleted";
"The event \"%{Summary}\" was updated" = "The event \"%{Summary}\" was updated";
"The task \"%{Summary}\" was created" = "The task \"%{Summary}\" was created";
"The task \"%{Summary}\" was deleted" = "The task \"%{Summary}\" was deleted";
"The task \"%{Summary}\" was updated" = "The task \"%{Summary}\" was updated";
"The following attendees(s) were notified" = "The following attendee(s) were notified";
"The following attendees(s) were added" = "The following attendee(s) were added";
"The following attendees(s) were removed" = "The following attendee(s) were removed";
/* IMIP messages */
"calendar_label" = "Calendar";
"startDate_label" = "Start";
"endDate_label" = "End";
"time_label" = "Time";
"to_label" = "to";
"due_label" = "Due Date";
"location_label" = "Location";
"summary_label" = "Summary";
"comment_label" = "Comment";
"organizer_label" = "Organizer";
"attendee_label" = "Attendee";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Event Invitation: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(sent by %{SentBy}) ";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Event Cancelled: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "The appointment \"%{Summary}\" for the %{OldStartDate} has changed";
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
= "The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed";
"The following parameters have changed in the \"%{Summary}\" meeting:"
= "The following parameters have changed in the \"%{Summary}\" meeting:";
"Please accept or decline those changes."
= "Please accept or decline those changes.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "Accepted invitation: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "Declined invitation: \"%{Summary}\"";
"Delegated invitation: \"%{Summary}\"" = "Delegated invitation: \"%{Summary}\"";
"Not yet decided on invitation: \"%{Summary}\"" = "Not yet decided on invitation: \"%{Summary}\"";
"%{Attendee} %{SentByText}has accepted your event invitation."
= "%{Attendee} %{SentByText}has accepted your event invitation.";
"%{Attendee} %{SentByText}has declined your event invitation."
= "%{Attendee} %{SentByText}has declined your event invitation.";
"%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}."
= "%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText}has not yet decided upon your event invitation.";
/* Reminder */
"Reminder: \"%{Summary}\" - %{StartDate}" = "Reminder: \"%{Summary}\" - %{StartDate}";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Cannot access resource: \"%{Cn} %{SystemEmail}\"";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}.";
/* Participation role */
"Your participation is required to this event" = "Your participation is required to this event";
"Your participation is optional to this event" = "Your participation is optional to this event";
"Your participation is not required to this event" = "Your participation is not required to this event";
@@ -1,82 +0,0 @@
"Inviting the following persons is prohibited:" = "다음 사람들을 초대하는 것은 금지되어 있습니다:";
"Personal Calendar" = "개인 캘린더";
vevent_class0 = "(공개 이벤트)";
vevent_class1 = "(비공개 이벤트)";
vevent_class2 = "(기밀 이벤트)";
vtodo_class0 = "(공개 할 일)";
vtodo_class1 = "(비공개 할 일)";
vtodo_class2 = "(기밀 할 일)";
/* Receipts */
"The event \"%{Summary}\" was created" = "이벤트 \"%{Summary}\"가 생성되었습니다";
"The event \"%{Summary}\" was deleted" = "이벤트 \"%{Summary}\"가 삭제되었습니다";
"The event \"%{Summary}\" was updated" = "이벤트 \"%{Summary}\"가 업데이트되었습니다";
"The task \"%{Summary}\" was created" = "할 일 \"%{Summary}\"이 생성되었습니다";
"The task \"%{Summary}\" was deleted" = "할 일 \"%{Summary}\"이 삭제되었습니다";
"The task \"%{Summary}\" was updated" = "할 일 \"%{Summary}\"이 업데이트되었습니다";
"The following attendees(s) were notified" = "다음 참석자에게 알림이 전송되었습니다";
"The following attendees(s) were added" = "다음 참석자가 추가되었습니다";
"The following attendees(s) were removed" = "다음 참석자가 제거되었습니다";
/* IMIP messages */
"calendar_label" = "캘린더";
"startDate_label" = "시작";
"endDate_label" = "종료";
"time_label" = "시간";
"to_label" = "까지";
"due_label" = "마감일";
"location_label" = "장소";
"summary_label" = "요약";
"comment_label" = "설명";
"organizer_label" = "주최자";
"attendee_label" = "참석자";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "이벤트 초대: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(%{SentBy}님이 보냄) ";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}님이 %{Summary}에 초대했습니다.\n\n시작: %{StartDate}\n종료: %{EndDate}\n설명: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}님이 %{Summary}에 초대했습니다.\n\n시작: %{StartDate} %{StartTime}\n종료: %{EndDate} %{EndTime}\n설명: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "이벤트 취소됨: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}님이 이 이벤트를 취소했습니다: %{Summary}.\n\n시작: %{StartDate}\n종료: %{EndDate}\n설명: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}님이 이 이벤트를 취소했습니다: %{Summary}.\n\n시작: %{StartDate} %{StartTime}\n종료: %{EndDate} %{EndTime}\n설명: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "%{OldStartDate}에 예정된 약속 \"%{Summary}\"이 변경되었습니다";
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
= "%{OldStartDate} %{OldStartTime}에 예정된 약속 \"%{Summary}\"이 변경되었습니다";
"The following parameters have changed in the \"%{Summary}\" meeting:"
= "모임 \"%{Summary}\"에서 다음 매개변수가 변경되었습니다:";
"Please accept or decline those changes."
= "이 변경 사항을 수락하거나 거절하십시오.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "초대 수락: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "초대 거절: \"%{Summary}\"";
"Delegated invitation: \"%{Summary}\"" = "초대 위임: \"%{Summary}\"";
"Not yet decided on invitation: \"%{Summary}\"" = "초대에 대해 아직 결정하지 않음: \"%{Summary}\"";
"%{Attendee} %{SentByText}has accepted your event invitation."
= "%{Attendee} %{SentByText}님이 귀하의 이벤트 초대를 수락했습니다.";
"%{Attendee} %{SentByText}has declined your event invitation."
= "%{Attendee} %{SentByText}님이 귀하의 이벤트 초대를 거절했습니다.";
"%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}."
= "%{Attendee} %{SentByText}님이 %{Delegate}에게 초대를 위임했습니다.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText}님이 귀하의 이벤트 초대에 대해 아직 결정하지 않았습니다.";
/* Reminder */
"Reminder: \"%{Summary}\" - %{StartDate}" = "알림: \"%{Summary}\" - %{StartDate}";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "리소스에 접근할 수 없습니다: \"%{Cn} %{SystemEmail}\"";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "리소스 \"%{Cn} %{SystemEmail}\"에 대한 최대 동시 예약 수(%{NumberOfSimultaneousBookings})에 도달했습니다. 충돌하는 이벤트는 \"%{EventTitle}\"이며, %{StartDate}에 시작합니다.";
/* Participation role */
"Your participation is required to this event" = "이 이벤트에 귀하의 참여가 필수입니다";
"Your participation is optional to this event" = "이 이벤트에 귀하의 참여는 선택 사항입니다";
"Your participation is not required to this event" = "이 이벤트에 귀하의 참여는 필요하지 않습니다";
@@ -1133,8 +1133,8 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
recordIndex = [self _indexOfRecordMatchingDate: recurrenceId inArray: ma];
if ([dateRange doesIntersectWithDateRange: recurrenceIdRange] && recordIndex == -1) {
[self warnWithFormat:
@"missing exception record for recurrence-id %@ (uid %@), dr = %@, rir = %@",
recurrenceId, [component uid], dateRange, recurrenceIdRange];
@"missing exception record for recurrence-id %@ (uid %@), ma = %@, dr = %@, rir = %@",
recurrenceId, [component uid], ma, dateRange, recurrenceIdRange];
}
if ([dateRange doesIntersectWithDateRange: recurrenceIdRange] && recordIndex != -1)
{
+10 -15
View File
@@ -65,9 +65,6 @@
#import "iCalRepeatableEntityObject+SOGo.h"
#import "SOGoCalendarComponent.h"
static NSArray *allowed_tags = nil;
@implementation SOGoCalendarComponent
+ (SOGoWebDAVAclManager *) webdavAclManager
@@ -213,6 +210,7 @@ static NSArray *allowed_tags = nil;
SOGoUser *calendarOwner;
NSEnumerator *children;
CardElement *element;
NSArray *tags;
int classification;
@@ -233,25 +231,22 @@ static NSArray *allowed_tags = nil;
summary = [self labelForKey: [NSString stringWithFormat: @"%@_class%d",
type, classification]
inContext: context];
if(!allowed_tags)
allowed_tags = [[NSArray arrayWithObjects: @"DTSTAMP", @"DTSTART", @"DTEND", @"DUE", @"EXDATE", @"EXRULE", @"RRULE", @"RECURRENCE-ID", nil] retain];
tags = [NSArray arrayWithObjects: @"DTSTAMP", @"DTSTART", @"DTEND", @"DUE", @"EXDATE", @"EXRULE", @"RRULE", @"RECURRENCE-ID", nil];
uid = [[component uid] asCryptedPassUsingScheme: @"ssha256"
withSalt: [[settings userPublicSalt] dataUsingEncoding: NSASCIIStringEncoding]
andEncoding: encHex
keyPath: nil];
children = [[component children] objectEnumerator];
NSMutableArray *to_remove = [NSMutableArray arrayWithCapacity: 8];
children = [[[[component children] copy] autorelease] objectEnumerator];
while ((element = [children nextObject]))
{
tag = [element tag];
if (![allowed_tags containsObject: [tag uppercaseString]])
[to_remove addObject: element];
if (![tags containsObject: [tag uppercaseString]])
[component removeChild: element];
}
[component removeChildren: to_remove];
[component setSummary: summary];
[component setUid: uid];
}
@@ -890,7 +885,7 @@ static NSArray *allowed_tags = nil;
mailDate = [[NSCalendarDate date] rfc822DateString];
[headerMap setObject: mailDate forKey: @"date"];
[headerMap setObject: subject forKey: @"subject"];
[headerMap setObject: [NSString generateMessageID: senderEmail] forKey: @"message-id"];
[headerMap setObject: [NSString generateMessageID] forKey: @"message-id"];
if ([msgType length] > 0)
[headerMap setObject: msgType forKey: @"x-sogo-message-type"];
msg = [NGMimeMessage messageWithHeader: headerMap];
@@ -979,7 +974,7 @@ static NSArray *allowed_tags = nil;
[headerMap setObject: mailDate forKey: @"date"];
[headerMap setObject: [[p getSubject] asQPSubjectString: @"UTF-8"]
forKey: @"subject"];
[headerMap setObject: [NSString generateMessageID: [[attendee mailAddress] pureEMailAddress]] forKey: @"message-id"];
[headerMap setObject: [NSString generateMessageID] forKey: @"message-id"];
[headerMap setObject: @"1.0" forKey: @"MIME-Version"];
if (textOnly)
@@ -1168,7 +1163,7 @@ static NSArray *allowed_tags = nil;
mailDate = [[NSCalendarDate date] rfc822DateString];
[headerMap setObject: mailDate forKey: @"date"];
[headerMap setObject: [page getSubject] forKey: @"subject"];
[headerMap setObject: [NSString generateMessageID: senderEmail] forKey: @"message-id"];
[headerMap setObject: [NSString generateMessageID] forKey: @"message-id"];
[headerMap setObject: @"1.0" forKey: @"MIME-Version"];
[headerMap setObject: @"text/html; charset=utf-8"
forKey: @"content-type"];
@@ -1,82 +0,0 @@
"Inviting the following persons is prohibited:" = " ไม่เชิญบุคคลต่อไปนี้:";
"Personal Calendar" = "ปฏิทินส่วนตัว";
vevent_class0 = "(กิจกรรมสาธารณะ)";
vevent_class1 = "(กิจกรรมส่วนตัว)";
vevent_class2 = "(กิจกรรมลับ)";
vtodo_class0 = "(งานสาธารณะ)";
vtodo_class1 = "(งานส่วนตัว)";
vtodo_class2 = "(งานลับ)";
/* Receipts */
"The event \"%{Summary}\" was created" = "กิจกรรม \"%{Summary}\" ถุกสร้างขึ้น";
"The event \"%{Summary}\" was deleted" = "กิจกรรม \"%{Summary}\" ถูกลบ";
"The event \"%{Summary}\" was updated" = "กิจกรรม \"%{Summary}\" ถูกอัพเดต";
"The task \"%{Summary}\" was created" = "งาน \"%{Summary}\" ถูกสร้างขี้น";
"The task \"%{Summary}\" was deleted" = "งาน \"%{Summary}\" ถูกลบ";
"The task \"%{Summary}\" was updated" = "งาน \"%{Summary}\" ถูกอัพเดต";
"The following attendees(s) were notified" = "ผู้เข้าร่วมต่อไปนี้ได้รับการแจ้งแล้ว";
"The following attendees(s) were added" = "ผู้เข้าร่วมต่อไปนี้ถูกเพิ่มเข้ามา";
"The following attendees(s) were removed" = "ผู้เข้าร่วมต่อไปนี้ถูกลบออก";
/* IMIP messages */
"calendar_label" = "ปฏิทิน";
"startDate_label" = "เริ่ม";
"endDate_label" = "สิ้นสุด";
"time_label" = "เวลา";
"to_label" = "ถึง";
"due_label" = "วันครบกำหนด";
"location_label" = "สถานที่";
"summary_label" = "สรุป";
"comment_label" = "ความคิดเห็น";
"organizer_label" = "ผู้จัดงาน";
"attendee_label" = "ผู้เข้าร่วม";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "คำเชิญร่วมกิจกรรม : \"%{Summary}\"";
"(sent by %{SentBy}) " = "(ส่งโดย %{SentBy}) ";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}ได้ทําการเชิญคุณไป %{Summary}.\n\nเริ่ม: %{StartDate}\nสิ้นสุด: %{EndDate}\nคำอธิบาย: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}ได้ทําการเชิญคุณไป %{Summary}.\n\nเริ่ม: %{StartDate} เวลา %{StartTime}\nสิ้นสุด: %{EndDate} เวลา %{EndTime}\nคำอธิบาย: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "กิจกรรมถูกยกเลิกแล้ว: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}:ได้ยกเลิกกิจกรรม %{Summary}.\n\nเริ่ม: %{StartDate}\nสิ้นสุด: %{EndDate}\nคำอธิบาย: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}hasได้ยกเลิกกิจกรรม: %{Summary}.\n\nเริ่ม: %{StartDate} เวลา %{StartTime}\nสิ้นสุด: %{EndDate} เวลา %{EndTime}\nคำอธิบาย: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "นัดหมาย \"%{Summary}\" สำหรับวันที่ %{OldStartDate} มีการเปลี่ยนแปลง";
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
= "นัดหมาย \"%{Summary}\" สำหรับวันที่ %{OldStartDate} เวลา %{OldStartTime} มีการเปลี่ยนแปลง";
"The following parameters have changed in the \"%{Summary}\" meeting:"
= "มีการเปลี่ยนแปลงใน \"%{Summary}\" :";
"Please accept or decline those changes."
= "กรุณาตอบรับหรือปฏิเสธการเปลี่ยนแปลงเหล่านั้น";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "ตอบรับคำเชิญ: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "ปฏิเสธคำเชิญ: \"%{Summary}\"";
"Delegated invitation: \"%{Summary}\"" = "ลบคำเชิญ: \"%{Summary}\"";
"Not yet decided on invitation: \"%{Summary}\"" = "ยังไม่ตัดสินใจเกี่ยวกับคำเชิญ: \"%{Summary}\"";
"%{Attendee} %{SentByText}has accepted your event invitation."
= "%{Attendee} %{SentByText} ได้ตอบรับคำเชิญกิจกรรมของคุณแล้ว";
"%{Attendee} %{SentByText}has declined your event invitation."
= "%{Attendee} %{SentByText} ได้ตอบปฏิเสธคำเชิญกิจกรรมของคุณแล้ว";
"%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}."
= "%{Attendee} %{SentByText}ได้มอบหมายการเชิญให้ %{Delegate}.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText}ยังไม่ได้ตัดสินใจเกี่ยวกับคำเชิญงานของคุณ";
/* Reminder */
"Reminder: \"%{Summary}\" - %{StartDate}" = "สิ่งเตือนความจำ: \"%{Summary}\" - %{StartDate}";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "ไม่สามารถเข้าถึงทรัพยากรได้: \"%{Cn} %{SystemEmail}\"";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "จำนวนการจองสูงสุดพร้อมกัน (%{NumberOfSimultaneousBookings}) ขอใช้ทรัพยากร \"%{Cn} %{SystemEmail}\". งานที่ขัดแย้งกัน \"%{EventTitle}\", and starts on %{StartDate}.";
/* Participation role */
"Your participation is required to this event" = "การเข้าร่วมของคุณจำเป็นสำหรับกิจกรรมนี้";
"Your participation is optional to this event" = " การเข้าร่วมกิจกรรมนี้ไม่จำเป็น";
"Your participation is not required to this event" = " การเข้าร่วมของคุณไม่จำเป็นสำหรับกิจกรรมนี้";
@@ -355,12 +355,12 @@ NSNumber *iCalDistantFutureNumber = nil;
if ([o isKindOfClass: [NSArray class]])
[self _setAttendees: o];
// o = [data objectForKey: @"alarm"];
// if ([o isKindOfClass: [NSDictionary class]])
// {
// owner = [data objectForKey: @"owner"];
// [self _setAlarm: o forOwner: owner];
// }
o = [data objectForKey: @"alarm"];
if ([o isKindOfClass: [NSDictionary class]])
{
owner = [data objectForKey: @"owner"];
[self _setAlarm: o forOwner: owner];
}
// Other attributes depend on the client object and therefore are set in [UIxComponentEditor setAttributes:]:
// - organizer & "created-by"
-11
View File
@@ -372,7 +372,6 @@
{
NSCalendarDate *aptStartDate, *aptEndDate, *allDayStartDate;
NSInteger offset, nbrDays;
NSString *owner;
NSTimeZone *timeZone;
iCalDateTime *startDate;
iCalTimeZone *tz;
@@ -455,16 +454,6 @@
}
}
}
//To make an ics conforme with outlook live (bouh), valarm must be the last element of vevent
//Try to set it here
o = [data objectForKey: @"alarm"];
if ([o isKindOfClass: [NSDictionary class]])
{
owner = [data objectForKey: @"owner"];
[self _setAlarm: o forOwner: owner];
}
}
- (iCalTimeZone *) adjustInContext: (WOContext *) context
-10
View File
@@ -107,7 +107,6 @@
BOOL isAllDayStartDate, isAllDayDueDate;
NSCalendarDate *startDate, *dueDate, *completedDate;
NSInteger percent;
NSString *owner;
SOGoUserDefaults *ud;
iCalDateTime *todoStartDate, *todoDueDate;
iCalTimeZone *tz;
@@ -231,15 +230,6 @@
}
else
[self setPercentComplete: @""];
//To make an ics conforme with outlook live (bouh), valarm must be the last element of vevent
//Try to set it here
o = [data objectForKey: @"alarm"];
if ([o isKindOfClass: [NSDictionary class]])
{
owner = [data objectForKey: @"owner"];
[self _setAlarm: o forOwner: owner];
}
}
- (NSMutableDictionary *) quickRecordFromContent: (NSString *) theContent
@@ -1,2 +0,0 @@
"Personal Address Book" = "Personal Address Book";
"Collected Address Book" = "Collected Address Book";
@@ -1,2 +0,0 @@
"Personal Address Book" = "개인 주소록";
"Collected Address Book" = "받은/보낸 주소록";
@@ -1,2 +0,0 @@
"Personal Address Book" = "สมุดที่อยู่ส่วนตัว";
"Collected Address Book" = "สมุดที่อยู่ที่เก็บรวบรวม";
-4
View File
@@ -94,8 +94,6 @@ Mailer_RESOURCE_FILES += \
SOGoMailJapaneseReply.wo \
SOGoMailKazakhForward.wo \
SOGoMailKazakhReply.wo \
SOGoMailKoreanForward.wo \
SOGoMailKoreanReply.wo \
SOGoMailLatvianForward.wo \
SOGoMailLatvianReply.wo \
SOGoMailLithuanianForward.wo \
@@ -130,8 +128,6 @@ Mailer_RESOURCE_FILES += \
SOGoMailSpanishArgentinaReply.wo \
SOGoMailSwedishForward.wo \
SOGoMailSwedishReply.wo \
SOGoMailThaiForward.wo \
SOGoMailThaiReply.wo \
SOGoMailTurkishTurkeyForward.wo \
SOGoMailTurkishTurkeyReply.wo \
SOGoMailUkrainianForward.wo \
@@ -1,5 +0,0 @@
"OtherUsersFolderName" = "Other Users";
"SharedFoldersName" = "Shared Folders";
"Report: Marked messages as junk" = "Report: Marked messages as junk";
"Report: Marked messages as not junk" = "Report: Marked messages as not junk";
@@ -1,5 +0,0 @@
"OtherUsersFolderName" = "다른 사용자";
"SharedFoldersName" = "공유 폴더";
"Report: Marked messages as junk" = "보고서: 스팸으로 표시된 메시지";
"Report: Marked messages as not junk" = "보고서: 스팸이 아닌 것으로 표시된 메시지";
+1 -1
View File
@@ -25,7 +25,7 @@
@interface NSString (SOGoExtension)
+ (NSString *) generateMessageID: (NSString *) mailOrDomain;
+ (NSString *) generateMessageID;
- (NSString *) htmlToText;
- (NSString *) htmlByExtractingImages: (NSMutableArray *) theImages;
- (NSString *) stringByConvertingCRLNToHTML;
+5 -17
View File
@@ -560,27 +560,15 @@
@implementation NSString (SOGoExtension)
+ (NSString *) generateMessageID: (NSString *) mailOrDomain
+ (NSString *) generateMessageID
{
NSMutableString *messageID;
NSString *_domain;
NSRange r;
NSString *pGUID;
messageID = [NSMutableString string];
[messageID appendFormat: @"<%@", [SOGoObject mailUniqueMessageId]];
if(mailOrDomain)
{
r = [mailOrDomain rangeOfString: @"@" options: NSBackwardsSearch];
if (r.location != NSNotFound)
{
//Its the full email not a domain
_domain = [mailOrDomain substringFromIndex: (r.location + r.length)];
}
else
_domain = mailOrDomain;
[messageID appendFormat: @"@%@>", _domain];
}
[messageID appendFormat: @"<%@", [SOGoObject globallyUniqueObjectId]];
pGUID = [[NSProcessInfo processInfo] globallyUniqueString];
[messageID appendFormat: @"@%u>", (unsigned int)[pGUID hash]];
return [messageID lowercaseString];
}
+1 -1
View File
@@ -222,7 +222,7 @@ static NSString *userAgent = nil;
messageID = [headers objectForKey: @"message-id"];
if (!messageID)
{
messageID = [NSString generateMessageID: [[newHeaders objectForKey: @"from"] pureEMailAddress]];
messageID = [NSString generateMessageID];
[headers setObject: messageID forKey: @"message-id"];
}
-4
View File
@@ -142,10 +142,6 @@
else
imapAuthMech = nil;
//If ActiveSync, force the plain mech
if([[[[self context] request] requestHandlerKey] isEqualToString:@"Microsoft-Server-ActiveSync"])
imapAuthMech = @"plain";
return [NGImap4ConnectionManager defaultConnectionManager: imapAuthMech];
}
-9
View File
@@ -91,9 +91,6 @@
@interface SOGoMailGermanForward : SOGoMailForward
@end
@interface SOGoMailGreekForward : SOGoMailForward
@end
@interface SOGoMailHebrewForward : SOGoMailForward
@end
@@ -115,9 +112,6 @@
@interface SOGoMailKazakhForward : SOGoMailForward
@end
@interface SOGoMailKoreanForward : SOGoMailForward
@end
@interface SOGoMailLatvianForward : SOGoMailForward
@end
@@ -169,9 +163,6 @@
@interface SOGoMailSwedishForward : SOGoMailForward
@end
@interface SOGoMailThaiForward : SOGoMailForward
@end
@interface SOGoMailTurkishTurkeyForward : SOGoMailForward
@end
-9
View File
@@ -345,9 +345,6 @@
@implementation SOGoMailGermanForward
@end
@implementation SOGoMailGreekForward
@end
@implementation SOGoMailHebrewForward
@end
@@ -369,9 +366,6 @@
@implementation SOGoMailKazakhForward
@end
@implementation SOGoMailKoreanForward
@end
@implementation SOGoMailLatvianForward
@end
@@ -423,9 +417,6 @@
@implementation SOGoMailSwedishForward
@end
@implementation SOGoMailThaiForward
@end
@implementation SOGoMailTurkishTurkeyForward
@end
@@ -1,15 +0,0 @@
<#newLine/><#newLine
/><#signaturePlacementOnTop><#signature/><#newLine/><#newLine/></#signaturePlacementOnTop
>-------- Αρχικό Μήνυμα --------<#newLine
/>Θέμα: <#subject/><#newLine
/>Ημερομηνία: <#date/><#newLine
/>Από: <#from/><#newLine
/><#hasReplyTo>Απάντηση-Προς: <#replyTo/><#newLine/></#hasReplyTo
><#hasOrganization>Οργανισμός: <#organization/><#newLine/></#hasOrganization
>Προς: <#to/><#newLine
/><#hasCc>Κοιν.: <#cc/><#newLine/></#hasCc
><#hasNewsGroups>Ομάδες ειδήσεων: <#newsgroups/><#newLine/></#hasNewsGroups
><#hasReferences>Αναφορές: <#references/><#newLine/></#hasReferences
><#newLine/><#newLine
/><#messageBody
/><#signaturePlacementOnBottom><#newLine/><#newLine/><#signature/></#signaturePlacementOnBottom>
@@ -1,88 +0,0 @@
subject: WOString {
value = subject;
escapeHTML = NO;
}
date: WOString {
value = date;
escapeHTML = NO;
}
from: WOString {
value = from;
escapeHTML = NO;
}
newLine: WOString {
value = newLine;
escapeHTML = NO;
}
hasReplyTo: WOConditional {
condition = hasReplyTo;
}
replyTo: WOString {
value = replyTo;
escapeHTML = NO;
}
hasOrganization: WOConditional {
condition = hasOrganization;
}
organization: WOString {
value = organization;
escapeHTML = NO;
}
to: WOString {
value = to;
escapeHTML = NO;
}
hasCc: WOConditional {
condition = hasCc;
}
cc: WOString {
value = cc;
escapeHTML = NO;
}
hasNewsGroups: WOConditional {
condition = hasNewsGroups;
}
newsgroups: WOString {
value = newsgroups;
escapeHTML = NO;
}
hasReferences: WOConditional {
condition = hasReferences;
}
references: WOString {
value = references;
escapeHTML = NO;
}
messageBody: WOString {
value = messageBody;
escapeHTML = NO;
}
signature: WOString {
value = signature;
escapeHTML = NO;
}
signaturePlacementOnTop: WOConditional {
condition = signaturePlacementOnTop;
}
signaturePlacementOnBottom: WOConditional {
condition = signaturePlacementOnTop;
negate = YES;
}
@@ -1,18 +0,0 @@
<#replyPlacementOnTop><#newLine/><#newLine/></#replyPlacementOnTop
><#signaturePlacementOnTop><#signature/><#newLine/><#newLine/></#signaturePlacementOnTop
><#outlookMode
>-------- Αρχικό Μήνυμα --------<#newLine
/>Θέμα: <#subject/><#newLine
/>Ημερομηνία: <#date/><#newLine
/>Από: <#from/><#newLine
/><#hasReplyTo>Απάντηση-Προς: <#replyTo/><#newLine/></#hasReplyTo
><#hasOrganization>Οργανισμός: <#organization/><#newLine/></#hasOrganization
>Προς: <#to/><#newLine
/><#hasCc>Κοιν.: <#cc/><#newLine/></#hasCc
><#hasNewsGroups>Ομάδες ειδήσεων: <#newsgroups/><#newLine/></#hasNewsGroups
><#hasReferences>Αναφορές: <#references/><#newLine/></#hasReferences
></#outlookMode
><#standardMode>Στις <#date/>, ο/η <#from/> έγραψε:</#standardMode
><#newLine/><#newLine/><#messageBody
/><#replyPlacementOnBottom><#newLine/><#newLine/></#replyPlacementOnBottom
><#signaturePlacementOnBottom><#newLine/><#signature/></#signaturePlacementOnBottom>
@@ -1,106 +0,0 @@
outlookMode: WOConditional {
condition = outlookMode;
}
standardMode: WOConditional {
condition = outlookMode;
negate = YES;
}
subject: WOString {
value = subject;
escapeHTML = NO;
}
date: WOString {
value = date;
escapeHTML = NO;
}
from: WOString {
value = from;
escapeHTML = NO;
}
newLine: WOString {
value = newLine;
escapeHTML = NO;
}
hasReplyTo: WOConditional {
condition = hasReplyTo;
}
replyTo: WOString {
value = replyTo;
escapeHTML = NO;
}
hasOrganization: WOConditional {
condition = hasOrganization;
}
organization: WOString {
value = organization;
escapeHTML = NO;
}
to: WOString {
value = to;
escapeHTML = NO;
}
hasCc: WOConditional {
condition = hasCc;
}
cc: WOString {
value = cc;
escapeHTML = NO;
}
hasNewsGroups: WOConditional {
condition = hasNewsGroups;
}
newsgroups: WOString {
value = newsgroups;
escapeHTML = NO;
}
hasReferences: WOConditional {
condition = hasReferences;
}
references: WOString {
value = references;
escapeHTML = NO;
}
messageBody: WOString {
value = messageBody;
escapeHTML = NO;
}
signature: WOString {
value = signature;
escapeHTML = NO;
}
replyPlacementOnTop: WOConditional {
condition = replyPlacementOnTop;
}
replyPlacementOnBottom: WOConditional {
condition = replyPlacementOnTop;
negate = YES;
}
signaturePlacementOnTop: WOConditional {
condition = signaturePlacementOnTop;
}
signaturePlacementOnBottom: WOConditional {
condition = signaturePlacementOnTop;
negate = YES;
}
@@ -1,15 +0,0 @@
<#newLine/><#newLine
/><#signaturePlacementOnTop><#signature/><#newLine/><#newLine/></#signaturePlacementOnTop
>-------- 원본 메시지 --------<#newLine
/>제목: <#subject/><#newLine
/>날짜: <#date/><#newLine
/>보낸 사람: <#from/><#newLine
/><#hasReplyTo>회신: <#replyTo/><#newLine/></#hasReplyTo>
<#hasOrganization>조직: <#organization/><#newLine/></#hasOrganization>
받는 사람: <#to/>
<#hasCc>참조: <#cc/><#newLine/></#hasCc>
<#hasNewsGroups>뉴스그룹: <#newsgroups/><#newLine/></#hasNewsGroups>
<#hasReferences>참조: <#references/><#newLine/></#hasReferences>
<#newLine/><#newLine
/><#messageBody
/><#signaturePlacementOnBottom><#newLine/><#newLine/><#signature/></#signaturePlacementOnBottom>
@@ -1,88 +0,0 @@
subject: WOString {
value = subject;
escapeHTML = NO;
}
date: WOString {
value = date;
escapeHTML = NO;
}
from: WOString {
value = from;
escapeHTML = NO;
}
newLine: WOString {
value = newLine;
escapeHTML = NO;
}
hasReplyTo: WOConditional {
condition = hasReplyTo;
}
replyTo: WOString {
value = replyTo;
escapeHTML = NO;
}
hasOrganization: WOConditional {
condition = hasOrganization;
}
organization: WOString {
value = organization;
escapeHTML = NO;
}
to: WOString {
value = to;
escapeHTML = NO;
}
hasCc: WOConditional {
condition = hasCc;
}
cc: WOString {
value = cc;
escapeHTML = NO;
}
hasNewsGroups: WOConditional {
condition = hasNewsGroups;
}
newsgroups: WOString {
value = newsgroups;
escapeHTML = NO;
}
hasReferences: WOConditional {
condition = hasReferences;
}
references: WOString {
value = references;
escapeHTML = NO;
}
messageBody: WOString {
value = messageBody;
escapeHTML = NO;
}
signature: WOString {
value = signature;
escapeHTML = NO;
}
signaturePlacementOnTop: WOConditional {
condition = signaturePlacementOnTop;
}
signaturePlacementOnBottom: WOConditional {
condition = signaturePlacementOnTop;
negate = YES;
}
@@ -1,20 +0,0 @@
<#replyPlacementOnTop><#newLine/><#newLine/></#replyPlacementOnTop>
<#signaturePlacementOnTop><#signature/><#newLine/><#newLine/></#signaturePlacementOnTop>
<#outlookMode>
-------- 원본 메시지 --------
제목: <#subject/><#newLine/>
날짜: <#date/><#newLine/>
보낸 사람: <#from/><#newLine/>
<#hasReplyTo>회신: <#replyTo/><#newLine/></#hasReplyTo>
<#hasOrganization>조직: <#organization/><#newLine/></#hasOrganization>
받는 사람: <#to/><#newLine/>
<#hasCc>참조: <#cc/><#newLine/></#hasCc>
<#hasNewsGroups>뉴스그룹: <#newsgroups/><#newLine/></#hasNewsGroups>
<#hasReferences>참조: <#references/><#newLine/></#hasReferences>
</#outlookMode>
<#standardMode>
<#date/>에 <#from/>(이)가 작성했습니다:
</#standardMode>
<#newLine/><#newLine/><#messageBody/>
<#replyPlacementOnBottom><#newLine/><#newLine/></#replyPlacementOnBottom>
<#signaturePlacementOnBottom><#newLine/><#signature/></#signaturePlacementOnBottom>
@@ -1,106 +0,0 @@
outlookMode: WOConditional {
condition = outlookMode;
}
standardMode: WOConditional {
condition = outlookMode;
negate = YES;
}
subject: WOString {
value = subject;
escapeHTML = NO;
}
date: WOString {
value = date;
escapeHTML = NO;
}
from: WOString {
value = from;
escapeHTML = NO;
}
newLine: WOString {
value = newLine;
escapeHTML = NO;
}
hasReplyTo: WOConditional {
condition = hasReplyTo;
}
replyTo: WOString {
value = replyTo;
escapeHTML = NO;
}
hasOrganization: WOConditional {
condition = hasOrganization;
}
organization: WOString {
value = organization;
escapeHTML = NO;
}
to: WOString {
value = to;
escapeHTML = NO;
}
hasCc: WOConditional {
condition = hasCc;
}
cc: WOString {
value = cc;
escapeHTML = NO;
}
hasNewsGroups: WOConditional {
condition = hasNewsGroups;
}
newsgroups: WOString {
value = newsgroups;
escapeHTML = NO;
}
hasReferences: WOConditional {
condition = hasReferences;
}
references: WOString {
value = references;
escapeHTML = NO;
}
messageBody: WOString {
value = messageBody;
escapeHTML = NO;
}
signature: WOString {
value = signature;
escapeHTML = NO;
}
replyPlacementOnTop: WOConditional {
condition = replyPlacementOnTop;
}
replyPlacementOnBottom: WOConditional {
condition = replyPlacementOnTop;
negate = YES;
}
signaturePlacementOnTop: WOConditional {
condition = signaturePlacementOnTop;
}
signaturePlacementOnBottom: WOConditional {
condition = signaturePlacementOnTop;
negate = YES;
}
-6
View File
@@ -108,9 +108,6 @@
@interface SOGoMailJapaneseReply : SOGoMailReply
@end
@interface SOGoMailKoreanReply : SOGoMailReply
@end
@interface SOGoMailKazakhReply : SOGoMailReply
@end
@@ -165,9 +162,6 @@
@interface SOGoMailSwedishReply : SOGoMailReply
@end
@interface SOGoMailThaiReply : SOGoMailReply
@end
@interface SOGoMailTurkishTurkeyReply : SOGoMailReply
@end
-6
View File
@@ -167,9 +167,6 @@
@implementation SOGoMailKazakhReply
@end
@implementation SOGoMailKoreanReply
@end
@implementation SOGoMailLatvianReply
@end
@@ -221,9 +218,6 @@
@implementation SOGoMailSwedishReply
@end
@implementation SOGoMailThaiReply
@end
@implementation SOGoMailTurkishTurkeyReply
@end
@@ -1,15 +0,0 @@
<#newLine/><#newLine
/><#signaturePlacementOnTop><#signature/><#newLine/><#newLine/></#signaturePlacementOnTop
>-------- ข้อความต้นทาง --------<#newLine
/>หัวข้อ: <#subject/><#newLine
/>วันที่: <#date/><#newLine
/>จาก: <#from/><#newLine
/><#hasReplyTo>ตอบกลับ: <#replyTo/><#newLine/></#hasReplyTo
><#hasOrganization>องค์กร: <#organization/><#newLine/></#hasOrganization
>ถึง: <#to/><#newLine
/><#hasCc>สำเนา: <#cc/><#newLine/></#hasCc
><#hasNewsGroups>กลุ่มข่าว: <#newsgroups/><#newLine/></#hasNewsGroups
><#hasReferences>การอ้างถึง: <#references/><#newLine/></#hasReferences
><#newLine/><#newLine
/><#messageBody
/><#signaturePlacementOnBottom><#newLine/><#newLine/><#signature/></#signaturePlacementOnBottom>
@@ -1,88 +0,0 @@
subject: WOString {
value = subject;
escapeHTML = NO;
}
date: WOString {
value = date;
escapeHTML = NO;
}
from: WOString {
value = from;
escapeHTML = NO;
}
newLine: WOString {
value = newLine;
escapeHTML = NO;
}
hasReplyTo: WOConditional {
condition = hasReplyTo;
}
replyTo: WOString {
value = replyTo;
escapeHTML = NO;
}
hasOrganization: WOConditional {
condition = hasOrganization;
}
organization: WOString {
value = organization;
escapeHTML = NO;
}
to: WOString {
value = to;
escapeHTML = NO;
}
hasCc: WOConditional {
condition = hasCc;
}
cc: WOString {
value = cc;
escapeHTML = NO;
}
hasNewsGroups: WOConditional {
condition = hasNewsGroups;
}
newsgroups: WOString {
value = newsgroups;
escapeHTML = NO;
}
hasReferences: WOConditional {
condition = hasReferences;
}
references: WOString {
value = references;
escapeHTML = NO;
}
messageBody: WOString {
value = messageBody;
escapeHTML = NO;
}
signature: WOString {
value = signature;
escapeHTML = NO;
}
signaturePlacementOnTop: WOConditional {
condition = signaturePlacementOnTop;
}
signaturePlacementOnBottom: WOConditional {
condition = signaturePlacementOnTop;
negate = YES;
}
@@ -1,18 +0,0 @@
<#replyPlacementOnTop><#newLine/><#newLine/></#replyPlacementOnTop
><#signaturePlacementOnTop><#signature/><#newLine/><#newLine/></#signaturePlacementOnTop
><#outlookMode
>-------- ข้อความต้นทาง --------<#newLine
/>หัวข้อ: <#subject/><#newLine
/>วันที่: <#date/><#newLine
/>จาก: <#from/><#newLine
/><#hasReplyTo>ตอบกลับ: <#replyTo/><#newLine/></#hasReplyTo
><#hasOrganization>องค์กร: <#organization/><#newLine/></#hasOrganization
>ถึง: <#to/><#newLine
/><#hasCc>สำเนา: <#cc/><#newLine/></#hasCc
><#hasNewsGroups>กลุ่มข่าว: <#newsgroups/><#newLine/></#hasNewsGroups
><#hasReferences>การอ้างถึง: <#references/><#newLine/></#hasReferences
></#outlookMode
><#standardMode>บน <#date/>, <#from/> ขียน:</#standardMode
><#newLine/><#newLine/><#messageBody
/><#replyPlacementOnBottom><#newLine/><#newLine/></#replyPlacementOnBottom
><#signaturePlacementOnBottom><#newLine/><#signature/></#signaturePlacementOnBottom>
@@ -1,106 +0,0 @@
outlookMode: WOConditional {
condition = outlookMode;
}
standardMode: WOConditional {
condition = outlookMode;
negate = YES;
}
subject: WOString {
value = subject;
escapeHTML = NO;
}
date: WOString {
value = date;
escapeHTML = NO;
}
from: WOString {
value = from;
escapeHTML = NO;
}
newLine: WOString {
value = newLine;
escapeHTML = NO;
}
hasReplyTo: WOConditional {
condition = hasReplyTo;
}
replyTo: WOString {
value = replyTo;
escapeHTML = NO;
}
hasOrganization: WOConditional {
condition = hasOrganization;
}
organization: WOString {
value = organization;
escapeHTML = NO;
}
to: WOString {
value = to;
escapeHTML = NO;
}
hasCc: WOConditional {
condition = hasCc;
}
cc: WOString {
value = cc;
escapeHTML = NO;
}
hasNewsGroups: WOConditional {
condition = hasNewsGroups;
}
newsgroups: WOString {
value = newsgroups;
escapeHTML = NO;
}
hasReferences: WOConditional {
condition = hasReferences;
}
references: WOString {
value = references;
escapeHTML = NO;
}
messageBody: WOString {
value = messageBody;
escapeHTML = NO;
}
signature: WOString {
value = signature;
escapeHTML = NO;
}
replyPlacementOnTop: WOConditional {
condition = replyPlacementOnTop;
}
replyPlacementOnBottom: WOConditional {
condition = replyPlacementOnTop;
negate = YES;
}
signaturePlacementOnTop: WOConditional {
condition = signaturePlacementOnTop;
}
signaturePlacementOnBottom: WOConditional {
condition = signaturePlacementOnTop;
negate = YES;
}
-5
View File
@@ -291,11 +291,6 @@ unsigned char GetRruleMonthNum(unsigned char a, unsigned char b) {
partName = nil;
data = [self fetchBLOB];
if((int)[data length] < sizeof(DWORD)) {
NSLog(@"TNEF: ERROR try to decode empty data where it should'nt");
return;
}
memcpy(&signature, [data bytes], sizeof(DWORD));
if (TNEFCheckForSignature(signature) == 0)
{
@@ -1,5 +0,0 @@
"OtherUsersFolderName" = "ผู้ใช้งานอื่นๆ";
"SharedFoldersName" = "โฟลเดอร์ที่แชร์";
"Report: Marked messages as junk" = "รายงาน: ทำเครื่องหมายข้อความเป็นขยะ";
"Report: Marked messages as not junk" = "รายงาน: ทำเครื่องหมายข้อความว่าไม่ใช่ขยะ";
+1 -1
View File
@@ -231,7 +231,7 @@ endif
ADDITIONAL_TOOL_LIBS += -Lobj -lSOGo$(LIBRARY_NAME_SUFFIX)
ADDITIONAL_INCLUDE_DIRS += -I../../SOPE/
ADDITIONAL_LIB_DIRS += -L../../SOPE/GDLContentStore/obj/
ADDITIONAL_LDFLAGS += -lmemcached -lzip -lytnef -lcurl
ADDITIONAL_LDFLAGS += -lmemcached -lzip -lytnef
-include GNUmakefile.preamble
ifneq ($(FHS_INSTALL_ROOT),)
-2
View File
@@ -784,8 +784,6 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
didChange = NO;
[self updateBaseDNFromLogin: login];
NS_DURING
if ([login length] > 0)
{
-1
View File
@@ -27,7 +27,6 @@
@class NSArray;
@class NSString;
@class WOContext;
@class NSNull;
@interface NSObject (SOGoObjectUtilities)
+9 -21
View File
@@ -33,9 +33,6 @@
#import "NSObject+Utilities.h"
static NSMutableDictionary *translationCache = nil;
@implementation NSObject (SOGoObjectUtilities)
- (NSString *) jsonRepresentation
@@ -104,28 +101,19 @@ static NSMutableDictionary *translationCache = nil;
if (!bundle)
bundle = [NSBundle mainBundle];
languages = [[self _languagesForLabelsInContext: context] objectEnumerator];
if(!translationCache)
translationCache = [NSMutableDictionary new];
while (!label && (language = [languages nextObject]))
{
if (![translationCache objectForKey: language] || [[translationCache objectForKey: language] objectForKey: key] != [NSNull null]) {
paths = [bundle pathsForResourcesOfType: @"strings"
inDirectory: [NSString stringWithFormat: @"%@.lproj",
language]
paths = [bundle pathsForResourcesOfType: @"strings"
inDirectory: [NSString stringWithFormat: @"%@.lproj",
language]
forLocalization: language];
if ([paths count] > 0)
{
strings = [NSDictionary
dictionaryFromStringsFile: [paths objectAtIndex: 0]];
[translationCache setObject: strings forKey: language];
label = [strings objectForKey: key];
}
} else {
strings = [translationCache objectForKey: language];
label = [strings objectForKey: key];
}
if ([paths count] > 0)
{
strings = [NSDictionary
dictionaryFromStringsFile: [paths objectAtIndex: 0]];
label = [strings objectForKey: key];
}
}
if (!label)
label = key;
-3
View File
@@ -184,9 +184,6 @@ static int cssEscapingCount;
{
rangePtr->location += offset;
urlText = [selfCopy substringFromRange: *rangePtr];
//If we try to detect mail, check if the first char is @, meaning this is not an email
if([prefix isEqualToString: @"mailto:"] && [urlText characterAtIndex: 0] == '@')
continue;
newUrlText = [NSString stringWithFormat: @"<a rel=\"noopener\" href=\"%@%@\">%@</a>",
([urlText hasPrefix: prefix]? @"" : prefix),
urlText, urlText];
+1 -1
View File
@@ -289,7 +289,7 @@ static memcached_st *handle = NULL;
NSData *keyData, *valueData;
memcached_return error;
//[self logWithFormat: @"setValue: '%@' forKey: '%@'", value, key];
// [self logWithFormat: @"setValue: '%@' forKey: '%@'", value, key];
if (handle)
{
keyData = [key dataUsingEncoding: NSUTF8StringEncoding];
-4
View File
@@ -59,7 +59,6 @@
"French",
"Galician",
"German",
"Greek",
"Hebrew",
"Hungarian",
"Icelandic",
@@ -67,7 +66,6 @@
"Italian",
"Japanese",
"Kazakh",
"Korean",
"Latvian",
"Lithuanian",
"Macedonian",
@@ -86,7 +84,6 @@
"SpanishArgentina",
"SpanishSpain",
"Swedish",
"Thai",
"TurkishTurkey",
"Ukrainian",
"Welsh"
@@ -130,7 +127,6 @@
SOGoMailDisplayFullEmail = NO;
SOGoMailDisplayRemoteInlineImages = "never";
SOGoMailCertificateEnabled = YES;
SOGoMailDisplayAttachmentAbove = NO;
SOGoMailAutoMarkAsReadDelay = "0";
SOGoMailAutoSave = "5";
+1 -2
View File
@@ -78,8 +78,7 @@
- (BOOL) appointmentSendEMailNotifications;
- (BOOL) foldersSendEMailNotifications;
- (NSArray *) calendarDefaultRoles;
- (NSString *) calendarJitsiBaseUrl;
- (NSString *) calendarJitsiRoomPrefix;
- (NSString *) calendarJistiBaseUrl;
- (NSArray *) contactsDefaultRoles;
- (NSArray *) refreshViewIntervals;
- (NSString *) subscriptionFolderFormat;
+2 -14
View File
@@ -169,7 +169,7 @@
return [self stringArrayForKey: @"SOGoCalendarDefaultRoles"];
}
- (NSString *) calendarJitsiBaseUrl
- (NSString *) calendarJistiBaseUrl
{
NSString *jitsiBaseUrl;
jitsiBaseUrl = [self stringForKey: @"SOGoCalendarJitsiBaseUrl"];
@@ -179,16 +179,6 @@
return jitsiBaseUrl;
}
- (NSString *) calendarJitsiRoomPrefix
{
NSString *jitsiRoomPrefix;
jitsiRoomPrefix = [self stringForKey: @"SOGoCalendarJitsiRoomPrefix"];
if(!jitsiRoomPrefix)
jitsiRoomPrefix = @"SOGo_meeting/";
return jitsiRoomPrefix;
}
- (NSArray *) contactsDefaultRoles
{
return [self stringArrayForKey: @"SOGoContactsDefaultRoles"];
@@ -462,9 +452,7 @@
- (int) searchMinimumWordLength
{
int min = [self integerForKey: @"SOGoSearchMinimumWordLength"];
if(min<1) min=1;
return min;
return [self integerForKey: @"SOGoSearchMinimumWordLength"];
}
- (BOOL) notifyOnPersonalModifications
+1 -1
View File
@@ -325,7 +325,7 @@
{
login = [currentAcount objectForKey: @"userName"];
password = [currentAcount objectForKey: @"password"];
smtpAuthMethod = @"plain"; //Only support plain for auxiliary account
smtpAuthMethod = "plain"; //Only support plain for auxiliary account
}
else
{
-4
View File
@@ -48,7 +48,6 @@
@class NSException;
@class NSTimeZone;
@class NSURL;
@class NSUUID;
@class WOContext;
@class WORequest;
@@ -75,9 +74,6 @@
+ (NSString *) globallyUniqueObjectId;
- (NSString *) globallyUniqueObjectId;
+ (NSString *) mailUniqueMessageId;
- (NSString *) mailUniqueMessageId;
+ (id) objectWithName: (NSString *)_name inContainer:(id)_container;
- (id) initWithName: (NSString *) _name inContainer:(id)_container;
+1 -17
View File
@@ -21,6 +21,7 @@
#import <unistd.h>
#import <NGObjWeb/SoClass.h>
#import <NGObjWeb/SoObject+SoDAV.h>
#import <NGObjWeb/WOApplication.h>
@@ -110,28 +111,11 @@
pid, (int) f, sequence++, (int) rndm];
}
- (NSString *) globallyUniqueObjectId
{
return [[self class] globallyUniqueObjectId];
}
+ (NSString *) mailUniqueMessageId
{
/*
843F8180-4E33-7B0B-6F1B-9847CAD3E6EB
*/
return [[NSUUID UUID] UUIDString];
}
- (NSString *) mailUniqueMessageId
{
/*
843F8180-4E33-7B0B-6F1B-9847CAD3E6EB
*/
return [[self class] mailUniqueMessageId];
}
/* containment */
+ (id) objectWithName: (NSString *)_name inContainer:(id)_container
+2 -21
View File
@@ -33,25 +33,6 @@
@class NSURL;
@class NSJSONSerialization;
size_t curl_body_function(void *ptr, size_t size, size_t nmemb, void *buffer);
@interface SimpleOpenIdResponse: NSObject
{
unsigned int status;
NSString *content;
NSString *headers;
}
- (id)initWithResponse: (NSString *)_data andHeaders: (NSString *)_headers andStatus:(unsigned int )_status;
- (void)setStatus:(unsigned int)_status;
- (unsigned int)status;
- (void)setContent:(NSString *)_data;
- (NSString *)contentString;
- (void)setHearders:(NSString *)_data;
- (NSString *)headers;
@end
@interface SOGoOpenIdSession : SOGoObject
{
@@ -99,12 +80,12 @@ size_t curl_body_function(void *ptr, size_t size, size_t nmemb, void *buffer);
- (void) initialize;
- (void) initializeWithConfig: (NSDictionary *) _config;
- (BOOL) sessionIsOK;
- (SimpleOpenIdResponse *) _performOpenIdRequest: (NSString *) endpoint
- (WOResponse *) _performOpenIdRequest: (NSString *) endpoint
method: (NSString *) method
headers: (NSDictionary *) headers
body: (NSData *) body;
- (NSMutableDictionary *) fecthConfiguration: (NSString *) _domain;
- (void) setAccessToken: (NSString* ) token;
- (void) setAccessToken;
- (NSString *) getRefreshToken;
- (NSString *) getToken;
- (NSString *) getCurrentToken;
+50 -248
View File
@@ -18,11 +18,6 @@
* Boston, MA 02111-1307, USA.
*/
#import <Foundation/NSProcessInfo.h>
#import <curl/curl.h>
#import <NGObjWeb/WOHTTPConnection.h>
#import <NGObjWeb/WORequest.h>
#import <NGObjWeb/WOResponse.h>
@@ -30,12 +25,12 @@
#import <SOGo/SOGoUser.h>
#import <GDLContentStore/GCSOpenIdFolder.h>
#import <GDLContentStore/GCSFolderManager.h>
#import "NSDictionary+Utilities.h"
#import <SOGo/NSString+Utilities.h>
#import <SOGo/NSString+Crypto.h>
#import "NSString+Utilities.h"
#import "SOGoCache.h"
#import "SOGoSystemDefaults.h"
@@ -43,87 +38,6 @@
static BOOL SOGoOpenIDDebugEnabled = YES;
size_t curl_body_function(void *ptr, size_t size, size_t nmemb, void *buffer)
{
size_t total;
total = size * nmemb;
[(NSMutableData *)buffer appendBytes: ptr length: total];
return total;
}
@implementation SimpleOpenIdResponse
- (id)init {
if ((self = [super init])) {
}
return self;
}
- (id)initWithResponse: (NSString *)_data andHeaders: (NSString *)_headers andStatus:(unsigned int )_status{
if ((self = [self init])) {
[self setStatus: _status];
[self setContent: _data];
[self setHeaders: _headers];
}
return self;
}
- (void)setStatus:(unsigned int)_status
{
self->status = _status;
}
- (unsigned int)status
{
if(self->status)
return self->status;
return 0;
}
- (void)setContent:(NSString *)_data
{
self->content = _data;
}
- (NSString *)contentString
{
if(self->content)
return self->content;
return nil;
}
- (void)setHeaders:(NSString *)_data
{
self->headers = _data;
}
- (NSString *)headers
{
if(self->headers)
return self->headers;
return nil;
}
- (NSString*)description {
NSString *co, *hd;
unsigned int st;
st = 0;
co = @"nil";
hd = @"nil";
if(self->status)
st = self->status;
if(self->headers)
hd = self->headers;
if(self->content)
co = self->content;
return [NSString stringWithFormat: @"<SimpleOpenIdResponse> <status: %d>, <headers: %@>, <content: %@>", st, hd, co];
}
@end
@implementation SOGoOpenIdSession
/// Check if all required parameters to set up a OpenIdSession are in sogo.conf
@@ -138,20 +52,13 @@ size_t curl_body_function(void *ptr, size_t size, size_t nmemb, void *buffer)
return NO;
}
sd = [SOGoSystemDefaults sharedSystemDefaults];
if(![[sd authenticationType] isEqualToString: @"openid"])
{
[self errorWithFormat: @"Sogo SOGoAuthenticationType is not openid"];
return NO;
}
return ([sd openIdConfigUrl] && [sd openIdScope] && [sd openIdClient] && [sd openIdClientSecret]);
}
- (void) initializeWithConfig: (NSDictionary *) _config
{
SOGoSystemDefaults *sd;
id refreshTokenBool, domainInfo, intForKey;
id refreshTokenBool, domainInfo;
if([_config objectForKey: @"SOGoOpenIdConfigUrl"] &&
[_config objectForKey: @"SOGoOpenIdScope"] &&
@@ -174,12 +81,8 @@ size_t curl_body_function(void *ptr, size_t size, size_t nmemb, void *buffer)
if (domainInfo && [domainInfo respondsToSelector: @selector (boolValue)])
sendDomainInfo = [domainInfo boolValue];
intForKey = [_config objectForKey: @"SOGoOpenIdTokenCheckInterval"];
if (intForKey && [intForKey respondsToSelector: @selector (intValue)])
userTokenInterval = [intForKey intValue];
else
userTokenInterval = 0;
userTokenInterval = [_config objectForKey: @"SOGoOpenIdTokenCheckInterval"];
[self _loadSessionFromCache: forDomain];
if(cacheUpdateNeeded)
@@ -215,7 +118,7 @@ size_t curl_body_function(void *ptr, size_t size, size_t nmemb, void *buffer)
}
else
{
[self errorWithFormat: @"LoginTypebyDOmain - Openid not found or missing parameters for domain", _domain];
[self errorWithFormat: @"Missing parameters from sogo.conf"];
}
}
else if ([[self class] checkUserConfig])
@@ -244,150 +147,66 @@ size_t curl_body_function(void *ptr, size_t size, size_t nmemb, void *buffer)
}
}
- (SimpleOpenIdResponse *) _performOpenIdRequest: (NSString *) endpoint
- (WOResponse *) _performOpenIdRequest: (NSString *) endpoint
method: (NSString *) method
headers: (NSDictionary *) headers
body: (NSData *) body
{
NSURL *url;
NSMutableData *buffer, *buffHeaders;
SimpleOpenIdResponse *response;
CURL *curl;
struct curl_slist *headerlist=NULL;
NSUInteger status;
NSString *content, *headerResp;
CURLcode rc;
char error[CURL_ERROR_SIZE];
WORequest *request;
WOResponse *response;
WOHTTPConnection *httpConnection;
url = [NSURL URLWithString: endpoint];
if(!url)
if (url)
{
[self errorWithFormat: @"OpenID can't handle endpoint (not a url): '%@'", endpoint];
return nil;
}
if(SOGoOpenIDDebugEnabled)
{
NSLog(@"OpenId perform request: %@ %@", method, endpoint);
NSLog(@"OpenId perform request, headers %@", headers);
}
if(SOGoOpenIDDebugEnabled)
NSLog(@"OpenId perform request, body raw %@", body);
curl_global_init(CURL_GLOBAL_SSL);
curl = curl_easy_init();
if (curl)
{
error[0] = 0;
curl_easy_setopt(curl, CURLOPT_URL, [endpoint UTF8String]);
//add form
if(body)
if(SOGoOpenIDDebugEnabled)
{
//Trick because sometimes NSData bytes don't add the \0 at the end and junk character may be added
const char *myBodyTmp = (const char*)[body bytes];
char myBody[[body length]+1];
strncpy(myBody, myBodyTmp, [body length]);
myBody[[body length]]='\0';
if(SOGoOpenIDDebugEnabled)
NSLog(@"OpenId perform request, myBodyTmp %s", myBodyTmp);
if(SOGoOpenIDDebugEnabled)
NSLog(@"OpenId perform request, myBody %s", myBody);
curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, myBody);
NSLog(@"OpenId perform request: %@ %@", method, endpoint);
NSLog(@"OpenId perform request, headers %@", headers);
// if(body)
// NSLog(@"OpenId perform request: content %@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
}
//add headers
if(headers)
httpConnection = [[WOHTTPConnection alloc] initWithURL: url];
[httpConnection autorelease];
request = [[WORequest alloc] initWithMethod: method
uri: [endpoint hostlessURL]
httpVersion: @"HTTP/1.1"
headers: headers content: body
userInfo: nil];
[request autorelease];
[httpConnection sendRequest: request];
response = [httpConnection readResponse];
status = [response status];
if(status >= 200 && status <500 && status != 404)
return response;
else if (status == 404)
{
NSEnumerator *enumerator = [headers keyEnumerator];
NSString *key, *header;
while((key = [enumerator nextObject]))
{
header = [NSString stringWithFormat: @"%@: %@", key, [headers objectForKey: key]];
headerlist = curl_slist_append(headerlist, [header UTF8String]);
}
}
if(headerlist != NULL)
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
buffer = [NSMutableData data];
buffHeaders = [NSMutableData data];
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_body_function);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, buffer);
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curl_body_function);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, buffHeaders);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error);
// Perform SOAP request
rc = curl_easy_perform(curl);
if (rc == CURLE_OK)
{
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
response = [SimpleOpenIdResponse alloc];
headerResp = [[NSString alloc] initWithData: buffHeaders
encoding: NSUTF8StringEncoding];
response = [response initWithResponse: nil andHeaders: headerResp andStatus: status];
if(status >= 200 && status <500 && status != 404)
{
content = [[NSString alloc] initWithData: buffer
encoding: NSUTF8StringEncoding];
if (!content)
content = [[NSString alloc] initWithData: buffer
encoding: NSISOLatin1StringEncoding];
[response setContent: content];
if(SOGoOpenIDDebugEnabled)
NSLog(@"OpenId perform request: response is: %@", response);
curl_easy_cleanup(curl);
return [response autorelease];
}
else if (status == 404)
{
[self errorWithFormat: @"OpenID endpoint not found (404): %@", endpoint];
curl_easy_cleanup(curl);
return nil;
}
else
{
[self errorWithFormat: @"OpenID server internal error during %@: %@", endpoint, response];
curl_easy_cleanup(curl);
return nil;
}
[self errorWithFormat: @"OpenID endpoint not found (404): %@", endpoint];
return nil;
}
else
{
[self errorWithFormat: @"CURL error while accessing %@ (%d): %@", endpoint, rc,
[NSString stringWithCString: strlen(error) ? error : curl_easy_strerror(rc)]];
[self errorWithFormat: @"OpenID server internal error during %@: %@", endpoint, response];
return nil;
}
curl_easy_cleanup(curl);
}
else
{
[self errorWithFormat: @"OpenID error when setting curl request for: %@", endpoint];
return nil;
[self errorWithFormat: @"OpenID can't handle endpoint (not a url): '%@'", endpoint];
return nil;
}
}
- (NSMutableDictionary *) fecthConfiguration: (NSString*) _domain
{
NSString *content;
SimpleOpenIdResponse *response;
// WOResponse * response;
WOResponse * response;
NSUInteger status;
NSMutableDictionary *result;
NSDictionary *config, *headers;
@@ -419,9 +238,7 @@ size_t curl_body_function(void *ptr, size_t size, size_t nmemb, void *buffer)
self->authorizationEndpoint = [config objectForKey: @"authorization_endpoint"];
self->tokenEndpoint = [config objectForKey: @"token_endpoint"];
self->userinfoEndpoint = [config objectForKey: @"userinfo_endpoint"];
if([config objectForKey: @"end_session_endpoint"])
self->endSessionEndpoint = [config objectForKey: @"end_session_endpoint"];
self->endSessionEndpoint = [config objectForKey: @"end_session_endpoint"];
//Optionnals?
if([config objectForKey: @"introspection_endpoint"])
@@ -529,8 +346,7 @@ size_t curl_body_function(void *ptr, size_t size, size_t nmemb, void *buffer)
ASSIGN (authorizationEndpoint, [sessionDict objectForKey: @"authorization_endpoint"]);
ASSIGN (tokenEndpoint, [sessionDict objectForKey: @"token_endpoint"]);
ASSIGN (userinfoEndpoint, [sessionDict objectForKey: @"userinfo_endpoint"]);
if([sessionDict objectForKey: @"end_session_endpoint"])
ASSIGN (endSessionEndpoint, [sessionDict objectForKey: @"end_session_endpoint"]);
ASSIGN (endSessionEndpoint, [sessionDict objectForKey: @"end_session_endpoint"]);
//Optionnals?
if([sessionDict objectForKey: @"introspection_endpoint"])
@@ -554,8 +370,7 @@ size_t curl_body_function(void *ptr, size_t size, size_t nmemb, void *buffer)
[sessionDict setObject: authorizationEndpoint forKey: @"authorization_endpoint"];
[sessionDict setObject: tokenEndpoint forKey: @"token_endpoint"];
[sessionDict setObject: userinfoEndpoint forKey: @"userinfo_endpoint"];
if(endSessionEndpoint)
[sessionDict setObject: endSessionEndpoint forKey: @"end_session_endpoint"];
[sessionDict setObject: endSessionEndpoint forKey: @"end_session_endpoint"];
//Optionnals?
if(introspectionEndpoint)
@@ -616,12 +431,6 @@ size_t curl_body_function(void *ptr, size_t size, size_t nmemb, void *buffer)
nextCheckAfter: nextCheck];
}
-(NSString *) _random_state
{
return [[[NSProcessInfo processInfo] globallyUniqueString] asSHA1String];;
}
- (NSString*) loginUrl: (NSString *) oldLocation
{
NSString* logUrl;
@@ -629,7 +438,6 @@ size_t curl_body_function(void *ptr, size_t size, size_t nmemb, void *buffer)
logUrl = [logUrl stringByAppendingString: @"&response_type=code"];
logUrl = [logUrl stringByAppendingFormat: @"&client_id=%@", self->openIdClient];
logUrl = [logUrl stringByAppendingFormat: @"&redirect_uri=%@", oldLocation];
logUrl = [logUrl stringByAppendingFormat: @"&state=%@", [self _random_state]];
if(self->forDomain != nil && [self->forDomain length] > 0)
logUrl = [logUrl stringByAppendingFormat: @"&sogo_domain=%@", forDomain];
// logurl = [self->logurl stringByAppendingFormat: @"&state=%@", state];
@@ -677,8 +485,7 @@ size_t curl_body_function(void *ptr, size_t size, size_t nmemb, void *buffer)
- (NSMutableDictionary *) fetchToken: (NSString * ) code redirect: (NSString *) oldLocation
{
NSString *location, *form, *content;
SimpleOpenIdResponse *response;
// WOResponse *response;
WOResponse *response;
NSUInteger status;
NSMutableDictionary *result;
NSDictionary *headers;
@@ -703,10 +510,7 @@ size_t curl_body_function(void *ptr, size_t size, size_t nmemb, void *buffer)
self->forDomain, @"sogo-user-domain", nil];
else
headers = [NSDictionary dictionaryWithObject: @"application/x-www-form-urlencoded" forKey: @"content-type"];
if(SOGoOpenIDDebugEnabled)
NSLog(@"OpenId fetch token, form %@", form);
response = [self _performOpenIdRequest: location
method: @"POST"
headers: headers
@@ -749,8 +553,7 @@ size_t curl_body_function(void *ptr, size_t size, size_t nmemb, void *buffer)
- (NSMutableDictionary *) refreshToken: (NSString * ) userRefreshToken
{
NSString *location, *form, *content;
SimpleOpenIdResponse *response;
// WOResponse *response;
WOResponse *response;
NSUInteger status;
NSMutableDictionary *result;
NSDictionary *headers;
@@ -823,8 +626,7 @@ size_t curl_body_function(void *ptr, size_t size, size_t nmemb, void *buffer)
- (NSMutableDictionary *) fetchUserInfo
{
NSString *location, *auth, *content;
SimpleOpenIdResponse *response;
// WOResponse *response;
WOResponse *response;
NSUInteger status;
NSMutableDictionary *result;
NSDictionary *profile, *headers;
@@ -880,7 +682,7 @@ size_t curl_body_function(void *ptr, size_t size, size_t nmemb, void *buffer)
}
else
{
[self logWithFormat: @"Error fetching userInfo (status %d), response: %@", status, response];
[self logWithFormat: @"Error during fetching the token (status %d), response: %@", status, response];
[result setObject: @"http-error" forKey: @"error"];
}
}
+3 -12
View File
@@ -569,10 +569,7 @@ static SoSecurityManager *sm = nil;
isPropfind = [requestMethod isEqualToString: @"PROPFIND"];
error = [self initSubFolders];
if(error)
{
[self errorWithFormat: @"a database error occured when inititializing the subFolders: %@", [error reason]];
if(isPropfind)
if (error && isPropfind)
{
/* We exceptionnally raise the exception here because doPROPFIND: will
not care for errors in its response from toManyRelationShipKeys,
@@ -580,16 +577,10 @@ static SoSecurityManager *sm = nil;
SOGo extensions. */
[error raise];
}
}
error = [self initSubscribedSubFolders];
if (error)
{
[self errorWithFormat: @"a database error occured when subscribing to subfolders: %@", [error reason]];
if(isPropfind)
[error raise];
}
if (error && isPropfind)
[error raise];
ma = [NSMutableArray arrayWithArray: [subFolders allValues]];
if ([subscribedSubFolders count])
+1 -1
View File
@@ -33,7 +33,7 @@ static const NSString *POLICY_MIN_DIGIT = @"POLICY_MIN_DIGIT";
static const NSString *POLICY_MIN_SPECIAL_SYMBOLS = @"POLICY_MIN_SPECIAL_SYMBOLS";
static const NSString *POLICY_MIN_LENGTH = @"POLICY_MIN_LENGTH";
static const NSString *SPECIAL_SYMBOL_ALLOWED = @"%$&*(){}!?\\@#.,:;+=\\[\\]\\|<>\\/\\-_";
static const NSString *SPECIAL_SYMBOL_ALLOWED = @"%$&*(){}!?\\@#.,:;+=\\[\\]\\|<>\\/-_";
@implementation SOGoPasswordPolicy
+18 -18
View File
@@ -122,28 +122,28 @@ static NSString *productDirectoryName = @"SOGo";
pathes = [[self productSearchPathes] objectEnumerator];
while ((lpath = [pathes nextObject]))
{
productNames = [[fm directoryContentsAtPath: lpath] objectEnumerator];
while ((productName = [productNames nextObject]))
{
if ([[productName pathExtension] isEqualToString: @"SOGo"])
productNames = [[fm directoryContentsAtPath: lpath] objectEnumerator];
while ((productName = [productNames nextObject]))
{
if ([[productName pathExtension] isEqualToString: @"SOGo"])
{
bpath = [lpath stringByAppendingPathComponent: productName];
[registry registerProductAtPath: bpath];
[loadedProducts addObject: productName];
}
}
if ([loadedProducts count])
{
bpath = [lpath stringByAppendingPathComponent: productName];
[registry registerProductAtPath: bpath];
[loadedProducts addObject: productName];
if (verbose)
{
[self logWithFormat: @"SOGo products loaded from '%@':", lpath];
[self logWithFormat: @" %@",
[loadedProducts componentsJoinedByString: @", "]];
}
[loadedProducts removeAllObjects];
}
}
if ([loadedProducts count])
{
if (verbose)
{
[self logWithFormat: @"SOGo products loaded from '%@':", lpath];
[self logWithFormat: @" %@",
[loadedProducts componentsJoinedByString: @", "]];
}
[loadedProducts removeAllObjects];
}
}
if (![registry loadAllProducts] && verbose)
[self warnWithFormat: @"could not load all products !"];
+3 -28
View File
@@ -162,23 +162,11 @@
// Get the key length and its bytes
data = [theKey dataByDecodingBase64];
key = (char *)[data bytes];
klen = [data length];
//value longer than the key, concatenate the key with itself until long enough
if (klen < [theValue length])
{
NSMutableData *concatenatedData = [NSMutableData data];
int j;
int nbDuplication = [theValue length]/klen;
for(j=0; j <= nbDuplication; j++)
[concatenatedData appendData:data];
data = [NSData dataWithData: concatenatedData];
klen = [data length];
}
key = (char *)[data bytes];
[self errorWithFormat: @"Value to be secured is too big (%i > %i) -- secured value will be corrupted", [theValue length], klen, [theKey length]];
// Get the key - padding it with 0 with key length
pass = (char *) calloc(klen, sizeof(char));
@@ -213,6 +201,7 @@
// Get the key length and its bytes
dataKey = [theKey dataByDecodingBase64];
key = (char *)[dataKey bytes];
klen = [dataKey length];
// Get the secured value length and its bytes
@@ -220,20 +209,6 @@
value = (char *)[dataValue bytes];
vlen = [dataValue length];
//If the key is shorer than the value, duplicate it with itself.
if(klen < vlen)
{
NSMutableData *concatenatedData = [NSMutableData data];
int j;
int nbDuplication = [dataValue length]/klen;
for(j=0; j <= nbDuplication; j++)
[concatenatedData appendData:dataKey];
dataKey = [NSData dataWithData: concatenatedData];
klen = [dataKey length];
}
key = (char *)[dataKey bytes];
// Target buffer
buf = (char *) calloc(klen, sizeof(char));
+3 -11
View File
@@ -496,7 +496,6 @@ static NSString *sieveScriptName = @"sogo";
}
- (NSString *) _extractSieveAction: (NSDictionary *) action
withReq: (NSMutableDictionary *) req
delimiter: (NSString *) delimiter
{
NSString *sieveAction, *method, *requirement, *argument, *flag, *mailbox;
@@ -549,9 +548,7 @@ static NSString *sieveScriptName = @"sogo";
{
argument = [NSString stringWithFormat: @"mailto:%@", argument];
sieveAction = [NSString stringWithFormat: @"%@ %@",
method, [argument asSieveQuotedString]];
[req addObjectUniquely: @"enotify"];
[req addObjectUniquely: @"variables"];
method, [argument asSieveQuotedString]];
}
else if ([method isEqualToString: @"reject"])
sieveAction = [NSString stringWithFormat: @"%@ %@",
@@ -578,7 +575,6 @@ static NSString *sieveScriptName = @"sogo";
}
- (NSArray *) _extractSieveActions: (NSArray *) actions
withReq: (NSMutableDictionary *) req
delimiter: (NSString *) delimiter
{
NSMutableArray *sieveActions;
@@ -590,7 +586,6 @@ static NSString *sieveScriptName = @"sogo";
for (count = 0; !scriptError && count < max; count++)
{
sieveAction = [self _extractSieveAction: [actions objectAtIndex: count]
withReq: req
delimiter: delimiter];
if (!scriptError)
[sieveActions addObject: sieveAction];
@@ -600,7 +595,6 @@ static NSString *sieveScriptName = @"sogo";
}
- (NSString *) _convertScriptToSieve: (NSDictionary *) newScript
withReq: (NSMutableDictionary *) req
delimiter: (NSString *) delimiter
{
NSMutableString *sieveText;
@@ -630,7 +624,6 @@ static NSString *sieveScriptName = @"sogo";
scriptError = [NSString stringWithFormat: @"Bad test: %@", match];
}
sieveActions = [self _extractSieveActions: [newScript objectForKey: @"actions"]
withReq: req
delimiter: delimiter];
if ([sieveActions count])
[sieveText appendFormat: @" %@;\r\n",
@@ -667,7 +660,6 @@ static NSString *sieveScriptName = @"sogo";
if ([[currentScript objectForKey: @"active"] boolValue])
{
sieveText = [self _convertScriptToSieve: currentScript
withReq: newRequirements
delimiter: delimiter];
[sieveScript appendString: sieveText];
}
@@ -790,10 +782,10 @@ static NSString *sieveScriptName = @"sogo";
imapAuthMech = nil;
if([sd doesLoginTypeByDomain])
{
r = [authname rangeOfString: @"@"];
r = [theUsername rangeOfString: @"@"];
if (r.location != NSNotFound)
{
userDomain = [authname substringFromIndex: r.location+1];
userDomain = [theUsername substringFromIndex: r.location+1];
imapAuthMech = [sd getImapAuthMechForDomain: userDomain];
}
}
-1
View File
@@ -83,7 +83,6 @@ static const NSString *kDisableSharingCalendar = @"Calendar";
- (BOOL) uixDebugEnabled;
- (BOOL) easDebugEnabled;
- (BOOL) openIdDebugEnabled;
- (BOOL) apiDebugEnabled;
- (BOOL) tnefDecoderDebugEnabled;
- (BOOL) xsrfValidationEnabled;
+6 -5
View File
@@ -654,11 +654,6 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict,
return [self boolForKey: @"SOGoOpenIDDebugEnabled"];
}
- (BOOL) apiDebugEnabled
{
return [self boolForKey: @"SOGoAPIDebugEnabled"];
}
- (BOOL) tnefDecoderDebugEnabled
{
return [self boolForKey: @"SOGoTnefDecoderDebugEnabled"];
@@ -1030,7 +1025,13 @@ NSComparisonResult languageSort(id el1, id el2, void *context)
v = [self integerForKey: @"SOGoInternalSyncInterval"];
if (!v)
{
NSLog(@"EASLOG: SOGoInternalSyncInterval didn't find value in conf, go with default 10");
v = 10;
}
else {
NSLog(@"EASLOG: SOGoInternalSyncInterval found, value is %d", v);
}
return v;
}
+1 -2
View File
@@ -124,8 +124,7 @@
- (BOOL) isSuperUser;
- (BOOL) canAuthenticate;
- (NSString *) totpKey: (bool) isCheck;
- (NSString *) oldtotpKey;
- (NSString *) totpKey;
/* resource */
- (BOOL) isResource;
+81 -135
View File
@@ -47,6 +47,7 @@
#import "SOGoUser.h"
static const NSString *kEncryptedUserNamePrefix = @"uenc";
@implementation SoUser (SOGoExtension)
@@ -144,91 +145,92 @@
realUID = nil;
domain = nil;
if ([newLogin isEqualToString: @"anonymous"] || [newLogin isEqualToString: @"freebusy"])
if ([newLogin isEqualToString: @"anonymous"]
|| [newLogin isEqualToString: @"freebusy"])
realUID = newLogin;
else
{
sd = [SOGoSystemDefaults sharedSystemDefaults];
if ([sd enableDomainBasedUID] || [[sd loginDomains] count] > 0)
{
r = [newLogin rangeOfString: @"@" options: NSBackwardsSearch];
if (r.location != NSNotFound)
{
// The domain is probably appended to the username;
// make sure it is defined as a domain in the configuration.
domain = [newLogin substringFromIndex: (r.location + r.length)];
if ([[SOGoUserManager sharedUserManager] isDomainDefined: domain] &&
![sd enableDomainBasedUID])
newLogin = [newLogin substringToIndex: r.location];
sd = [SOGoSystemDefaults sharedSystemDefaults];
if ([sd enableDomainBasedUID] || [[sd loginDomains] count] > 0)
{
r = [newLogin rangeOfString: @"@" options: NSBackwardsSearch];
if (r.location != NSNotFound)
{
// The domain is probably appended to the username;
// make sure it is defined as a domain in the configuration.
domain = [newLogin substringFromIndex: (r.location + r.length)];
if ([[SOGoUserManager sharedUserManager] isDomainDefined: domain] &&
![sd enableDomainBasedUID])
newLogin = [newLogin substringToIndex: r.location];
if (domain != nil && ![sd enableDomainBasedUID])
// Login domains are enabled (SOGoLoginDomains) but not
// domain-based UID (SOGoEnableDomainBasedUID).
// Drop the domain from the login name.
domain = nil;
}
if (domain != nil && ![sd enableDomainBasedUID])
// Login domains are enabled (SOGoLoginDomains) but not
// domain-based UID (SOGoEnableDomainBasedUID).
// Drop the domain from the login name.
domain = nil;
}
}
newLogin = [newLogin stringByReplacingString: @"%40"
withString: @"@"];
if (b)
realUID = newLogin;
else
{
um = [SOGoUserManager sharedUserManager];
contactInfos = [um contactInfosForUserWithUIDorEmail: newLogin
inDomain: domain];
realUID = [contactInfos objectForKey: @"c_uid"];
if (domain == nil && [sd enableDomainBasedUID])
domain = [contactInfos objectForKey: @"c_domain"];
}
if ([realUID length] && [domain length])
{
// When the user is associated to a domain, the [SOGoUser login]
// method returns the combination login@domain while
// [SOGoUser loginInDomain] only returns the login.
r = [realUID rangeOfString: domain options: NSBackwardsSearch|NSCaseInsensitiveSearch];
// Do NOT strip @domain.com if SOGoEnableDomainBasedUID is enabled since
// the real login most likely is the email address.
if (r.location != NSNotFound && ![sd enableDomainBasedUID])
uid = [realUID substringToIndex: r.location-1];
// If we don't have the domain in the UID but SOGoEnableDomainBasedUID is
// enabled, let's add it internally so so it becomes unique across
// all potential domains.
else if (r.location == NSNotFound && [sd enableDomainBasedUID])
{
uid = [NSString stringWithString: realUID];
realUID = [NSString stringWithFormat: @"%@@%@", realUID, domain];
}
// We found the domain and SOGoEnableDomainBasedUID is enabled,
// we keep realUID.. This would happen for example if the user
// authenticates with foo@bar.com and the UIDFieldName is also foo@bar.com
else if ([sd enableDomainBasedUID])
uid = [NSString stringWithString: realUID];
}
}
newLogin = [newLogin stringByReplacingString: @"%40"
withString: @"@"];
if (b)
realUID = newLogin;
else
{
um = [SOGoUserManager sharedUserManager];
contactInfos = [um contactInfosForUserWithUIDorEmail: newLogin
inDomain: domain];
realUID = [contactInfos objectForKey: @"c_uid"];
if (domain == nil && [sd enableDomainBasedUID])
domain = [contactInfos objectForKey: @"c_domain"];
}
if ([realUID length] && [domain length])
{
// When the user is associated to a domain, the [SOGoUser login]
// method returns the combination login@domain while
// [SOGoUser loginInDomain] only returns the login.
r = [realUID rangeOfString: domain options: NSBackwardsSearch|NSCaseInsensitiveSearch];
// Do NOT strip @domain.com if SOGoEnableDomainBasedUID is enabled since
// the real login most likely is the email address.
if (r.location != NSNotFound && ![sd enableDomainBasedUID])
uid = [realUID substringToIndex: r.location-1];
// If we don't have the domain in the UID but SOGoEnableDomainBasedUID is
// enabled, let's add it internally so so it becomes unique across
// all potential domains.
else if (r.location == NSNotFound && [sd enableDomainBasedUID])
{
uid = [NSString stringWithString: realUID];
realUID = [NSString stringWithFormat: @"%@@%@", realUID, domain];
}
// We found the domain and SOGoEnableDomainBasedUID is enabled,
// we keep realUID.. This would happen for example if the user
// authenticates with foo@bar.com and the UIDFieldName is also foo@bar.com
else if ([sd enableDomainBasedUID])
uid = [NSString stringWithString: realUID];
}
}
if ([realUID length])
{
if ((self = [super initWithLogin: realUID roles: newRoles]))
{
allEmails = nil;
currentPassword = nil;
cn = nil;
ASSIGN (loginInDomain, (uid ? uid : realUID));
_defaults = nil;
_domainDefaults = nil;
_settings = nil;
mailAccounts = nil;
if ((self = [super initWithLogin: realUID roles: newRoles]))
{
allEmails = nil;
currentPassword = nil;
cn = nil;
ASSIGN (loginInDomain, (uid ? uid : realUID));
_defaults = nil;
_domainDefaults = nil;
_settings = nil;
mailAccounts = nil;
}
}
}
else
{
[self release];
self = nil;
}
{
[self release];
self = nil;
}
return self;
}
@@ -263,14 +265,6 @@
- (NSString *) loginInDomain
{
SOGoSystemDefaults *sd;
NSString *_domain, *realLoginInDomain;
NSRange r;
sd = [SOGoSystemDefaults sharedSystemDefaults];
if ([sd enableDomainBasedUID] || [[sd loginDomains] count] > 0)
{
return self->login;
}
return loginInDomain;
}
@@ -1298,7 +1292,7 @@
return [authValue boolValue];
}
- (NSString *) totpKey: (bool) isCheck
- (NSString *) totpKey
{
#if defined(MFA_CONFIG)
NSString *key, *result;
@@ -1307,51 +1301,6 @@
size_t s_len, secret_len;
//Until 5.12.4, SOGo had two problems with totp:
// * It was not renew after a user disable it/renable it.
// * The length was too small: 12 instead of the recommanded 20
if(![_defaults totpEnabled])
{
//Totp was not enabled
//Only renew if this is not a check (happen when the user enable it for the first time and save its preferences
//the saveAction will check the totp code but [_defaults totpEnabled] is still False )
key = [[self userSettings] userCurrentTotpKey: !isCheck];
}
else
{
//Totp currently enabled
key = [[self userSettings] userCurrentTotpKey: NO];
}
s = [key UTF8String];
s_len = strlen(s);
oath_init();
oath_base32_encode(s,s_len, &secret, &secret_len);
oath_done();
result = [[NSString alloc] initWithBytesNoCopy: secret
length: secret_len
encoding: NSASCIIStringEncoding
freeWhenDone: YES];
return [result autorelease];
#else
return nil;
#endif
}
- (NSString *) oldtotpKey
{
#if defined(MFA_CONFIG)
//Was used before 5.12.5, is to not make obsolete totp profile set before
NSString *key, *result;
const char *s;
char *secret;
size_t s_len, secret_len;
key = [[[self userSettings] userPrivateSalt] substringToIndex: 12];
s = [key UTF8String];
s_len = strlen(s);
@@ -1415,7 +1364,7 @@
return username;
cache = [SOGoCache sharedCache];
cacheKey = [NSString stringWithFormat: @"encrypted_%@",username];
cacheKey = [NSString stringWithFormat: @"%@_%@", kEncryptedUserNamePrefix, username];
exception = nil;
tmp = nil;
@@ -1446,14 +1395,11 @@
NSString *tmp, *cacheKey;
SOGoCache *cache;
if (![[SOGoSystemDefaults sharedSystemDefaults] isURLEncryptionEnabled] ||
[username isEqualToString: @"anonymous"] ||
[[request requestHandlerKey] isEqualToString:@"dav"] ||
[[request requestHandlerPath] hasSuffix: @"freebusy.ifb/ajaxRead"])
if (![[SOGoSystemDefaults sharedSystemDefaults] isURLEncryptionEnabled] || [username isEqualToString: @"anonymous"] || [[request requestHandlerKey] isEqualToString:@"dav"])
return username;
cache = [SOGoCache sharedCache];
cacheKey = [NSString stringWithFormat: @"decrypted_%@", username];
cacheKey = [NSString stringWithFormat: @"%@_%@", kEncryptedUserNamePrefix, username];
exception = nil;
tmp = nil;
+2 -4
View File
@@ -156,14 +156,12 @@ extern NSString *SOGoPasswordRecoverySecondaryEmail;
- (void) setMailComposeFontSize: (int) newValue;
- (int) mailComposeFontSize;
- (void) setMailDisplayFullEmail: (BOOL) newValue;
- (BOOL) mailDisplayFullEmail;
- (void) setMailDisplayFullEmail: (BOOL *) newValue;
- (BOOL *) mailDisplayFullEmail;
- (void) setMailDisplayRemoteInlineImages: (NSString *) newValue;
- (NSString *) mailDisplayRemoteInlineImages;
- (void) setMailDisplayAttachmentAbove: (BOOL) newValue;
- (BOOL) mailDisplayAttachmentAbove;
- (void) setMailAutoMarkAsReadDelay: (int) newValue;
- (int) mailAutoMarkAsReadDelay;
+2 -12
View File
@@ -634,12 +634,12 @@ NSString *SOGoPasswordRecoverySecondaryEmail = @"SecondaryEmail";
return [self integerForKey: @"SOGoMailComposeFontSize"];
}
- (void) setMailDisplayFullEmail: (BOOL) newValue
- (void) setMailDisplayFullEmail: (BOOL *) newValue
{
[self setBool: newValue forKey: @"SOGoMailDisplayFullEmail"];
}
- (BOOL) mailDisplayFullEmail;
- (BOOL *) mailDisplayFullEmail;
{
return [self boolForKey: @"SOGoMailDisplayFullEmail"];
}
@@ -654,16 +654,6 @@ NSString *SOGoPasswordRecoverySecondaryEmail = @"SecondaryEmail";
return [self stringForKey: @"SOGoMailDisplayRemoteInlineImages"];
}
- (void) setMailDisplayAttachmentAbove: (BOOL) newValue
{
[self setBool: newValue forKey: @"SOGoMailDisplayAttachmentAbove"];
}
- (BOOL) mailDisplayAttachmentAbove;
{
return [self boolForKey: @"SOGoMailDisplayAttachmentAbove"];
}
- (void) setMailAutoMarkAsReadDelay: (int) newValue
{
[self setInteger: newValue forKey: @"SOGoMailAutoMarkAsReadDelay"];
-2
View File
@@ -35,8 +35,6 @@
- (NSArray *) subscribedCalendars;
- (NSArray *) subscribedAddressBooks;
- (NSString *) userPrivateSalt;
- (NSString *) userCurrentTotpKey: (bool) renew;
- (void) setTotpKey: (NSString* ) newKey;
- (NSString *) userPublicSalt;
- (void)enableForceResetPassword;
- (void)disableForceResetPassword;
-36
View File
@@ -116,42 +116,6 @@ static Class SOGoUserProfileKlass = Nil;
return salt;
}
- (NSString *) userCurrentTotpKey: (bool) renew
{
NSMutableDictionary *values;
NSString *key;
key = [[self dictionaryForKey: @"General"] objectForKey: @"totpKey"];
if (!key || renew)
{
key = [[[[NSProcessInfo processInfo] globallyUniqueString] asSHA1String] substringToIndex: 20];
values = [self objectForKey: @"General"];
if (!values)
values = [NSMutableDictionary dictionary];
[values setObject: key forKey: @"totpKey"];
[self setObject: values forKey: @"General"];
[self synchronize];
}
return key;
}
- (void) setTotpKey: (NSString* ) newKey
{
NSMutableDictionary *values;
values = [self objectForKey: @"General"];
if (!values)
values = [NSMutableDictionary dictionary];
[values setObject: newKey forKey: @"totpKey"];
[self setObject: values forKey: @"General"];
[self synchronize];
}
- (void) enableForceResetPassword
{
[self setObject: [NSNumber numberWithInt:1] forKey: @"ForceResetPassword"];
+21 -49
View File
@@ -225,16 +225,9 @@
*/
- (NSString *) _encryptPassword: (NSString *) plainPassword
{
NSString *pass, *passwordScheme;
NSString *pass;
NSString* result;
// if ([_userPasswordAlgorithm caseInsensitiveCompare: @"none"] == NSOrderedSame ||
// [_userPasswordAlgorithm caseInsensitiveCompare: @"plain"] == NSOrderedSame ||
// [_userPasswordAlgorithm caseInsensitiveCompare: @"cleartext"] == NSOrderedSame)
// {
// pass = [pass stringByReplacingString: @"'" withString: @"''"];
// }
pass = [plainPassword asCryptedPassUsingScheme: _userPasswordAlgorithm
keyPath: _keyPath];
@@ -279,7 +272,6 @@
grace: (int *) _grace
disablepasswordPolicyCheck: (BOOL) _disablepasswordPolicyCheck
{
EOAdaptor *adaptor;
EOAdaptorChannel *channel;
EOQualifier *qualifier;
GCSChannelManager *cm;
@@ -289,13 +281,11 @@
rc = NO;
_login = [_login stringByReplacingString: @"'" withString: @"''"];
cm = [GCSChannelManager defaultChannelManager];
channel = [cm acquireOpenChannelForURL: _viewURL];
if (channel)
{
EOAdaptorContext *adaptorCtx;
adaptorCtx = [channel adaptorContext];
adaptor = [adaptorCtx adaptor];
if (_loginFields)
{
NSMutableArray *qualifiers;
@@ -334,8 +324,7 @@
nil];
[qualifier autorelease];
}
[qualifier appendSQLToString: sql
withAdaptor: adaptor];
[qualifier appendSQLToString: sql];
ex = [channel evaluateExpressionX: sql];
if (!ex)
@@ -441,11 +430,9 @@
{
BOOL didChange, isOldPwdOk, isPolicyOk;
EOAdaptorChannel *channel;
EOAdaptor *adaptor;
EOQualifier *qualifier_login, *qualifier_pwd;
GCSChannelManager *cm;
NSException *ex;
NSMutableString *sqlstr;
NSString *sqlstr;
*perr = -1;
isOldPwdOk = NO;
@@ -468,28 +455,16 @@
return NO;
// Save new password
// login = [login stringByReplacingString: @"'" withString: @"''"];
login = [login stringByReplacingString: @"'" withString: @"''"];
cm = [GCSChannelManager defaultChannelManager];
channel = [cm acquireOpenChannelForURL: _viewURL];
if (channel)
{
EOAdaptorContext *adaptorCtx;
adaptorCtx = [channel adaptorContext];
adaptor = [adaptorCtx adaptor];
sqlstr = [NSMutableString stringWithFormat: @"UPDATE %@ SET ",
[_viewURL gcsTableName]];
qualifier_pwd = [[EOKeyValueQualifier alloc] initWithKey: @"c_password"
operatorSelector: EOQualifierOperatorEqual
value: encryptedPassword];
[qualifier_pwd appendSQLToString: sqlstr
withAdaptor: adaptor];
[sqlstr appendString: @" WHERE "];
qualifier_login = [[EOKeyValueQualifier alloc] initWithKey: @"c_uid"
operatorSelector: EOQualifierOperatorEqual
value: login];
[qualifier_login appendSQLToString: sqlstr
withAdaptor: adaptor];
sqlstr = [NSString stringWithFormat: (@"UPDATE %@"
@" SET c_password = '%@'"
@" WHERE c_uid = '%@'"),
[_viewURL gcsTableName], encryptedPassword, login];
ex = [channel evaluateExpressionX: sqlstr];
if (!ex)
{
@@ -1023,9 +998,7 @@
inDomain: (NSString *)domain
limit: (int)limit
{
EOAdaptor *adaptor;
EOAdaptorChannel *channel;
EOQualifier *qualifier;
NSEnumerator *criteriaList;
NSMutableArray *fields, *results;
GCSChannelManager *cm;
@@ -1041,14 +1014,12 @@
channel = [cm acquireOpenChannelForURL: _viewURL];
if (channel)
{
EOAdaptorContext *adaptorCtx;
adaptorCtx = [channel adaptorContext];
adaptor = [adaptorCtx adaptor];
fields = [NSMutableArray array];
if ([filter length])
{
filter = [[filter asSafeSQLString] stringByReplacingString: @"\%" withString: @"%%"];
filterFormat = [NSString stringWithFormat: @"(%%@ isCaseInsensitiveLike: '*%@*')", filter];
lowerFilter = [filter lowercaseString];
lowerFilter = [lowerFilter asSafeSQLLikeString];
filterFormat = [NSString stringWithFormat: @"LOWER(%%@) LIKE '%%%%%@%%%%'", lowerFilter];
if (criteria)
criteriaList = [criteria objectEnumerator];
else
@@ -1073,9 +1044,7 @@
if ([fields count])
{
qs = [[[fields uniqueObjects] stringsWithFormat: filterFormat] componentsJoinedByString: @" OR "];
qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
[qualifier appendSQLToString: sql
withAdaptor: adaptor];
[sql appendString: qs];
}
else
[sql appendString: @"1 = 1"];
@@ -1139,7 +1108,7 @@
andSortOrdering: (EOSortOrdering *) ordering
inDomain: (NSString *) domain
{
EOAdaptor *adaptor;
static EOAdaptor *adaptor = nil;
NSException *ex;
NSMutableArray *results;
NSMutableString *sql;
@@ -1154,9 +1123,12 @@
channel = [cm acquireOpenChannelForURL: _viewURL];
if (channel)
{
EOAdaptorContext *adaptorCtx;
adaptorCtx = [channel adaptorContext];
adaptor = [adaptorCtx adaptor];
if (!adaptor)
{
EOAdaptorContext *adaptorCtx;
adaptorCtx = [channel adaptorContext];
adaptor = [adaptorCtx adaptor];
}
sql = [NSMutableString stringWithFormat: @"SELECT c_name FROM %@ WHERE (", [_viewURL gcsTableName]];
if (qualifier)
+2 -19
View File
@@ -172,9 +172,6 @@
EOAdaptorContext *ac;
EOAdaptorChannel *fc;
NSString *profileURL, *sql, *sqlFromUserID, *sqlToUserID;
NSString *old_c_default, *old_c_settings, *new_c_default, *new_c_settings;
NSArray *attrs;
NSDictionary *row;
NSException *sqlError;
SOGoSystemDefaults *sd;
@@ -191,22 +188,9 @@
[ac beginTransaction];
sql = [NSString stringWithFormat: @"SELECT c_defaults, c_settings FROM %@"
sql = [NSString stringWithFormat: @"UPDATE %@ SET c_uid = '%@'"
@" WHERE c_uid = '%@'",
[profileLocation gcsTableName], sqlFromUserID];
sqlError = [fc evaluateExpressionX: sql];
attrs = [fc describeResults: NO];
while ((row = [fc fetchAttributes: attrs withZone: NULL]))
{
old_c_default = [row objectForKey: @"c_defaults"];
old_c_settings = [row objectForKey: @"c_settings"];
new_c_default = [old_c_default stringByReplacingOccurrencesOfString:fromUserID withString:toUserID];
new_c_settings = [old_c_settings stringByReplacingOccurrencesOfString:fromUserID withString:toUserID];
}
sql = [NSString stringWithFormat: @"UPDATE %@ SET c_uid = '%@', c_defaults = '%@', "
@"c_settings = '%@' WHERE c_uid = '%@'",
[profileLocation gcsTableName], sqlToUserID, new_c_default, new_c_settings, sqlFromUserID];
[profileLocation gcsTableName], sqlToUserID, sqlFromUserID];
sqlError = [fc evaluateExpressionX: sql];
if (sqlError)
{
@@ -584,7 +568,6 @@
[self _updateForeignSubscriptionsFromUser: oldUserID toUser: newUserID];
[self _updateLocalACLsFromUser: oldUserID toUser: newUserID];
[self _updateForeignACLsFromUser: oldUserID toUser: newUserID];
[self _updateOldUserIDDefaultAndSettings: oldUserID toUser: newUserID];
rc = YES;
}
Loaded 100 of 323 files, more files were not shown because too many files have changed in this diff. Show more