diff --git a/SoObjects/Mailer/SOGoMailFolder.m b/SoObjects/Mailer/SOGoMailFolder.m index b3c50fa87..280f12705 100644 --- a/SoObjects/Mailer/SOGoMailFolder.m +++ b/SoObjects/Mailer/SOGoMailFolder.m @@ -21,7 +21,7 @@ */ #import -#import +#import #import #import @@ -65,6 +65,7 @@ #import #import #import +#import #import "EOQualifier+MailDAV.h" #import "SOGoMailAccount.h" @@ -523,9 +524,9 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) NSDictionary *msgs; NSArray *messages; NSData *content, *zipContent; - NSTask *zipTask; - NSMutableArray *zipTaskArguments; WOResponse *response; + SOGoZipArchiver *archiver; + NSFileHandle *zipFileHandle; int i; if (!archiveName) @@ -540,21 +541,15 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) return (WOResponse *)error; } - zipPath = [[SOGoSystemDefaults sharedSystemDefaults] zipPath]; fm = [NSFileManager defaultManager]; - if (![fm fileExistsAtPath: zipPath]) - { + zipPath = [NSString stringWithFormat: @"%@/%@", spoolPath, archiveName]; + archiver = [SOGoZipArchiver archiverAtPath: zipPath]; + if (archiver == nil) { + [self errorWithFormat: @"Failed to create zip archive at %@", spoolPath]; error = [NSException exceptionWithHTTPStatus: 500 - reason: @"zip not available"]; + reason: @"Internal server error"]; return (WOResponse *)error; - } - - zipTask = [[NSTask alloc] init]; - [zipTask setCurrentDirectoryPath: spoolPath]; - [zipTask setLaunchPath: zipPath]; - - zipTaskArguments = [NSMutableArray arrayWithObjects: nil]; - [zipTaskArguments addObject: @"SavedMessages.zip"]; + } msgs = (NSDictionary *)[self fetchUIDs: uids parts: [NSArray arrayWithObject: @"BODY.PEEK[]"]]; @@ -563,30 +558,26 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) for (i = 0; i < [messages count]; i++) { content = [[[messages objectAtIndex: i] objectForKey: @"body[]"] objectForKey: @"data"]; - fileName = [NSString stringWithFormat:@"%@/%@.eml", spoolPath, [uids objectAtIndex: i]];; - [content writeToFile: fileName atomically: YES]; - - [zipTaskArguments addObject: - [NSString stringWithFormat:@"%@.eml", [uids objectAtIndex: i]]]; - } - - [zipTask setArguments: zipTaskArguments]; - [zipTask launch]; - [zipTask waitUntilExit]; - - [zipTask release]; - - zipContent = [[NSData alloc] initWithContentsOfFile: - [NSString stringWithFormat: @"%@/SavedMessages.zip", spoolPath]]; - - for (i = 0; i < [zipTaskArguments count]; i++) - { - fileName = [zipTaskArguments objectAtIndex: i]; - [fm removeFileAtPath: - [NSString stringWithFormat: @"%@/%@", spoolPath, fileName] handler: nil]; + fileName = [NSString stringWithFormat:@"%@.eml", [uids objectAtIndex: i]]; + [archiver putFileWithName: fileName andData: content]; } + [archiver close]; + response = [context response]; + + // Check if SOPE has support for serving files directly + if ([response respondsToSelector: @selector(setContentFile:)]) { + zipFileHandle = [NSFileHandle fileHandleForReadingAtPath: zipPath]; + [response setContentFile: zipFileHandle]; + } else { + zipContent = [[NSData alloc] initWithContentsOfFile:zipPath]; + [response setContent:zipContent]; + [zipContent release]; + } + + [fm removeFileAtPath: zipPath handler: nil]; + baseName = [archiveName stringByDeletingPathExtension]; extension = [archiveName pathExtension]; if ([extension length] > 0) @@ -604,9 +595,6 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) [response setHeader: [NSString stringWithFormat: @"attachment; filename=\"%@\"", qpFileName] forKey: @"Content-Disposition"]; - [response setContent: zipContent]; - - [zipContent release]; return response; } diff --git a/SoObjects/Mailer/SOGoMailObject.m b/SoObjects/Mailer/SOGoMailObject.m index 1c3d23d99..d96685335 100644 --- a/SoObjects/Mailer/SOGoMailObject.m +++ b/SoObjects/Mailer/SOGoMailObject.m @@ -20,9 +20,9 @@ 02111-1307, USA. */ -#import #import #import +#import #import #import @@ -49,6 +49,7 @@ #import #import #import +#import #import "NSString+Mail.h" #import "NSData+Mail.h" @@ -962,17 +963,18 @@ static BOOL debugSoParts = NO; - (WOResponse *) archiveAllFilesinArchiveNamed: (NSString *) archiveName { +#warning duplicated code from [SOGoMailFolder archiveUIDs] NSArray *attachments; NSData *body, *zipContent; NSDictionary *currentAttachment; NSException *error; NSFileManager *fm; - NSMutableArray *zipTaskArguments; - NSString *spoolPath, *name, *fileName, *baseName, *extension, *zipPath, *qpFileName; - NSTask *zipTask; + NSString *spoolPath, *name, *baseName, *extension, *zipPath, *qpFileName; SOGoMailFolder *folder; WOResponse *response; unsigned int max, count; + SOGoZipArchiver *archiver; + NSFileHandle *zipFileHandle;; if (!archiveName) archiveName = @"attachments.zip"; @@ -988,22 +990,15 @@ static BOOL debugSoParts = NO; return (WOResponse *)error; } - // Prepare execution of zip - zipPath = [[SOGoSystemDefaults sharedSystemDefaults] zipPath]; fm = [NSFileManager defaultManager]; - if (![fm fileExistsAtPath: zipPath]) - { + zipPath = [NSString stringWithFormat: @"%@/%@", spoolPath, archiveName]; + archiver = [SOGoZipArchiver archiverAtPath: zipPath]; + if (archiver == nil) { + [self errorWithFormat: @"Failed to create zip archive at %@", spoolPath]; error = [NSException exceptionWithHTTPStatus: 500 - reason: @"zip not available"]; + reason: @"Internal server error"]; return (WOResponse *)error; - } - - zipTask = [[NSTask alloc] init]; - [zipTask setCurrentDirectoryPath: spoolPath]; - [zipTask setLaunchPath: zipPath]; - - zipTaskArguments = [NSMutableArray arrayWithObjects: nil]; - [zipTaskArguments addObject: @"attachments.zip"]; + } // Fetch attachments and write them on disk attachments = [self fetchFileAttachments]; @@ -1013,32 +1008,25 @@ static BOOL debugSoParts = NO; currentAttachment = [attachments objectAtIndex: count]; body = [currentAttachment objectForKey: @"body"]; name = [[currentAttachment objectForKey: @"filename"] asSafeFilename]; - - fileName = [NSString stringWithFormat:@"%@/%@", spoolPath, name]; - [body writeToFile: fileName atomically: YES]; - - [zipTaskArguments addObject: [NSString stringWithFormat: @"./%@", name]]; + [archiver putFileWithName: name andData: body]; } - // Zip files - [zipTask setArguments: zipTaskArguments]; - [zipTask launch]; - [zipTask waitUntilExit]; - [zipTask release]; - zipContent = [[NSData alloc] initWithContentsOfFile: - [NSString stringWithFormat: @"%@/attachments.zip", spoolPath]]; + [archiver close]; - // Delete attachments from disk - max = [zipTaskArguments count]; - for (count = 0; count < max; count++) - { - fileName = [zipTaskArguments objectAtIndex: count]; - [fm removeFileAtPath: - [NSString stringWithFormat: @"%@/%@", spoolPath, fileName] handler: nil]; - } - - // Prepare response response = [context response]; + + // Check if SOPE has support for serving files directly + if ([response respondsToSelector: @selector(setContentFile:)]) { + zipFileHandle = [NSFileHandle fileHandleForReadingAtPath: zipPath]; + [response setContentFile: zipFileHandle]; + } else { + zipContent = [[NSData alloc] initWithContentsOfFile:zipPath]; + [response setContent:zipContent]; + [zipContent release]; + } + + [fm removeFileAtPath: zipPath handler: nil]; + baseName = [archiveName stringByDeletingPathExtension]; extension = [archiveName pathExtension]; if ([extension length] > 0) @@ -1054,9 +1042,6 @@ static BOOL debugSoParts = NO; [response setHeader: [NSString stringWithFormat: @"attachment; filename=\"%@\"", qpFileName] forKey: @"Content-Disposition"]; - [response setContent: zipContent]; - - [zipContent release]; return response; } diff --git a/SoObjects/SOGo/GNUmakefile b/SoObjects/SOGo/GNUmakefile index df32913e9..6003f8e2a 100644 --- a/SoObjects/SOGo/GNUmakefile +++ b/SoObjects/SOGo/GNUmakefile @@ -86,7 +86,8 @@ SOGo_HEADER_FILES = \ WOContext+SOGo.h \ \ SOGoCredentialsFile.h \ - SOGoTextTemplateFile.h + SOGoTextTemplateFile.h \ + SOGoZipArchiver.h all:: @touch SOGoBuild.m @@ -165,7 +166,8 @@ SOGo_OBJC_FILES = \ WOContext+SOGo.m \ \ SOGoCredentialsFile.m \ - SOGoTextTemplateFile.m + SOGoTextTemplateFile.m \ + SOGoZipArchiver.m SOGo_C_FILES += lmhash.c aes.c crypt_blowfish.c diff --git a/SoObjects/SOGo/SOGoZipArchiver.h b/SoObjects/SOGo/SOGoZipArchiver.h new file mode 100644 index 000000000..197b6aeaa --- /dev/null +++ b/SoObjects/SOGo/SOGoZipArchiver.h @@ -0,0 +1,38 @@ +/* SOGoZipArchiver.h - this file is part of SOGo + * + * Copyright (C) 2020 Inverse inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef SOGOZIPARCHIVER_H +#define SOGOZIPARCHIVER_H + +#include + +@interface SOGoZipArchiver : NSObject +{ + zip_t *zip; +} + +- (id) initFromFile: (NSString *) file; ++ (id) archiverAtPath: (NSString *) file; + +- (BOOL) putFileWithName: (NSString *) filename andData: (NSData *) data; +- (BOOL) close; +@end + +#endif /* SOGOZIPARCHIVER_H */ diff --git a/SoObjects/SOGo/SOGoZipArchiver.m b/SoObjects/SOGo/SOGoZipArchiver.m new file mode 100644 index 000000000..17fa91999 --- /dev/null +++ b/SoObjects/SOGo/SOGoZipArchiver.m @@ -0,0 +1,107 @@ +/* SOGoZipArchiver.m - this file is part of SOGo + * + * Copyright (C) 2020 Inverse inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import + +#import "SOGoZipArchiver.h" + +@implementation SOGoZipArchiver + ++ (id)archiverAtPath:(NSString *)file +{ + id newArchiver = [[self alloc] initFromFile: file]; + [newArchiver autorelease]; + return newArchiver; +} + +- (id)init +{ + if ((self = [super init])) { + zip = NULL; + } + return self; +} + +- (void)dealloc +{ + [self close]; + [super dealloc]; +} + +- (id)initFromFile:(NSString *)file +{ + id ret; + + ret = nil; + if (file) { + if ((self = [self init])) { + int errorp; + self->zip = zip_open([file cString], ZIP_CREATE | ZIP_EXCL, &errorp); + if (self->zip == NULL) { + zip_error_t ziperror; + zip_error_init_with_code(&ziperror, errorp); + NSLog(@"Failed to open zip output file %@: %@", file, + [NSString stringWithCString: zip_error_strerror(&ziperror)]); + } else { + ret = self; + } + } + } + + return ret; +} + +- (BOOL)putFileWithName:(NSString *)filename andData:(NSData *)data +{ + if (self->zip == NULL) { + NSLog(@"Failed to add file, archive is not open"); + return NO; + } + + zip_source_t *source = zip_source_buffer(self->zip, [data bytes], [data length], 0); + if (source == NULL) { + NSLog(@"Failed to create zip source from buffer: %@", [NSString stringWithCString: zip_strerror(self->zip)]); + return NO; + } + + if (zip_file_add(self->zip, [filename UTF8String], source, ZIP_FL_ENC_UTF_8) < 0) { + NSLog(@"Failed to add file %@: %@", filename, [NSString stringWithCString: zip_strerror(self->zip)]); + zip_source_free(source); + } + + return YES; +} + +- (BOOL)close +{ + BOOL success = YES; + if (self->zip != NULL) { + if (zip_close(zip) != 0) { + NSLog(@"Failed to close zip archive: %@", [NSString stringWithCString: zip_strerror(self->zip)]); + zip_discard(self->zip); + success = NO; + } + self->zip = NULL; + } + return success; +} + +@end diff --git a/configure b/configure index 19dd92338..88e359528 100755 --- a/configure +++ b/configure @@ -391,7 +391,7 @@ EOF } checkDependencies() { - cfgwrite "BASE_LIBS := `gnustep-config --base-libs`" + cfgwrite "BASE_LIBS := `gnustep-config --base-libs` -lzip" if test "x$ARG_ENABLE_SAML2" = "x1"; then checkLinking "lasso" required; if test $? = 0; then diff --git a/packaging/debian/control b/packaging/debian/control index bc1a5d28a..f05262686 100644 --- a/packaging/debian/control +++ b/packaging/debian/control @@ -1,14 +1,14 @@ Source: sogo Priority: optional Maintainer: Inverse Support -Build-Depends: debhelper (>= 7.0.15), gobjc | objc-compiler, libgnustep-base-dev, libsope-appserver4.9-dev, libsope-core4.9-dev, libsope-gdl1-4.9-dev, libsope-ldap4.9-dev, libsope-mime4.9-dev, libsope-xml4.9-dev, libmemcached-dev, libxml2-dev, libsbjson-dev, libssl-dev, libcurl4-openssl-dev | libcurl4-gnutls-dev, libwbxml2-dev (>= 0.11.2), liblasso3-dev (>= 2.3.5) +Build-Depends: debhelper (>= 7.0.15), gobjc | objc-compiler, libgnustep-base-dev, libsope-appserver4.9-dev, libsope-core4.9-dev, libsope-gdl1-4.9-dev, libsope-ldap4.9-dev, libsope-mime4.9-dev, libsope-xml4.9-dev, libmemcached-dev, libxml2-dev, libsbjson-dev, libssl-dev, libcurl4-openssl-dev | libcurl4-gnutls-dev, libwbxml2-dev (>= 0.11.2), liblasso3-dev (>= 2.3.5), libzip-dev Section: web Standards-Version: 3.9.1 Package: sogo Section: web Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, sope4.9-libxmlsaxdriver, sope4.9-db-connector, gnustep-make, libcurl3 | libcurl4, zip, liblasso3 (>= 2.3.5) +Depends: ${shlibs:Depends}, ${misc:Depends}, sope4.9-libxmlsaxdriver, sope4.9-db-connector, gnustep-make, libcurl3 | libcurl4, libzip4, liblasso3 (>= 2.3.5) Recommends: memcached, apache2 | nginx | httpd Description: a modern and scalable groupware SOGo is a groupware server built around OpenGroupware.org (OGo) and