feat(mail): initial support for ms-tnef (winmail.dat) body part
Fixes #2242 Fixes #4503pull/301/head
parent
98dad1d2dc
commit
045f134321
|
@ -30,6 +30,7 @@ Mailer_OBJC_FILES += \
|
|||
SOGoImageMailBodyPart.m \
|
||||
SOGoMessageMailBodyPart.m \
|
||||
SOGoCalendarMailBodyPart.m \
|
||||
SOGoTNEFMailBodyPart.m \
|
||||
SOGoVCardMailBodyPart.m \
|
||||
\
|
||||
SOGoMailForward.m \
|
||||
|
|
|
@ -157,8 +157,7 @@ static BOOL debugOn = NO;
|
|||
{
|
||||
if (!partInfo)
|
||||
{
|
||||
partInfo
|
||||
= [[self mailObject] lookupInfoForBodyPart: [self bodyPartPath]];
|
||||
partInfo = [[self mailObject] lookupInfoForBodyPart: [self bodyPartPath]];
|
||||
[partInfo retain];
|
||||
}
|
||||
|
||||
|
@ -546,7 +545,7 @@ static BOOL debugOn = NO;
|
|||
return self;
|
||||
|
||||
/* hard coded for now */
|
||||
|
||||
|
||||
switch ([pe length]) {
|
||||
case 3:
|
||||
if ([pe isEqualToString:@"gif"] ||
|
||||
|
@ -588,6 +587,8 @@ static BOOL debugOn = NO;
|
|||
classString = @"SOGoVCardMailBodyPart";
|
||||
else if ([mimeType isEqualToString: @"message/rfc822"])
|
||||
classString = @"SOGoMessageMailBodyPart";
|
||||
else if ([mimeType isEqualToString: @"application/ms-tnef"])
|
||||
classString = @"SOGoTNEFMailBodyPart";
|
||||
else
|
||||
{
|
||||
classString = @"SOGoMailBodyPart";
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
Copyright (C) 2021 Inverse inc.
|
||||
|
||||
This file is part of SOGo.
|
||||
|
||||
SOGo 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.
|
||||
|
||||
SOGo 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 SOGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __Mailer_SOGoTNEFMailBodyPart_H__
|
||||
#define __Mailer_SOGoTNEFMailBodyPart_H__
|
||||
|
||||
#import "SOGoMailBodyPart.h"
|
||||
|
||||
@class NGMimeBodyPart;
|
||||
|
||||
@interface SOGoTNEFMailBodyPart : SOGoMailBodyPart
|
||||
{
|
||||
BOOL debugOn;
|
||||
NSData *part;
|
||||
NSString *filename;
|
||||
NGMimeMultipartBody *bodyParts;
|
||||
}
|
||||
|
||||
- (NGMimeMultipartBody *) bodyParts;
|
||||
- (void) setFilename: (NSString *) newFilename;
|
||||
- (void) setPart: (NGMimeBodyPart *) newPart;
|
||||
- (void) setPartInfo: (id) newPartInfo;
|
||||
- (void) decodeBLOB;
|
||||
- (NGMimeBodyPart *) bodyPartForData: (NSData *) _data
|
||||
withType: (NSString *) _type
|
||||
andSubtype: (NSString *) _subtype;
|
||||
- (NGMimeBodyPart *) bodyPartForAttachment: (NSData *) _data
|
||||
withName: (NSString *) _name
|
||||
andType: (NSString *) _type
|
||||
andSubtype: (NSString *) _subtype
|
||||
andContentId: (NSString *) _cid;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __Mailer_SOGoTNEFMailBodyPart_H__ */
|
|
@ -0,0 +1,957 @@
|
|||
/*
|
||||
Copyright (C) 2021 Inverse inc.
|
||||
|
||||
This file is part of SOGo.
|
||||
|
||||
SOGo 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.
|
||||
|
||||
OGo 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 OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
|
||||
The following code is a derivative work of the code from the Yerase's TNEF
|
||||
Stream Reader which is licensed GPLv2.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
#import <NGExtensions/NGHashMap.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#import <NGMime/NGMimeBodyPart.h>
|
||||
#import <NGMime/NGMimeFileData.h>
|
||||
#import <NGMime/NGMimeHeaderFields.h>
|
||||
#import <NGMime/NGMimeMultipartBody.h>
|
||||
|
||||
#import <SOGo/RTFHandler.h>
|
||||
#import <SOGo/SOGoBuild.h>
|
||||
#import <SOGo/SOGoSystemDefaults.h>
|
||||
|
||||
#import <SoObjects/Mailer/NSData+SMIME.h>
|
||||
|
||||
#import <ytnef.h>
|
||||
|
||||
#import "SOGoTNEFMailBodyPart.h"
|
||||
|
||||
#define UPR_TO_ATTENDEES_STRING 0x823B
|
||||
#define UPR_CC_ATTENDEES_STRING 0x823C
|
||||
#define UPR_ALL_ATTENDEES_STRING 0x8238
|
||||
|
||||
/*
|
||||
SOGoTNEFMailBodyPart
|
||||
|
||||
A specialized SOGoMailBodyPart subclass for application/ms-tnef attachments. Can
|
||||
be used to attach special SoMethods.
|
||||
|
||||
See the superclass for more information on part objects.
|
||||
*/
|
||||
|
||||
unsigned char GetRruleCount(unsigned char a, unsigned char b) {
|
||||
return ((a << 8) | b);
|
||||
}
|
||||
|
||||
char *GetRruleDayname(unsigned char a) {
|
||||
static char daystring[25];
|
||||
|
||||
*daystring = 0;
|
||||
|
||||
if (a & 0x01) {
|
||||
strcat(daystring, "SU,");
|
||||
}
|
||||
if (a & 0x02) {
|
||||
strcat(daystring, "MO,");
|
||||
}
|
||||
if (a & 0x04) {
|
||||
strcat(daystring, "TU,");
|
||||
}
|
||||
if (a & 0x08) {
|
||||
strcat(daystring, "WE,");
|
||||
}
|
||||
if (a & 0x10) {
|
||||
strcat(daystring, "TH,");
|
||||
}
|
||||
if (a & 0x20) {
|
||||
strcat(daystring, "FR,");
|
||||
}
|
||||
if (a & 0x40) {
|
||||
strcat(daystring, "SA,");
|
||||
}
|
||||
|
||||
if (strlen(daystring)) {
|
||||
daystring[strlen(daystring) - 1] = 0;
|
||||
}
|
||||
|
||||
return (daystring);
|
||||
}
|
||||
|
||||
unsigned char GetRruleMonthNum(unsigned char a, unsigned char b) {
|
||||
switch (a) {
|
||||
case 0x00:
|
||||
switch (b) {
|
||||
case 0x00:
|
||||
// Jan
|
||||
return (1);
|
||||
case 0xA3:
|
||||
// May
|
||||
return (5);
|
||||
case 0xAE:
|
||||
// Nov
|
||||
return (11);
|
||||
}
|
||||
break;
|
||||
case 0x60:
|
||||
switch (b) {
|
||||
case 0xAE:
|
||||
// Feb
|
||||
return (2);
|
||||
case 0x51:
|
||||
// Jun
|
||||
return (6);
|
||||
}
|
||||
break;
|
||||
case 0xE0:
|
||||
switch (b) {
|
||||
case 0x4B:
|
||||
// Mar
|
||||
return (3);
|
||||
case 0x56:
|
||||
// Sep
|
||||
return (9);
|
||||
}
|
||||
break;
|
||||
case 0x40:
|
||||
switch (b) {
|
||||
case 0xFA:
|
||||
// Apr
|
||||
return (4);
|
||||
}
|
||||
break;
|
||||
case 0x20:
|
||||
if (b == 0xFA) {
|
||||
// Jul
|
||||
return (7);
|
||||
}
|
||||
break;
|
||||
case 0x80:
|
||||
if (b == 0xA8) {
|
||||
// Aug
|
||||
return (8);
|
||||
}
|
||||
break;
|
||||
case 0xA0:
|
||||
if (b == 0xFF) {
|
||||
// Oct
|
||||
return (10);
|
||||
}
|
||||
break;
|
||||
case 0xC0:
|
||||
if (b == 0x56) {
|
||||
return (12);
|
||||
}
|
||||
}
|
||||
|
||||
// Error
|
||||
return (0);
|
||||
}
|
||||
|
||||
@implementation SOGoTNEFMailBodyPart
|
||||
|
||||
/* Overwritten methods */
|
||||
|
||||
- (id) init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
debugOn = [[SOGoSystemDefaults sharedSystemDefaults] tnefDecoderDebugEnabled];
|
||||
part = nil;
|
||||
filename = nil;
|
||||
bodyParts = [[NGMimeMultipartBody alloc] init];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id) initWithName: (NSString *) _name
|
||||
inContainer: (id) _container
|
||||
{
|
||||
self = [super initWithName: _name inContainer: _container];
|
||||
|
||||
[self decodeBLOB];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[part release];
|
||||
[filename release];
|
||||
[bodyParts release];
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (id) lookupName: (NSString *) _key
|
||||
inContext: (id) _ctx
|
||||
acquire: (BOOL) _flag
|
||||
{
|
||||
NSArray *parts;
|
||||
int i;
|
||||
|
||||
if ([self isBodyPartKey: _key])
|
||||
{
|
||||
// _key is an integer
|
||||
parts = [bodyParts parts];
|
||||
i = [_key intValue] - 1;
|
||||
|
||||
if (i > -1 && i < [parts count])
|
||||
{
|
||||
[self setPart: [parts objectAtIndex: i]];
|
||||
return self;
|
||||
}
|
||||
}
|
||||
else if ([_key isEqualToString: [self filename]])
|
||||
{
|
||||
return self;
|
||||
}
|
||||
else if ([_key isEqualToString: @"asAttachment"])
|
||||
{
|
||||
[self setAsAttachment];
|
||||
return self;
|
||||
}
|
||||
|
||||
/* Fallback to super class */
|
||||
return [super lookupName: _key inContext: _ctx acquire: _flag];
|
||||
}
|
||||
|
||||
- (NSData *) fetchBLOB
|
||||
{
|
||||
if (part)
|
||||
return [part body];
|
||||
|
||||
return [super fetchBLOB];
|
||||
}
|
||||
|
||||
- (NSString *) filename
|
||||
{
|
||||
if (filename)
|
||||
return filename;
|
||||
else if (part)
|
||||
return nil; // don't try to fetch the filename from the IMAP body structure
|
||||
else
|
||||
return [super filename];
|
||||
}
|
||||
|
||||
- (id) partInfo
|
||||
{
|
||||
if (partInfo)
|
||||
return partInfo;
|
||||
else if (part)
|
||||
return nil; // don't try to fetch the info from the IMAP body structure
|
||||
else
|
||||
return [super partInfo];
|
||||
}
|
||||
|
||||
/* New methods */
|
||||
|
||||
- (NGMimeMultipartBody *) bodyParts
|
||||
{
|
||||
return bodyParts;
|
||||
}
|
||||
|
||||
- (void) decodeBLOB
|
||||
{
|
||||
NSData *data;
|
||||
NSEnumerator *list;
|
||||
NSString *messageClass;
|
||||
NSString *partName, *type, *subtype;
|
||||
NSString *value, *attendee;
|
||||
RTFHandler *handler;
|
||||
|
||||
DWORD signature;
|
||||
DDWORD *classification;
|
||||
dtr datetime;
|
||||
TNEFStruct tnef;
|
||||
variableLength *attachmentName;
|
||||
variableLength *filedata;
|
||||
variableLength buf;
|
||||
|
||||
[self setPart: nil];
|
||||
partName = nil;
|
||||
data = [self fetchBLOB];
|
||||
|
||||
memcpy(&signature, [data bytes], sizeof(DWORD));
|
||||
if (TNEFCheckForSignature(signature) == 0)
|
||||
{
|
||||
TNEFInitialize(&tnef);
|
||||
tnef.Debug = 0;
|
||||
if (TNEFParseMemory((unsigned char *)[data bytes], [data length], &tnef) != -1)
|
||||
{
|
||||
messageClass = [NSString stringWithCString: tnef.messageClass];
|
||||
|
||||
if (debugOn)
|
||||
{
|
||||
NSLog(@"TNEF message class: %@", messageClass);
|
||||
MAPIPrint(&tnef.MapiProperties);
|
||||
}
|
||||
|
||||
if ([messageClass isEqualToString: @"IPM.Microsoft Mail.Note"])
|
||||
{
|
||||
if (tnef.subject.size > 0)
|
||||
{
|
||||
filedata = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_BINARY, PR_BODY_HTML));
|
||||
if (filedata != MAPI_UNDEFINED)
|
||||
{
|
||||
partName = [NSString stringWithFormat: @"%s.html", tnef.subject.data];
|
||||
data = [NSData dataWithBytes: filedata->data length: filedata->size];
|
||||
}
|
||||
else
|
||||
{
|
||||
filedata = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_BINARY, PR_RTF_COMPRESSED));
|
||||
if (filedata != MAPI_UNDEFINED)
|
||||
{
|
||||
buf.data = DecompressRTF(filedata, &(buf.size));
|
||||
if (buf.data != NULL)
|
||||
{
|
||||
partName = [NSString stringWithFormat: @"%s.html", tnef.subject.data];
|
||||
data = [NSData dataWithBytes: buf.data length: buf.size];
|
||||
|
||||
handler = [[RTFHandler alloc] initWithData: data];
|
||||
AUTORELEASE(handler);
|
||||
data = [handler parse]; // RTF to HTML
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ([data length])
|
||||
{
|
||||
[self bodyPartForData: data
|
||||
withType: @"text"
|
||||
andSubtype: @"html"];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ([messageClass isEqualToString: @"IPM.Microsoft Schedule.MtgRespA"] || // tentative response
|
||||
[messageClass isEqualToString: @"IPM.Microsoft Schedule.MtgRespP"] || // positive (accepted) response
|
||||
[messageClass isEqualToString: @"IPM.Microsoft Schedule.MtgRespN"] || // negative (declined) response
|
||||
[messageClass isEqualToString: @"IPM.Microsoft Schedule.MtgReq"]) // request (invitation)
|
||||
{
|
||||
// Meeting object -- construct text/calendar part
|
||||
|
||||
// Parse HTML body, if any
|
||||
filedata = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_BINARY, PR_BODY_HTML));
|
||||
if (filedata != MAPI_UNDEFINED && filedata->size > 0)
|
||||
{
|
||||
partName = [NSString stringWithFormat: @"%s.html", tnef.subject.data];
|
||||
data = [NSData dataWithBytes: filedata->data length: filedata->size];
|
||||
[self bodyPartForData: data
|
||||
withType: @"text"
|
||||
andSubtype: @"html"];
|
||||
}
|
||||
|
||||
// Create ics attachment
|
||||
NSMutableString *vcalendar = [NSMutableString stringWithString: @"BEGIN:VCALENDAR\n"];
|
||||
BOOL isRequest = [messageClass isEqualToString: @"IPM.Microsoft Schedule.MtgReq"];
|
||||
|
||||
if (isRequest)
|
||||
[vcalendar appendString: @"METHOD:REQUEST\n"];
|
||||
else
|
||||
[vcalendar appendString: @"METHOD:REPLY\n"];
|
||||
[vcalendar appendFormat: @"PRODID:-//Inverse inc./SOGo %@//EN\n", SOGoVersion];
|
||||
[vcalendar appendString: @"VERSION:2.0\n"];
|
||||
[vcalendar appendString: @"BEGIN:VEVENT\n"];
|
||||
|
||||
// UID
|
||||
// TODO: Probably wrong, probably irrelevant
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_BINARY, 0x3));
|
||||
if (filedata == MAPI_UNDEFINED)
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_BINARY, 0x23));
|
||||
if (filedata != MAPI_UNDEFINED && filedata->size > 1)
|
||||
{
|
||||
int i;
|
||||
[vcalendar appendString: @"UID:"];
|
||||
for (i = 0; i < filedata->size; i++)
|
||||
{
|
||||
[vcalendar appendFormat: @"%02X", (unsigned char)filedata->data[i]];
|
||||
}
|
||||
[vcalendar appendString: @"\n"];
|
||||
}
|
||||
|
||||
// Sequence
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_LONG, 0x8201));
|
||||
if (filedata != MAPI_UNDEFINED)
|
||||
{
|
||||
[vcalendar appendFormat: @"SEQUENCE:%i\n", (int)*filedata->data];
|
||||
}
|
||||
|
||||
// Attendee email
|
||||
if (isRequest)
|
||||
{
|
||||
// Organizer
|
||||
filedata = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_BINARY, PR_SENDER_SEARCH_KEY));
|
||||
if (filedata == MAPI_UNDEFINED)
|
||||
{
|
||||
filedata = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_UNICODE, PR_SENT_REPRESENTING_EMAIL_ADDRESS));
|
||||
}
|
||||
if (filedata != MAPI_UNDEFINED && filedata->size > 1)
|
||||
{
|
||||
NSArray *components;
|
||||
NSString *email, *cn;
|
||||
email = [NSString stringWithUTF8String: (const char *)filedata->data];
|
||||
components = [email componentsSeparatedByString: @":"];
|
||||
email = [components objectAtIndex: 0];
|
||||
|
||||
if ([components count] > 1)
|
||||
cn = [components objectAtIndex: 1];
|
||||
else
|
||||
{
|
||||
filedata = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_UNICODE, PR_SENT_REPRESENTING_NAME));
|
||||
if (filedata != MAPI_UNDEFINED && filedata->size > 1)
|
||||
cn = [NSString stringWithUTF8String: (const char *)filedata->data];
|
||||
else
|
||||
cn = email;
|
||||
}
|
||||
[vcalendar appendFormat: @"ORGANIZER;cn=\"%@\":mailto:%@\n", cn, email];
|
||||
}
|
||||
|
||||
// Attendees
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, UPR_TO_ATTENDEES_STRING));
|
||||
if (filedata == MAPI_UNDEFINED)
|
||||
{
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_UNICODE, UPR_TO_ATTENDEES_STRING));
|
||||
}
|
||||
if (filedata == MAPI_UNDEFINED)
|
||||
{
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, UPR_ALL_ATTENDEES_STRING));
|
||||
}
|
||||
if (filedata == MAPI_UNDEFINED)
|
||||
{
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_UNICODE, UPR_ALL_ATTENDEES_STRING));
|
||||
}
|
||||
if (filedata != MAPI_UNDEFINED && filedata->size > 1)
|
||||
{
|
||||
// Required attendees
|
||||
value = [NSString stringWithUTF8String: (const char *)filedata->data];
|
||||
list = [[value componentsSeparatedByString: @";"] objectEnumerator];
|
||||
while ((attendee = [list nextObject]))
|
||||
{
|
||||
attendee = [attendee stringByTrimmingSpaces];
|
||||
[vcalendar appendFormat: @"ATTENDEE;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE;CN=\"%@\":MAILTO:%@\n",
|
||||
attendee, attendee];
|
||||
}
|
||||
|
||||
// Optional attendees
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, UPR_CC_ATTENDEES_STRING));
|
||||
if (filedata == MAPI_UNDEFINED)
|
||||
{
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_UNICODE, UPR_CC_ATTENDEES_STRING));
|
||||
}
|
||||
if (filedata != MAPI_UNDEFINED && filedata->size > 1)
|
||||
{
|
||||
value = [NSString stringWithUTF8String: (const char *)filedata->data];
|
||||
if ([value length])
|
||||
{
|
||||
list = [[value componentsSeparatedByString: @";"] objectEnumerator];
|
||||
while ((attendee = [list nextObject]))
|
||||
{
|
||||
attendee = [attendee stringByTrimmingSpaces];
|
||||
[vcalendar appendFormat: @"ATTENDEE;PARTSTAT=NEEDS-ACTION;ROLE=OPT-PARTICIPANT;RSVP=TRUE;CN=\"%@\":MAILTO:%@\n",
|
||||
attendee, attendee];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Meeting response
|
||||
filedata = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_UNICODE, PR_SENT_REPRESENTING_EMAIL_ADDRESS));
|
||||
if (filedata == MAPI_UNDEFINED)
|
||||
{
|
||||
filedata = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_UNICODE, PR_SENDER_SMTP_ADDRESS));
|
||||
}
|
||||
if (filedata != MAPI_UNDEFINED && filedata->size > 1)
|
||||
{
|
||||
NSString *email, *cn, *partstat;
|
||||
email = [NSString stringWithUTF8String: (const char *)filedata->data];
|
||||
filedata = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_UNICODE, PR_SENT_REPRESENTING_NAME));
|
||||
if (filedata == MAPI_UNDEFINED)
|
||||
{
|
||||
filedata = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_UNICODE, PR_SENDER_NAME));
|
||||
}
|
||||
if (filedata != MAPI_UNDEFINED && filedata->size > 1)
|
||||
cn = [NSString stringWithUTF8String: (const char *)filedata->data];
|
||||
else
|
||||
cn = email;
|
||||
|
||||
switch ([messageClass characterAtIndex: [messageClass length] - 1])
|
||||
{
|
||||
case 'N':
|
||||
partstat = @"DECLINED";
|
||||
break;
|
||||
case 'A':
|
||||
partstat = @"TENTATIVE";
|
||||
break;
|
||||
default:
|
||||
partstat = @"ACCEPTED";
|
||||
}
|
||||
[vcalendar appendFormat: @"ATTENDEE;PARTSTAT=%@;CN=\"%@\":MAILTO:%@\n", partstat, cn, email];
|
||||
}
|
||||
}
|
||||
|
||||
// Summary
|
||||
filedata = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, PR_CONVERSATION_TOPIC));
|
||||
if (filedata == MAPI_UNDEFINED)
|
||||
{
|
||||
filedata = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_UNICODE, PR_CONVERSATION_TOPIC));
|
||||
}
|
||||
if (filedata != MAPI_UNDEFINED && filedata->size > 1)
|
||||
{
|
||||
[vcalendar appendFormat: @"SUMMARY:%@\n", [NSString stringWithUTF8String: (const char *)filedata->data]];
|
||||
}
|
||||
|
||||
// Description
|
||||
filedata = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, 0x3fd9));
|
||||
if (filedata == MAPI_UNDEFINED)
|
||||
{
|
||||
filedata = MAPIFindProperty(&(tnef.MapiProperties), PROP_TAG(PT_UNICODE, 0x3fd9));
|
||||
}
|
||||
if (filedata != MAPI_UNDEFINED && filedata->size > 1)
|
||||
{
|
||||
[vcalendar appendFormat: @"DESCRIPTION:%@\n", [NSString stringWithUTF8String: (const char *)filedata->data]];
|
||||
}
|
||||
|
||||
// Location
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, 0x0002));
|
||||
if (filedata == MAPI_UNDEFINED)
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_STRING8, 0x8208));
|
||||
if (filedata == MAPI_UNDEFINED)
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_UNICODE, 0x0002));
|
||||
if (filedata == MAPI_UNDEFINED)
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_UNICODE, 0x8208));
|
||||
if (filedata != MAPI_UNDEFINED && filedata->size > 1)
|
||||
{
|
||||
[vcalendar appendFormat: @"LOCATION: %@\n", [NSString stringWithUTF8String: (const char *)filedata->data]];
|
||||
}
|
||||
|
||||
// Date start
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_SYSTIME, 0x820d));
|
||||
if (filedata == MAPI_UNDEFINED)
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_SYSTIME, 0x8516));
|
||||
if (filedata != MAPI_UNDEFINED && filedata->size > 1)
|
||||
{
|
||||
MAPISysTimetoDTR(filedata->data, &datetime);
|
||||
[vcalendar appendFormat: @"DTSTART:%04i%02i%02iT%02i%02i%02iZ\n",
|
||||
datetime.wYear, datetime.wMonth, datetime.wDay, datetime.wHour, datetime.wMinute, datetime.wSecond];
|
||||
}
|
||||
|
||||
// Date end
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_SYSTIME, 0x820e));
|
||||
if (filedata == MAPI_UNDEFINED)
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_SYSTIME, 0x8517));
|
||||
if (filedata != MAPI_UNDEFINED && filedata->size > 1)
|
||||
{
|
||||
MAPISysTimetoDTR(filedata->data, &datetime);
|
||||
[vcalendar appendFormat: @"DTEND:%04i%02i%02iT%02i%02i%02iZ\n",
|
||||
datetime.wYear, datetime.wMonth, datetime.wDay, datetime.wHour, datetime.wMinute, datetime.wSecond];
|
||||
}
|
||||
|
||||
// Date stamp
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_SYSTIME, 0x8202));
|
||||
if (filedata == MAPI_UNDEFINED)
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_SYSTIME, 0x001a));
|
||||
if (filedata != MAPI_UNDEFINED && filedata->size > 1)
|
||||
{
|
||||
MAPISysTimetoDTR(filedata->data, &datetime);
|
||||
[vcalendar appendFormat: @"DTSTAMP:%04i%02i%02iT%02i%02i%02iZ\n",
|
||||
datetime.wYear, datetime.wMonth, datetime.wDay, datetime.wHour, datetime.wMinute, datetime.wSecond];
|
||||
}
|
||||
|
||||
// Classification
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_BOOLEAN, 0x8506));
|
||||
if (filedata != MAPI_UNDEFINED)
|
||||
{
|
||||
classification = (DDWORD *)filedata->data;
|
||||
if (*classification == 1)
|
||||
[vcalendar appendString: @"CLASS:PRIVATE\n"];
|
||||
else
|
||||
[vcalendar appendString: @"CLASS:PUBLIC\n"];
|
||||
}
|
||||
|
||||
// Repeating rule
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_BINARY, 0x8216));
|
||||
if (filedata != MAPI_UNDEFINED && filedata->size >= 0x1F)
|
||||
{
|
||||
NSMutableString *rrule = [NSMutableString string];
|
||||
unsigned char *recurData = filedata->data;
|
||||
|
||||
// [vcalendar appendString: @"RRULE:FREQ="];
|
||||
if (recurData[0x04] == 0x0A)
|
||||
{
|
||||
[rrule appendString: @"DAILY"];
|
||||
if (recurData[0x16] == 0x23 || recurData[0x16] == 0x22 || recurData[0x16] == 0x21)
|
||||
{
|
||||
filedata = MAPIFindUserProp(&(tnef.MapiProperties), PROP_TAG(PT_I2, 0x0011));
|
||||
if (filedata != MAPI_UNDEFINED)
|
||||
[rrule appendFormat: @";INTERVAL=%d", *(filedata->data)];
|
||||
if (recurData[0x16] == 0x22 || recurData[0x16] == 0x21)
|
||||
[rrule appendFormat: @";COUNT=%d", GetRruleCount(recurData[0x1B], recurData[0x1A])];
|
||||
}
|
||||
else if (recurData[0x16] == 0x3E)
|
||||
{
|
||||
[rrule appendString: @";BYDAY=MO,TU,WE,TH,FR"];
|
||||
if (recurData[0x1A] == 0x22 || recurData[0x1A] == 0x21)
|
||||
[rrule appendFormat: @";COUNT=%d", GetRruleCount(recurData[0x1F], recurData[0x1E])];
|
||||
|
||||
}
|
||||
}
|
||||
else if (recurData[0x04] == 0x0B)
|
||||
{
|
||||
[rrule appendFormat: @"WEEKLY;INTERVAL=%d;BYDAY=%s", recurData[0x0E], GetRruleDayname(recurData[0x16])];
|
||||
if (recurData[0x1A] == 0x22 || recurData[0x1A] == 0x21)
|
||||
[rrule appendFormat: @";COUNT=%d", GetRruleCount(recurData[0x1F], recurData[0x1E])];
|
||||
}
|
||||
else if (recurData[0x04] == 0x0C)
|
||||
{
|
||||
[rrule appendString: @"MONTHLY"];
|
||||
if (recurData[0x06] == 0x02)
|
||||
{
|
||||
[rrule appendFormat: @";INTERVAL=%d;BYMONTHDAY=%d", recurData[0x0E], recurData[0x16]];
|
||||
if (recurData[0x1A] == 0x22 || recurData[0x1A] == 0x21)
|
||||
[rrule appendFormat: @";COUNT=%d", GetRruleCount(recurData[0x1F], recurData[0x1E])];
|
||||
}
|
||||
else if (recurData[0x06] == 0x03)
|
||||
{
|
||||
[rrule appendFormat: @";BYDAY=%s;BYSETPOS=%d;INTERVAL=%d",
|
||||
GetRruleDayname(recurData[0x16]),
|
||||
recurData[0x1A] == 0x05 ? -1 : recurData[0x1A],
|
||||
recurData[0x0E]];
|
||||
if (recurData[0x1E] == 0x22 || recurData[0x1E] == 0x21)
|
||||
[rrule appendFormat: @";COUNT=%d", GetRruleCount(recurData[0x23], recurData[0x22])];
|
||||
}
|
||||
}
|
||||
else if (recurData[0x04] == 0x0D)
|
||||
{
|
||||
[rrule appendFormat: @"YEARLY;BYMONTH=%d", GetRruleMonthNum(recurData[0x0A], recurData[0x0B])];
|
||||
if (recurData[0x06] == 0x02)
|
||||
[rrule appendFormat: @";BYMONTHDAY=%d", recurData[0x16]];
|
||||
else if (recurData[0x06] == 0x03)
|
||||
[rrule appendFormat: @";BYDAY=%s;BYSETPOS=%d",
|
||||
GetRruleDayname(recurData[0x16]),
|
||||
recurData[0x1A] == 0x05 ? -1 : recurData[0x1A]];
|
||||
if (recurData[0x1E] == 0x22 || recurData[0x1E] == 0x21)
|
||||
[rrule appendFormat: @";COUNT=%d", GetRruleCount(recurData[0x23], recurData[0x22])];
|
||||
}
|
||||
|
||||
if ([rrule length])
|
||||
[vcalendar appendFormat: @"RRULE:FREQ=%@\n", rrule];
|
||||
}
|
||||
|
||||
[vcalendar appendString: @"END:VEVENT\n"];
|
||||
[vcalendar appendString: @"END:VCALENDAR"];
|
||||
|
||||
if (debugOn)
|
||||
NSLog(@"TNEF reconstructed vCalendar:\n%@", vcalendar);
|
||||
|
||||
[self bodyPartForData: [vcalendar dataUsingEncoding: NSUTF8StringEncoding]
|
||||
withType: @"text"
|
||||
andSubtype: @"calendar"];
|
||||
|
||||
}
|
||||
// Other classes to handle:
|
||||
//
|
||||
// IPM.Contact
|
||||
// IPM.Task
|
||||
// IPM.Microsoft Schedule.MtgCncl
|
||||
// IPM.Appointment
|
||||
//
|
||||
|
||||
Attachment *p;
|
||||
BOOL isObject, isRealAttachment;
|
||||
|
||||
p = tnef.starting_attach.next;
|
||||
while (p != NULL)
|
||||
{
|
||||
if (p->FileData.size > 0)
|
||||
{
|
||||
isObject = YES;
|
||||
|
||||
// See if the contents are stored as "attached data" inside the MAPI blocks.
|
||||
filedata = MAPIFindProperty(&(p->MAPI), PROP_TAG(PT_OBJECT, PR_ATTACH_DATA_OBJ));
|
||||
if (filedata == MAPI_UNDEFINED)
|
||||
{
|
||||
// Nope, standard TNEF stuff.
|
||||
filedata = &(p->FileData);
|
||||
isObject = NO;
|
||||
}
|
||||
|
||||
// See if this is an embedded TNEF stream.
|
||||
isRealAttachment = YES;
|
||||
|
||||
TNEFStruct emb_tnef;
|
||||
DWORD object_signature;
|
||||
|
||||
if (isObject)
|
||||
{
|
||||
// This is an "embedded object", so skip the 16-byte identifier first.
|
||||
memcpy(&object_signature, filedata->data + 16, sizeof(DWORD));
|
||||
if (TNEFCheckForSignature(object_signature) == 0) {
|
||||
TNEFInitialize(&emb_tnef);
|
||||
emb_tnef.Debug = tnef.Debug;
|
||||
if (TNEFParseMemory(filedata->data + 16, filedata->size - 16, &emb_tnef) != -1)
|
||||
{
|
||||
isRealAttachment = NO;
|
||||
}
|
||||
TNEFFree(&emb_tnef);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(&object_signature, filedata->data, sizeof(DWORD));
|
||||
if (TNEFCheckForSignature(object_signature) == 0) {
|
||||
TNEFInitialize(&emb_tnef);
|
||||
emb_tnef.Debug = tnef.Debug;
|
||||
if (TNEFParseMemory(filedata->data, filedata->size, &emb_tnef) != -1)
|
||||
{
|
||||
isRealAttachment = NO;
|
||||
}
|
||||
TNEFFree(&emb_tnef);
|
||||
}
|
||||
}
|
||||
if (isRealAttachment)
|
||||
{
|
||||
// Ok, it's not an embedded stream, so now we process it.
|
||||
attachmentName = MAPIFindProperty(&(p->MAPI), PROP_TAG(PT_STRING8, PR_ATTACH_LONG_FILENAME));
|
||||
if (attachmentName == MAPI_UNDEFINED)
|
||||
{
|
||||
attachmentName = MAPIFindProperty(&(p->MAPI), PROP_TAG(PT_STRING8, PR_DISPLAY_NAME));
|
||||
if (attachmentName == MAPI_UNDEFINED)
|
||||
{
|
||||
attachmentName = MAPIFindProperty(&(p->MAPI), PROP_TAG(PT_STRING8, PR_ATTACH_TRANSPORT_NAME));
|
||||
if (attachmentName == MAPI_UNDEFINED)
|
||||
{
|
||||
attachmentName = &(p->Title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MAPIPrint(&p->MAPI);
|
||||
|
||||
variableLength *prop;
|
||||
|
||||
if (attachmentName->size > 1)
|
||||
{
|
||||
partName = [NSString stringWithUTF8String: (const char *)attachmentName->data];
|
||||
|
||||
type = @"application";
|
||||
subtype = @"octet-stream";
|
||||
|
||||
prop = MAPIFindProperty(&(p->MAPI), PROP_TAG(PT_UNICODE, PR_ATTACH_MIME_TAG));
|
||||
if (prop != MAPI_UNDEFINED)
|
||||
{
|
||||
NSString *mime = [NSString stringWithUTF8String: (const char *)prop->data];
|
||||
NSArray *pair = [mime componentsSeparatedByString: @"/"];
|
||||
if ([pair count] == 2)
|
||||
{
|
||||
type = [pair objectAtIndex: 0];
|
||||
subtype = [pair objectAtIndex: 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self warnWithFormat: @"Unexpected MIME type %@", mime];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
prop = MAPIFindProperty(&(p->MAPI), PROP_TAG(PT_STRING8, PR_ATTACH_EXTENSION));
|
||||
if (prop != MAPI_UNDEFINED)
|
||||
{
|
||||
NSString *ext = [NSString stringWithUTF8String: (const char *)prop->data];
|
||||
if ([ext caseInsensitiveCompare: @".txt"] == NSOrderedSame)
|
||||
{
|
||||
type = @"text";
|
||||
subtype = @"plain";
|
||||
}
|
||||
else
|
||||
{
|
||||
[self warnWithFormat: @"Unidentified extension %@", ext];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSString *cid = partName;
|
||||
prop = MAPIFindProperty(&(p->MAPI), PROP_TAG(PT_UNICODE, 0x3712)); // PR_CONTENT_IDENTIFIER?
|
||||
if (prop != MAPI_UNDEFINED)
|
||||
{
|
||||
cid = [NSString stringWithUTF8String: (const char *)prop->data];
|
||||
}
|
||||
|
||||
NSData *attachment;
|
||||
if (isObject)
|
||||
attachment = [NSData dataWithBytes: filedata->data + 16 length: filedata->size - 16];
|
||||
else
|
||||
attachment = [NSData dataWithBytes: filedata->data length: filedata->size];
|
||||
|
||||
[self bodyPartForAttachment: attachment
|
||||
withName: partName
|
||||
andType: type
|
||||
andSubtype: subtype
|
||||
andContentId: cid];
|
||||
|
||||
// count++;
|
||||
}
|
||||
} // if isRealAttachment
|
||||
} // if size>0
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
TNEFFree(&tnef);
|
||||
}
|
||||
}
|
||||
|
||||
- (void) setPart: (NGMimeBodyPart *) newPart
|
||||
{
|
||||
ASSIGN (part, newPart);
|
||||
if (newPart)
|
||||
{
|
||||
[self setFilename: [[newPart bodyInfo] filename]];
|
||||
[self setPartInfo: [newPart bodyInfo]];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setFilename: nil];
|
||||
[self setPartInfo: nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) setFilename: (NSString *) newFilename
|
||||
{
|
||||
ASSIGN (filename, newFilename);
|
||||
}
|
||||
|
||||
- (void) setPartInfo: (id) newPartInfo
|
||||
{
|
||||
ASSIGN (partInfo, newPartInfo);
|
||||
}
|
||||
|
||||
- (NSString *) contentDispositionForAttachmentWithName: (NSString *) _name
|
||||
andSize: (NSNumber *) _size
|
||||
andContentType: (NSString *) _type
|
||||
{
|
||||
NSString *cdtype, *cd;
|
||||
|
||||
if (([_type caseInsensitiveCompare: @"image"] == NSOrderedSame) ||
|
||||
([_type caseInsensitiveCompare: @"message"] == NSOrderedSame))
|
||||
cdtype = @"inline";
|
||||
else
|
||||
cdtype = @"attachment";
|
||||
|
||||
cd = [NSString stringWithFormat: @"%@; filename=\"%@\"; size=%i;",
|
||||
cdtype, [_name stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""], [_size intValue]];
|
||||
|
||||
return cd;
|
||||
}
|
||||
|
||||
- (NGMimeBodyPart *) bodyPartForData: (NSData *) _data
|
||||
withType: (NSString *) _type
|
||||
andSubtype: (NSString *) _subtype
|
||||
{
|
||||
NGMutableHashMap *map;
|
||||
NGMimeBodyPart *bodyPart;
|
||||
NSData *content;
|
||||
id body;
|
||||
|
||||
if (_data == nil) return nil;
|
||||
|
||||
/* check attachment */
|
||||
|
||||
/* prepare header of body part */
|
||||
|
||||
map = [[[NGMutableHashMap alloc] initWithCapacity: 4] autorelease];
|
||||
|
||||
// Content-Type
|
||||
[map setObject: [NSString stringWithFormat: @"%@/%@", _type, _subtype]
|
||||
forKey: @"content-type"];
|
||||
|
||||
/* prepare body content */
|
||||
|
||||
content = [NSData dataWithBytes: [_data bytes] length: [_data length]];
|
||||
[map setObject: [NSNumber numberWithInt: [content length]]
|
||||
forKey: @"content-length"];
|
||||
|
||||
/* Note: the -init method will create a temporary file! */
|
||||
body = [[[NGMimeFileData alloc] initWithBytes: [content bytes]
|
||||
length: [content length]] autorelease];
|
||||
|
||||
bodyPart = [[[NGMimeBodyPart alloc] initWithHeader: map] autorelease];
|
||||
[bodyPart setBody: body];
|
||||
[bodyParts addBodyPart: bodyPart];
|
||||
|
||||
return bodyPart;
|
||||
}
|
||||
|
||||
- (NGMimeBodyPart *) bodyPartForAttachment: (NSData *) _data
|
||||
withName: (NSString *) _name
|
||||
andType: (NSString *) _type
|
||||
andSubtype: (NSString *) _subtype
|
||||
andContentId: (NSString *) _cid
|
||||
{
|
||||
NGMutableHashMap *map;
|
||||
NGMimeBodyPart *bodyPart;
|
||||
NSData *content;
|
||||
NSString *s;
|
||||
id body;
|
||||
|
||||
if (_name == nil) return nil;
|
||||
|
||||
/* prepare header of body part */
|
||||
|
||||
map = [[[NGMutableHashMap alloc] initWithCapacity: 4] autorelease];
|
||||
|
||||
// Content-Type
|
||||
[map setObject: [NSString stringWithFormat: @"%@/%@", _type, _subtype]
|
||||
forKey: @"content-type"];
|
||||
|
||||
// Content-Id
|
||||
[map setObject: _cid
|
||||
forKey: @"content-id"];
|
||||
|
||||
// Content-Disposition
|
||||
s = [self contentDispositionForAttachmentWithName: _name
|
||||
andSize: [NSNumber numberWithLong: [_data length]]
|
||||
andContentType: _type];
|
||||
NGMimeContentDispositionHeaderField *o;
|
||||
o = [[NGMimeContentDispositionHeaderField alloc] initWithString: s];
|
||||
[map setObject: o forKey: @"content-disposition"];
|
||||
[o release];
|
||||
|
||||
/* prepare body content */
|
||||
|
||||
content = [NSData dataWithBytes: [_data bytes] length: [_data length]];
|
||||
[map setObject: [NSNumber numberWithInt: [content length]]
|
||||
forKey: @"content-length"];
|
||||
|
||||
/* Note: the -init method will create a temporary file! */
|
||||
body = [[NGMimeFileData alloc] initWithBytes: [content bytes]
|
||||
length: [content length]];
|
||||
|
||||
bodyPart = [[[NGMimeBodyPart alloc] initWithHeader: map] autorelease];
|
||||
[bodyPart setBody: body];
|
||||
|
||||
[body release];
|
||||
[bodyParts addBodyPart: bodyPart];
|
||||
|
||||
return bodyPart;
|
||||
}
|
||||
|
||||
@end /* SOGoTNEFMailBodyPart */
|
|
@ -59,6 +59,9 @@
|
|||
SOGoMessageMailBodyPart = {
|
||||
superclass = "SOGoMailBodyPart";
|
||||
};
|
||||
SOGoTNEFMailBodyPart = {
|
||||
superclass = "SOGoMailBodyPart";
|
||||
};
|
||||
SOGoCalendarMailBodyPart = {
|
||||
superclass = "SOGoMailBodyPart";
|
||||
};
|
||||
|
|
|
@ -21,6 +21,7 @@ SOGo_HEADER_FILES = \
|
|||
EOBitmaskQualifier.h \
|
||||
EOQualifier+SOGoCacheObject.h \
|
||||
GCSSpecialQueries+SOGoCacheObject.h \
|
||||
RTFHandler.h \
|
||||
SOGoCache.h \
|
||||
SOGoCacheGCSFolder.h \
|
||||
SOGoCacheGCSObject.h \
|
||||
|
@ -100,6 +101,7 @@ SOGo_OBJC_FILES = \
|
|||
EOBitmaskQualifier.m \
|
||||
EOQualifier+SOGoCacheObject.m \
|
||||
GCSSpecialQueries+SOGoCacheObject.m \
|
||||
RTFHandler.m \
|
||||
SOGoCache.m \
|
||||
SOGoCacheGCSFolder.m \
|
||||
SOGoCacheGCSObject.m \
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
Copyright (C) 2005-2012 Inverse inc.
|
||||
|
||||
This file is part of SOGo.
|
||||
|
||||
SOGo 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.
|
||||
|
||||
SOGo 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 OGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <Foundation/NSArray.h>
|
||||
#include <Foundation/NSData.h>
|
||||
#include <Foundation/NSMapTable.h>
|
||||
#include <Foundation/NSObject.h>
|
||||
#include <Foundation/NSString.h>
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
@class RTFFontTable;
|
||||
|
||||
@interface RTFHandler : NSObject
|
||||
{
|
||||
NSMapTable *_charsets;
|
||||
NSMutableData *_html;
|
||||
NSData *_data;
|
||||
|
||||
const char *_bytes;
|
||||
int _current_pos;
|
||||
int _len;
|
||||
}
|
||||
|
||||
- (id) initWithData: (NSData *) theData;
|
||||
- (NSMutableData *) parse;
|
||||
|
||||
- (RTFFontTable *) parseFontTable;
|
||||
- (void) mangleInternalStateWithBytesPtr: (const char*) newBytes
|
||||
andCurrentPos: (int) newCurrentPos;
|
||||
|
||||
@end
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
@interface RTFStack: NSObject
|
||||
{
|
||||
NSMutableArray *a;
|
||||
}
|
||||
- (void) push: (id) theObject;
|
||||
- (id) pop;
|
||||
@end
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
@interface RTFFormattingOptions : NSObject
|
||||
{
|
||||
@public
|
||||
BOOL bold;
|
||||
BOOL italic;
|
||||
BOOL underline;
|
||||
BOOL strikethrough;
|
||||
int font_index;
|
||||
int color_index;
|
||||
int start_pos;
|
||||
const unsigned short *charset;
|
||||
}
|
||||
@end
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
@interface RTFFontInfo : NSObject
|
||||
{
|
||||
@public
|
||||
NSString *family;
|
||||
unsigned char charset;
|
||||
NSString *name;
|
||||
unsigned int pitch;
|
||||
unsigned int index;
|
||||
}
|
||||
|
||||
- (NSString *) description;
|
||||
@end
|
||||
|
||||
//
|
||||
// \fX - font, index in font table
|
||||
//
|
||||
@interface RTFFontTable : NSObject
|
||||
{
|
||||
@public
|
||||
NSMapTable *fontInfos;
|
||||
}
|
||||
|
||||
- (void) addFontInfo: (RTFFontInfo *) theFontInfo
|
||||
atIndex: (unsigned int ) theIndex;
|
||||
- (RTFFontInfo *) fontInfoAtIndex: (unsigned int ) theIndex;
|
||||
- (NSString *) description;
|
||||
|
||||
@end
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
@interface RTFColorDef : NSObject
|
||||
{
|
||||
@public
|
||||
unsigned char red;
|
||||
unsigned char green;
|
||||
unsigned char blue;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
//
|
||||
// {\colortbl\red0\green0\blue0;\red128\green0\blue0;\red255\green0\blue0;}
|
||||
//
|
||||
// \cfX - color/foreground - index
|
||||
// \cbX - color/background - index
|
||||
//
|
||||
//
|
||||
@interface RTFColorTable : NSObject
|
||||
{
|
||||
@public
|
||||
NSMutableArray *colorDefs;
|
||||
}
|
||||
|
||||
- (void) addColorDef: (RTFColorDef *) theColorDef;
|
||||
|
||||
@end
|
File diff suppressed because it is too large
Load Diff
|
@ -26,6 +26,7 @@
|
|||
SOGoMemcachedHost = "localhost";
|
||||
|
||||
SOGoUIxDebugEnabled = NO;
|
||||
SOGoTnefDecoderDebugEnabled = NO;
|
||||
|
||||
SOGoLDAPContactInfoAttribute = "description";
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* SOGoSystemDefaults.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2009-2016 Inverse inc.
|
||||
* Copyright (C) 2009-2021 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
|
||||
|
@ -65,6 +65,7 @@
|
|||
|
||||
- (BOOL) uixDebugEnabled;
|
||||
- (BOOL) easDebugEnabled;
|
||||
- (BOOL) tnefDecoderDebugEnabled;
|
||||
- (BOOL) xsrfValidationEnabled;
|
||||
|
||||
- (NSString *) pageTitle;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* SOGoSystemDefaults.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2009-2015 Inverse inc.
|
||||
* Copyright (C) 2009-2021 Inverse inc.
|
||||
* Copyright (C) 2012 Jeroen Dekkers <jeroen@dekkers.ch>
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
|
@ -434,6 +434,11 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict,
|
|||
return [self boolForKey: @"SOGoEASDebugEnabled"];
|
||||
}
|
||||
|
||||
- (BOOL) tnefDecoderDebugEnabled
|
||||
{
|
||||
return [self boolForKey: @"SOGoTnefDecoderDebugEnabled"];
|
||||
}
|
||||
|
||||
- (BOOL) xsrfValidationEnabled
|
||||
{
|
||||
id o;
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,21 @@
|
|||
{\rtf1\ansi\deff3\adeflang1025
|
||||
{\fonttbl{\f0\froman\fprq2\fcharset0 Times New Roman;}{\f1\froman\fprq2\fcharset2 Symbol;}{\f2\fswiss\fprq2\fcharset0 Arial;}{\f3\froman\fprq2\fcharset0 Times New Roman;}{\f4\fs
|
||||
wiss\fprq2\fcharset0 Arial;}{\f5\fnil\fprq2\fcharset1 Ubuntu;}{\f6\fnil\fprq2\fcharset0 DejaVu Sans;}{\f7\fnil\fprq2\fcharset0 Lohit Hindi;}{\f8\fnil\fprq0\fcharset1 Lohit Hindi
|
||||
;}}
|
||||
{\colortbl;\red0\green0\blue0;\red128\green128\blue128;}
|
||||
{\stylesheet{\s0\snext0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033 Normal;}
|
||||
{\s15\sbasedon0\snext16\sb240\sa120\keepn\dbch\af6\dbch\af7\afs28\loch\f4\fs28 Heading;}
|
||||
{\s16\sbasedon0\snext16\sb0\sa120 Text Body;}
|
||||
{\s17\sbasedon16\snext17\sb0\sa120\dbch\af8 List;}
|
||||
{\s18\sbasedon0\snext18\sb120\sa120\noline\i\dbch\af8\afs24\ai\fs24 Caption;}
|
||||
{\s19\sbasedon0\snext19\noline\dbch\af8 Index;}
|
||||
}{\info{\creatim\yr2014\mo8\dy4\hr16\min16}{\revtim\yr0\mo0\dy0\hr0\min0}{\printim\yr0\mo0\dy0\hr0\min0}{\comment LibreOffice}{\vern67175170}}\deftab709
|
||||
\viewscale100
|
||||
{\*\pgdsctbl
|
||||
{\pgdsc0\pgdscuse451\pgwsxn11906\pghsxn16838\marglsxn1134\margrsxn1134\margtsxn1134\margbsxn1134\pgdscnxt0 Default Style;}}
|
||||
\formshade\paperh16838\paperw11906\margl1134\margr1134\margt1134\margb1134\sectd\sbknone\sectunlocked1\pgndec\pgwsxn11906\pghsxn16838\marglsxn1134\margrsxn1134\margtsxn1134\marg
|
||||
bsxn1134\ftnbj\ftnstart1\ftnrstcont\ftnnar\aenddoc\aftnrstcont\aftnstart1\aftnnrlc
|
||||
\pgndec\pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033{\rtlch \ltrch\loch\l
|
||||
och\f5000
|
||||
foobar}
|
||||
\par }
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -34,6 +34,8 @@ $(TEST_TOOL)_OBJC_FILES += \
|
|||
TestNGMailAddressParser.m \
|
||||
TestNGInternetSocketAddress.m \
|
||||
\
|
||||
TestRTFHandler.m \
|
||||
\
|
||||
# I don't know how to link against -l:SOGoBackend \
|
||||
undefined reference to `__objc_class_name_SOGoMailFolder'
|
||||
|
||||
|
|
|
@ -0,0 +1,306 @@
|
|||
/* TestRTFHandler.m
|
||||
*
|
||||
* Copyright (C) 2014 Zentyal
|
||||
*
|
||||
* Author: Jesús García Sáez <jgarcia@zentyal.org>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* This file is encoded in utf-8. */
|
||||
|
||||
#import "SOGoTest.h"
|
||||
|
||||
#import <Foundation/NSFileManager.h>
|
||||
#import <Foundation/NSException.h>
|
||||
|
||||
#import <SOGo/RTFHandler.h>
|
||||
|
||||
|
||||
@interface TestRTFHandler : SOGoTest
|
||||
@end
|
||||
|
||||
@implementation TestRTFHandler
|
||||
|
||||
|
||||
- (NSString *) rtf2html: (NSData *) rtf
|
||||
{
|
||||
NSString *html;
|
||||
if (!rtf)
|
||||
return nil;
|
||||
|
||||
RTFHandler *handler = [[RTFHandler alloc] initWithData: rtf];
|
||||
NSMutableData *data = [handler parse];
|
||||
if (data == nil)
|
||||
{
|
||||
NSString *error = [NSString stringWithFormat: @"Couldn't parse RTF data:\n %s",
|
||||
(char *)[rtf bytes]];
|
||||
testWithMessage(NO, error);
|
||||
}
|
||||
|
||||
html = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
|
||||
if (html == nil)
|
||||
{
|
||||
NSString *error = [NSString stringWithFormat: @"Couldn't convert parsed data to UTF8 string"];
|
||||
testWithMessage(NO, error);
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
- (NSData *) dataWithContentsOfFixture: (NSString*) name
|
||||
{
|
||||
NSString *file_path = [NSString stringWithFormat: @"Fixtures/%@", name];
|
||||
|
||||
if(![[NSFileManager defaultManager] fileExistsAtPath: file_path]) {
|
||||
NSString *error = [NSString stringWithFormat: @"File %@ doesn't exist", file_path];
|
||||
testWithMessage(NO, error);
|
||||
}
|
||||
return [NSData dataWithContentsOfFile: file_path];
|
||||
}
|
||||
|
||||
- (NSData *) dataWithContentsOfZentyalCrash: (unsigned int) number
|
||||
{
|
||||
NSString *fixture = [NSString stringWithFormat: @"zentyal_crash_%u.rtf", number];
|
||||
return [self dataWithContentsOfFixture: fixture];
|
||||
|
||||
}
|
||||
|
||||
- (void) checkDoesNotCrash: (unsigned int) number
|
||||
{
|
||||
// FIXME fork
|
||||
[self rtf2html: [self dataWithContentsOfZentyalCrash: number]];
|
||||
}
|
||||
|
||||
- (void) checkHTMLConversionOfRTFFile: (NSString*) file
|
||||
againstExpectedHTML: (NSString*) expected
|
||||
{
|
||||
NSData *in = nil;
|
||||
NSString *out = nil, *error = nil;
|
||||
|
||||
in = [self dataWithContentsOfFixture: file];
|
||||
out = [self rtf2html: in];
|
||||
error = [NSString stringWithFormat:
|
||||
@"Html from rtf result is not what we expected.\n>> Actual:\n%@\n>> Expected:\n%@\n", out, expected];
|
||||
testWithMessage([out isEqualToString: expected], error);
|
||||
}
|
||||
|
||||
- (void) checkFonTableParsingOfRTFFile: (NSString*) file
|
||||
againstExpectedTable: (NSString*) expected
|
||||
{
|
||||
NSData *in = nil;
|
||||
char *in_bytes;
|
||||
char *table_pointer;
|
||||
int newCurrentPos;
|
||||
RTFHandler *handler;
|
||||
RTFFontTable *out_table;
|
||||
NSString *out_description, *error = nil;
|
||||
|
||||
in = [self dataWithContentsOfFixture: file];
|
||||
in_bytes = (char *) [in bytes];
|
||||
table_pointer = strstr(in_bytes, "{\\fonttbl");
|
||||
if (table_pointer == NULL)
|
||||
{
|
||||
[NSException raise: @"NSInvalidArgumentException"
|
||||
format: @"No font table in RTF file"];
|
||||
}
|
||||
newCurrentPos = table_pointer - in_bytes;
|
||||
|
||||
handler = [[RTFHandler alloc] initWithData: in];
|
||||
|
||||
[handler mangleInternalStateWithBytesPtr: table_pointer
|
||||
andCurrentPos: newCurrentPos];
|
||||
out_table = [handler parseFontTable];
|
||||
out_description = [out_table description];
|
||||
if ([out_description isEqualToString: expected])
|
||||
{
|
||||
testWithMessage(YES, @"no error");
|
||||
}
|
||||
else
|
||||
{
|
||||
error = [NSString stringWithFormat:
|
||||
@"Font table is not what we expected.\n>> Actual:\n%@-----\n>> Expected:\n%@-----\n", out_description, expected];
|
||||
testWithMessage(NO, error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
- (void) test_zentyal_crash_2058
|
||||
{
|
||||
[self checkDoesNotCrash: 2058];
|
||||
// Output is not correct... but the original issue was segfault
|
||||
}
|
||||
|
||||
- (void) test_zentyal_crash_2089
|
||||
{
|
||||
NSData *in = nil;
|
||||
NSString *out = nil, *error = nil, *expected = nil;
|
||||
|
||||
in = [self dataWithContentsOfZentyalCrash: 2089];
|
||||
expected =@"<html><meta charset='utf-8'><body><font face=\"Calibri\"><font color=\"#000000\">Lorem Ipsum</font><font color=\"#000000\"><br></font></body></html>";
|
||||
out = [self rtf2html: in];
|
||||
error = [NSString stringWithFormat:
|
||||
@"Html from rtf result `%@` is not what we expected", out];
|
||||
testWithMessage([out isEqualToString: expected], error);
|
||||
}
|
||||
|
||||
- (void) test_zentyal_crash_6330
|
||||
{
|
||||
[self checkDoesNotCrash: 6330];
|
||||
}
|
||||
|
||||
- (void) test_zentyal_crash_8346
|
||||
{
|
||||
[self checkDoesNotCrash: 8346];
|
||||
}
|
||||
|
||||
- (void) test_mini_russian_font_table
|
||||
{
|
||||
NSString *file =@"mini_russian.rtf";
|
||||
NSMutableString *expected = [NSMutableString stringWithFormat: @"Number of fonts: 84\n"];
|
||||
[expected appendString: @"0 name=Times New Roman family=roman charset=0 pitch=2\n"];
|
||||
[expected appendString: @"31500 name=Times New Roman family=roman charset=0 pitch=2\n"];
|
||||
[expected appendString: @"31501 name=Times New Roman family=roman charset=0 pitch=2\n"];
|
||||
[expected appendString: @"31502 name=Cambria family=roman charset=0 pitch=2\n"];
|
||||
[expected appendString: @"37 name=Calibri family=swiss charset=0 pitch=2\n"];
|
||||
[expected appendString: @"31503 name=Times New Roman family=roman charset=0 pitch=2\n"];
|
||||
[expected appendString: @"39 name=Times New Roman CE family=roman charset=238 pitch=2\n"];
|
||||
[expected appendString: @"31504 name=Times New Roman family=roman charset=0 pitch=2\n"];
|
||||
[expected appendString: @"40 name=Times New Roman Cyr family=roman charset=204 pitch=2\n"];
|
||||
[expected appendString: @"31505 name=Times New Roman family=roman charset=0 pitch=2\n"];
|
||||
[expected appendString: @"31506 name=Calibri family=swiss charset=0 pitch=2\n"];
|
||||
[expected appendString: @"42 name=Times New Roman Greek family=roman charset=161 pitch=2\n"];
|
||||
[expected appendString: @"31507 name=Times New Roman family=roman charset=0 pitch=2\n"];
|
||||
[expected appendString: @"31508 name=Times New Roman CE family=roman charset=238 pitch=2\n"];
|
||||
[expected appendString: @"43 name=Times New Roman Tur family=roman charset=162 pitch=2\n"];
|
||||
[expected appendString: @"31509 name=Times New Roman Cyr family=roman charset=204 pitch=2\n"];
|
||||
[expected appendString: @"44 name=Times New Roman (Hebrew) family=roman charset=177 pitch=2\n"];
|
||||
[expected appendString: @"45 name=Times New Roman (Arabic) family=roman charset=178 pitch=2\n"];
|
||||
[expected appendString: @"31511 name=Times New Roman Greek family=roman charset=161 pitch=2\n"];
|
||||
[expected appendString: @"46 name=Times New Roman Baltic family=roman charset=186 pitch=2\n"];
|
||||
[expected appendString: @"31512 name=Times New Roman Tur family=roman charset=162 pitch=2\n"];
|
||||
[expected appendString: @"47 name=Times New Roman (Vietnamese) family=roman charset=163 pitch=2\n"];
|
||||
[expected appendString: @"31513 name=Times New Roman (Hebrew) family=roman charset=177 pitch=2\n"];
|
||||
[expected appendString: @"31514 name=Times New Roman (Arabic) family=roman charset=178 pitch=2\n"];
|
||||
[expected appendString: @"31515 name=Times New Roman Baltic family=roman charset=186 pitch=2\n"];
|
||||
[expected appendString: @"31516 name=Times New Roman (Vietnamese) family=roman charset=163 pitch=2\n"];
|
||||
[expected appendString: @"31518 name=Times New Roman CE family=roman charset=238 pitch=2\n"];
|
||||
[expected appendString: @"31519 name=Times New Roman Cyr family=roman charset=204 pitch=2\n"];
|
||||
[expected appendString: @"31521 name=Times New Roman Greek family=roman charset=161 pitch=2\n"];
|
||||
[expected appendString: @"31522 name=Times New Roman Tur family=roman charset=162 pitch=2\n"];
|
||||
[expected appendString: @"31523 name=Times New Roman (Hebrew) family=roman charset=177 pitch=2\n"];
|
||||
[expected appendString: @"31524 name=Times New Roman (Arabic) family=roman charset=178 pitch=2\n"];
|
||||
[expected appendString: @"31525 name=Times New Roman Baltic family=roman charset=186 pitch=2\n"];
|
||||
[expected appendString: @"31526 name=Times New Roman (Vietnamese) family=roman charset=163 pitch=2\n"];
|
||||
[expected appendString: @"31528 name=Cambria CE family=roman charset=238 pitch=2\n"];
|
||||
[expected appendString: @"31529 name=Cambria Cyr family=roman charset=204 pitch=2\n"];
|
||||
[expected appendString: @"31531 name=Cambria Greek family=roman charset=161 pitch=2\n"];
|
||||
[expected appendString: @"31532 name=Cambria Tur family=roman charset=162 pitch=2\n"];
|
||||
[expected appendString: @"31535 name=Cambria Baltic family=roman charset=186 pitch=2\n"];
|
||||
[expected appendString: @"31536 name=Cambria (Vietnamese) family=roman charset=163 pitch=2\n"];
|
||||
[expected appendString: @"31538 name=Times New Roman CE family=roman charset=238 pitch=2\n"];
|
||||
[expected appendString: @"31539 name=Times New Roman Cyr family=roman charset=204 pitch=2\n"];
|
||||
[expected appendString: @"31541 name=Times New Roman Greek family=roman charset=161 pitch=2\n"];
|
||||
[expected appendString: @"31542 name=Times New Roman Tur family=roman charset=162 pitch=2\n"];
|
||||
[expected appendString: @"31543 name=Times New Roman (Hebrew) family=roman charset=177 pitch=2\n"];
|
||||
[expected appendString: @"31544 name=Times New Roman (Arabic) family=roman charset=178 pitch=2\n"];
|
||||
[expected appendString: @"31545 name=Times New Roman Baltic family=roman charset=186 pitch=2\n"];
|
||||
[expected appendString: @"31546 name=Times New Roman (Vietnamese) family=roman charset=163 pitch=2\n"];
|
||||
[expected appendString: @"31548 name=Times New Roman CE family=roman charset=238 pitch=2\n"];
|
||||
[expected appendString: @"31549 name=Times New Roman Cyr family=roman charset=204 pitch=2\n"];
|
||||
[expected appendString: @"31551 name=Times New Roman Greek family=roman charset=161 pitch=2\n"];
|
||||
[expected appendString: @"31552 name=Times New Roman Tur family=roman charset=162 pitch=2\n"];
|
||||
[expected appendString: @"31553 name=Times New Roman (Hebrew) family=roman charset=177 pitch=2\n"];
|
||||
[expected appendString: @"31554 name=Times New Roman (Arabic) family=roman charset=178 pitch=2\n"];
|
||||
[expected appendString: @"31555 name=Times New Roman Baltic family=roman charset=186 pitch=2\n"];
|
||||
[expected appendString: @"31556 name=Times New Roman (Vietnamese) family=roman charset=163 pitch=2\n"];
|
||||
[expected appendString: @"31558 name=Times New Roman CE family=roman charset=238 pitch=2\n"];
|
||||
[expected appendString: @"31559 name=Times New Roman Cyr family=roman charset=204 pitch=2\n"];
|
||||
[expected appendString: @"31561 name=Times New Roman Greek family=roman charset=161 pitch=2\n"];
|
||||
[expected appendString: @"31562 name=Times New Roman Tur family=roman charset=162 pitch=2\n"];
|
||||
[expected appendString: @"31563 name=Times New Roman (Hebrew) family=roman charset=177 pitch=2\n"];
|
||||
[expected appendString: @"31564 name=Times New Roman (Arabic) family=roman charset=178 pitch=2\n"];
|
||||
[expected appendString: @"31565 name=Times New Roman Baltic family=roman charset=186 pitch=2\n"];
|
||||
[expected appendString: @"31566 name=Times New Roman (Vietnamese) family=roman charset=163 pitch=2\n"];
|
||||
[expected appendString: @"31568 name=Calibri CE family=swiss charset=238 pitch=2\n"];
|
||||
[expected appendString: @"31569 name=Calibri Cyr family=swiss charset=204 pitch=2\n"];
|
||||
[expected appendString: @"31571 name=Calibri Greek family=swiss charset=161 pitch=2\n"];
|
||||
[expected appendString: @"31572 name=Calibri Tur family=swiss charset=162 pitch=2\n"];
|
||||
[expected appendString: @"31575 name=Calibri Baltic family=swiss charset=186 pitch=2\n"];
|
||||
[expected appendString: @"31576 name=Calibri (Vietnamese) family=swiss charset=163 pitch=2\n"];
|
||||
[expected appendString: @"31578 name=Times New Roman CE family=roman charset=238 pitch=2\n"];
|
||||
[expected appendString: @"31579 name=Times New Roman Cyr family=roman charset=204 pitch=2\n"];
|
||||
[expected appendString: @"31581 name=Times New Roman Greek family=roman charset=161 pitch=2\n"];
|
||||
[expected appendString: @"31582 name=Times New Roman Tur family=roman charset=162 pitch=2\n"];
|
||||
[expected appendString: @"31583 name=Times New Roman (Hebrew) family=roman charset=177 pitch=2\n"];
|
||||
[expected appendString: @"31584 name=Times New Roman (Arabic) family=roman charset=178 pitch=2\n"];
|
||||
[expected appendString: @"409 name=Calibri CE family=swiss charset=238 pitch=2\n"];
|
||||
[expected appendString: @"31585 name=Times New Roman Baltic family=roman charset=186 pitch=2\n"];
|
||||
[expected appendString: @"410 name=Calibri Cyr family=swiss charset=204 pitch=2\n"];
|
||||
[expected appendString: @"31586 name=Times New Roman (Vietnamese) family=roman charset=163 pitch=2\n"];
|
||||
[expected appendString: @"412 name=Calibri Greek family=swiss charset=161 pitch=2\n"];
|
||||
[expected appendString: @"413 name=Calibri Tur family=swiss charset=162 pitch=2\n"];
|
||||
[expected appendString: @"416 name=Calibri Baltic family=swiss charset=186 pitch=2\n"];
|
||||
[expected appendString: @"417 name=Calibri (Vietnamese) family=swiss charset=163 pitch=2\n"];
|
||||
|
||||
[self checkFonTableParsingOfRTFFile: file
|
||||
againstExpectedTable: expected];
|
||||
}
|
||||
|
||||
- (void) test_mini_russian
|
||||
{
|
||||
NSString *file =@"mini_russian.rtf";
|
||||
NSString *expected=@"<html><meta charset='utf-8'><body><font face=\"Calibri\"><font face=\"Calibri Cyr\"><font color=\"#000000\">XXзык польски, польщизнаXX</font></font></font></body></html>";
|
||||
[self checkHTMLConversionOfRTFFile: file
|
||||
againstExpectedHTML: expected];
|
||||
}
|
||||
|
||||
- (void) test_escapes
|
||||
{
|
||||
NSString *file =@"escapes.rtf";
|
||||
NSString *expected=@"<html><meta charset='utf-8'><body><font face=\"Calibri\"><font color=\"#000000\">x341x351x372x355x363x361x</font><font color=\"#000000\">S SS-S\\S</font><font color=\"#000000\">U老UřU</font><font color=\"#000000\"><br></font></font></body></html>";
|
||||
[self checkHTMLConversionOfRTFFile: file
|
||||
againstExpectedHTML: expected];
|
||||
}
|
||||
|
||||
- (void) test_spanish_accents
|
||||
{
|
||||
NSString *file =@"spanish_accents.rtf";
|
||||
NSString *expected=@"<html><meta charset='utf-8'><body><font face=\"Calibri\"><font color=\"#000000\">xñxáxéxíxóxú</font><font color=\"#000000\"><br></font></font></body></html>";
|
||||
|
||||
[self checkHTMLConversionOfRTFFile: file
|
||||
againstExpectedHTML: expected];
|
||||
}
|
||||
|
||||
- (void) test_cyr_event_ru_editor
|
||||
{
|
||||
NSString *file =@"cyr_event_ru_editor.rtf";
|
||||
NSString *expected=@"<html><meta charset='utf-8'><body><font face=\"Calibri\"><font face=\"Calibri Cyr\"><font color=\"#000000\">йчсмй</font></font><font color=\"#000000\"><br></font></font></body></html>";
|
||||
|
||||
[self checkHTMLConversionOfRTFFile: file
|
||||
againstExpectedHTML: expected];
|
||||
}
|
||||
|
||||
- (void) test_bad_hex_and_cr
|
||||
{
|
||||
NSString *file =@"bad_hex_and_cr.rtf";
|
||||
NSString *expected=@"<html><meta charset='utf-8'><body><font face=\"Calibri\"><font face=\"Calibri Cyr\"><font color=\"#000000\">Good hex:H Bad1Hex: Bad2Hex: Ignored Carriadge Return</font></font></font></body></html>";
|
||||
|
||||
[self checkHTMLConversionOfRTFFile: file
|
||||
againstExpectedHTML: expected];
|
||||
}
|
||||
|
||||
@end
|
|
@ -25,6 +25,7 @@ MailPartViewers_OBJC_FILES += \
|
|||
UIxMailPartAlternativeViewer.m \
|
||||
UIxMailPartMessageViewer.m \
|
||||
UIxMailPartICalViewer.m \
|
||||
UIxMailPartTNEFViewer.m \
|
||||
\
|
||||
UIxMailPartICalActions.m
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ ADDITIONAL_CPPFLAGS += -DHAVE_OPENSSL=1
|
|||
BUNDLE_LIBS += -lcrypto
|
||||
endif
|
||||
|
||||
SOGo_LIBRARIES_DEPEND_UPON += -lytnef
|
||||
|
||||
ADDITIONAL_CPPFLAGS += \
|
||||
-Wall -DCOMPILE_FOR_GSTEP_MAKE=1 \
|
||||
-DUIX_MAILER_MAJOR_VERSION="@\"$(MAJOR_VERSION)\"" \
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/* UIxMailPartTNEFViewer.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2021 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 UIXMAILPARTTNEFVIEWER_H
|
||||
#define UIXMAILPARTTNEFVIEWER_H
|
||||
|
||||
#import "UIxMailPartMixedViewer.h"
|
||||
|
||||
@interface UIxMailPartTNEFViewer : UIxMailPartMixedViewer
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
#endif /* UIXMAILPARTTNEFVIEWER_H */
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
Copyright (C) 2021 Inverse inc.
|
||||
|
||||
This file is part of SOGo.
|
||||
|
||||
SOGo 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.
|
||||
|
||||
SOGo 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 SOGo; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
#import <NGMime/NGMimeBodyPart.h>
|
||||
#import <NGMime/NGMimeHeaderFields.h>
|
||||
#import <NGMime/NGMimeMultipartBody.h>
|
||||
#import <NGMime/NGMimeType.h>
|
||||
|
||||
#import <SoObjects/Mailer/NSString+Mail.h>
|
||||
#import <SoObjects/Mailer/SOGoTNEFMailBodyPart.h>
|
||||
|
||||
#import "UIxMailRenderingContext.h"
|
||||
#import "UIxMailPartTNEFViewer.h"
|
||||
|
||||
@implementation UIxMailPartTNEFViewer
|
||||
|
||||
|
||||
- (void) _attachmentIdsFromBodyPart: (id) thePart
|
||||
partPath: (NSString *) thePartPath
|
||||
{
|
||||
if ([thePart isKindOfClass: [NGMimeBodyPart class]])
|
||||
{
|
||||
NSString *cid, *filename, *mimeType;
|
||||
|
||||
mimeType = [[thePart contentType] stringValue];
|
||||
cid = [thePart contentId];
|
||||
filename = [(NGMimeContentDispositionHeaderField *)[thePart headerForKey: @"content-disposition"] filename];
|
||||
|
||||
if (!filename)
|
||||
filename = [mimeType asPreferredFilenameUsingPath: nil];
|
||||
|
||||
if (filename)
|
||||
{
|
||||
[(id)attachmentIds setObject: [NSString stringWithFormat: @"%@%@%@",
|
||||
[[self clientObject] baseURLInContext: [self context]],
|
||||
thePartPath,
|
||||
filename]
|
||||
forKey: [NSString stringWithFormat: @"<%@>", cid]];
|
||||
}
|
||||
}
|
||||
else if ([thePart isKindOfClass: [NGMimeMultipartBody class]])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < [[thePart parts] count]; i++)
|
||||
{
|
||||
[self _attachmentIdsFromBodyPart: [[thePart parts] objectAtIndex: i]
|
||||
partPath: [NSString stringWithFormat: @"%@%d/", thePartPath, i+1]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (id) contentViewerComponent
|
||||
{
|
||||
id info;
|
||||
|
||||
info = [self childInfo];
|
||||
return [[[self context] mailRenderingContext] viewerForBodyInfo: info];
|
||||
}
|
||||
|
||||
- (id) renderedPart
|
||||
{
|
||||
NSArray *parts;
|
||||
NSInteger i, max;
|
||||
NSMutableArray *renderedParts;
|
||||
SOGoTNEFMailBodyPart *tnefPart;
|
||||
id viewer, info;
|
||||
|
||||
tnefPart = (SOGoTNEFMailBodyPart *)[self clientPart];
|
||||
parts = [[tnefPart bodyParts] parts];
|
||||
max = [parts count];
|
||||
renderedParts = [NSMutableArray arrayWithCapacity: max];
|
||||
|
||||
// Populate the list of attachments ids
|
||||
for (i = 0; i < max; i++)
|
||||
{
|
||||
NGMimeBodyPart *part;
|
||||
|
||||
part = [parts objectAtIndex: i];
|
||||
[self _attachmentIdsFromBodyPart: part
|
||||
partPath: [NSString stringWithFormat: @"%@/%d/", [tnefPart bodyPartIdentifier], i+1]];
|
||||
}
|
||||
|
||||
// Render each part
|
||||
for (i = 0; i < max; i++)
|
||||
{
|
||||
NGMimeBodyPart *part = [parts objectAtIndex: i];
|
||||
|
||||
[self setChildIndex: i];
|
||||
[self setChildInfo: [part bodyInfo]];
|
||||
info = [self childInfo];
|
||||
|
||||
viewer = [[[self context] mailRenderingContext] viewerForBodyInfo: info];
|
||||
[viewer setBodyInfo: info];
|
||||
[viewer setPartPath: [self childPartPath]];
|
||||
[viewer setAttachmentIds: attachmentIds];
|
||||
[viewer setFlatContent: [part body]];
|
||||
|
||||
[renderedParts addObject: [viewer renderedPart]];
|
||||
}
|
||||
|
||||
return [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[self className], @"type",
|
||||
renderedParts, @"content",
|
||||
nil];
|
||||
}
|
||||
|
||||
@end /* UIxMailPartTNEFViewer */
|
|
@ -69,6 +69,7 @@
|
|||
- (SOGoMailBodyPart *) clientPart;
|
||||
- (id) renderedPart;
|
||||
|
||||
- (NSDictionary *) attachmentIds;
|
||||
- (void) setAttachmentIds: (NSDictionary *) newAttachmentIds;
|
||||
|
||||
- (NSData *)flatContent;
|
||||
|
|
|
@ -189,9 +189,19 @@
|
|||
[self className], @"type",
|
||||
type, @"contentType",
|
||||
[[self generateResponse] contentAsString], @"content",
|
||||
[self filenameForDisplay], @"filename",
|
||||
[self preferredPathExtension], @"extension",
|
||||
[[self sizeFormatter] stringForObjectValue: [bodyInfo objectForKey: @"size"]], @"size",
|
||||
[self pathToAttachment], @"viewURL",
|
||||
[self pathForDownload], @"downloadURL",
|
||||
nil];
|
||||
}
|
||||
|
||||
- (NSDictionary *) attachmentIds
|
||||
{
|
||||
return attachmentIds;
|
||||
}
|
||||
|
||||
//
|
||||
// Attachment IDs are used to replace CID from HTML content
|
||||
// with their MIME parts when viewing an HTML mail with
|
||||
|
|
|
@ -174,6 +174,11 @@ static BOOL showNamedTextAttachmentsInline = NO;
|
|||
return [viewer pageWithName: @"UIxMailPartICalViewer"];
|
||||
}
|
||||
|
||||
- (WOComponent *) tnefViewer
|
||||
{
|
||||
return [viewer pageWithName: @"UIxMailPartTNEFViewer"];
|
||||
}
|
||||
|
||||
/* main viewer selection */
|
||||
|
||||
- (WOComponent *) viewerForBodyInfo: (id) _info
|
||||
|
@ -271,6 +276,11 @@ static BOOL showNamedTextAttachmentsInline = NO;
|
|||
}
|
||||
}
|
||||
|
||||
if ([st isEqualToString: @"ms-tnef"])
|
||||
{
|
||||
return [self tnefViewer];
|
||||
}
|
||||
|
||||
#if 0 /* the link viewer looks better than plain text ;-) */
|
||||
if ([st isEqualToString: @"pgp-signature"]) // TODO: real PGP viewer
|
||||
return [self textViewer];
|
||||
|
|
|
@ -180,11 +180,6 @@ static NSString *mailETag = nil;
|
|||
return attachmentAttrs;
|
||||
}
|
||||
|
||||
- (BOOL) hasAttachments
|
||||
{
|
||||
return [[self attachmentAttrs] count] > 0 ? YES : NO;
|
||||
}
|
||||
|
||||
- (NSFormatter *) sizeFormatter
|
||||
{
|
||||
return [UIxMailSizeFormatter sharedMailSizeFormatter];
|
||||
|
@ -199,14 +194,6 @@ static NSString *mailETag = nil;
|
|||
return [formatter stringForObjectValue: [[self clientObject] date]];
|
||||
}
|
||||
|
||||
- (NSString *) attachmentsText
|
||||
{
|
||||
if ([[self attachmentAttrs] count] > 1)
|
||||
return [self labelForKey: @"files"];
|
||||
else
|
||||
return [self labelForKey: @"file"];
|
||||
}
|
||||
|
||||
/* viewers */
|
||||
|
||||
//
|
||||
|
@ -287,6 +274,7 @@ static NSString *mailETag = nil;
|
|||
SOGoMailObject *co;
|
||||
UIxEnvelopeAddressFormatter *addressFormatter;
|
||||
UIxMailRenderingContext *mctx;
|
||||
id viewer, renderedPart;
|
||||
|
||||
co = [self clientObject];
|
||||
addressFormatter = [context mailEnvelopeAddressFormatter];
|
||||
|
@ -338,11 +326,13 @@ static NSString *mailETag = nil;
|
|||
andJSONRepresentation: data];
|
||||
}
|
||||
|
||||
viewer = [self contentViewerComponent]; // set attachmentIds for common parts
|
||||
renderedPart = [viewer renderedPart]; // set attachmentIds for encrypted & TNEF parts
|
||||
|
||||
data = [NSMutableDictionary dictionaryWithObjectsAndKeys:
|
||||
[self attachmentAttrs], @"attachmentAttrs",
|
||||
[self shouldAskReceipt], @"shouldAskReceipt",
|
||||
[NSNumber numberWithBool: [self mailIsDraft]], @"isDraft",
|
||||
[[self contentViewerComponent] renderedPart], @"parts",
|
||||
renderedPart, @"parts",
|
||||
nil];
|
||||
if ([self formattedDate])
|
||||
[data setObject: [self formattedDate] forKey: @"date"];
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" standalone="yes"?>
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:var="http://www.skyrix.com/od/binding"
|
||||
xmlns:const="http://www.skyrix.com/od/constant"
|
||||
xmlns:rsrc="OGo:url"
|
||||
xmlns:label="OGo:label"
|
||||
>
|
||||
<!-- decoded parts from winmail.dat -->
|
||||
</div>
|
|
@ -361,9 +361,23 @@
|
|||
};
|
||||
}
|
||||
}
|
||||
_.forEach(part.content, function(mixedPart) {
|
||||
_visit(mixedPart);
|
||||
var winmail = _.find(part.content, function(mixedPart) {
|
||||
// Ignore empty content -- that could mean a decoding error server-side.
|
||||
return mixedPart.type == 'UIxMailPartTnefViewer' && mixedPart.content.length > 0;
|
||||
});
|
||||
|
||||
if (winmail && !_.find(part.content, function(mixedPart) {
|
||||
return mixedPart.type == 'UIxMailPartAlternativeViewer';
|
||||
})) {
|
||||
// If there's no alternate part in the message, show the winmail.dat attachment only.
|
||||
// Otherwise, show all parts.
|
||||
_visit(winmail);
|
||||
}
|
||||
else {
|
||||
_.forEach(part.content, function(mixedPart) {
|
||||
_visit(mixedPart);
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (angular.isUndefined(part.safeContent)) {
|
||||
|
|
|
@ -80,12 +80,21 @@
|
|||
$mdPanel = ImageGallery.$mdPanel,
|
||||
partSrc = angular.element(this.message.$content()[partIndex].content).find('img')[0].src;
|
||||
|
||||
var images = _.filter(this.message.attachmentAttrs, function(attrs) {
|
||||
return attrs.mimetype.indexOf('image/') === 0 && attrs.mimetype.indexOf('svg+xml') < 0;
|
||||
});
|
||||
var _findImages = function (parts, images) {
|
||||
_.forEach(parts, function (part) {
|
||||
if (part.type == 'UIxMailPartImageViewer') {
|
||||
images.push(part);
|
||||
}
|
||||
else if (typeof part.content != 'string') {
|
||||
_findImages(part.content, images);
|
||||
}
|
||||
});
|
||||
};
|
||||
var images = [];
|
||||
_findImages(this.message.$content(), images);
|
||||
|
||||
var selectedIndex = _.findIndex(images, function(image) {
|
||||
return image.url.indexOf(partSrc) >= 0;
|
||||
return partSrc.indexOf(image.viewURL) >= 0;
|
||||
});
|
||||
|
||||
// Add a class to the body in order to modify the panel backdrop opacity
|
||||
|
@ -128,7 +137,7 @@
|
|||
' <div md-truncate class="md-flex" ng-bind="$panelCtrl.selectedImage.filename"></div>',
|
||||
' <md-button class="md-icon-button"',
|
||||
' aria-label="' + l('Save Attachment') + '"',
|
||||
' ng-href="{{$panelCtrl.selectedImage.urlAsAttachment}}">',
|
||||
' ng-href="{{$panelCtrl.selectedImage.downloadURL}}">',
|
||||
' <md-icon>file_download</md-icon>',
|
||||
' </md-button>',
|
||||
' </div>',
|
||||
|
@ -137,7 +146,7 @@
|
|||
' ng-disabled="$panelCtrl.selectedIndex == 0">',
|
||||
' <md-icon>navigate_before</md-icon>',
|
||||
' </md-button>',
|
||||
' <img class="sg-image" ng-src="{{$panelCtrl.selectedImage.url}}">',
|
||||
' <img class="sg-image" ng-src="{{$panelCtrl.selectedImage.viewURL}}">',
|
||||
' <md-button class="md-icon-button" ng-click="$panelCtrl.nextImage()"',
|
||||
' ng-disabled="$panelCtrl.selectedIndex == $panelCtrl.lastIndex">',
|
||||
' <md-icon>navigate_next</md-icon>',
|
||||
|
@ -145,7 +154,7 @@
|
|||
' </div>',
|
||||
' <div class="sg-image-thumbnails">',
|
||||
' <div class="sg-image-thumbnail" ng-repeat="image in ::$panelCtrl.images">',
|
||||
' <img class="sg-hide" ng-src="{{::image.url}}" ng-click="$panelCtrl.selectImage($index)">',
|
||||
' <img class="sg-hide" ng-src="{{::image.viewURL}}" ng-click="$panelCtrl.selectImage($index)">',
|
||||
' </div>',
|
||||
' </div>',
|
||||
'</sg-image-gallery>'
|
||||
|
|
|
@ -397,7 +397,7 @@ EOF
|
|||
}
|
||||
|
||||
checkDependencies() {
|
||||
cfgwrite "BASE_LIBS := `gnustep-config --base-libs` -lzip"
|
||||
cfgwrite "BASE_LIBS := `gnustep-config --base-libs` -lzip -lytnef"
|
||||
if test "x$ARG_ENABLE_SAML2" = "x1"; then
|
||||
checkLinking "lasso" required;
|
||||
if test $? = 0; then
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
Source: sogo
|
||||
Priority: optional
|
||||
Maintainer: Inverse Support <support@inverse.ca>
|
||||
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, libsodium-dev (>= 1.0.9) | base-files (<= 9.4ubuntu4)
|
||||
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), libytnef0-dev (>= 1.9.3), libzip-dev, libsodium-dev (>= 1.0.9) | base-files (<= 9.4ubuntu4)
|
||||
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, libzip1 | libzip2 | libzip4 | libzip5, liblasso3 (>= 2.3.5)
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, sope4.9-libxmlsaxdriver, sope4.9-db-connector, gnustep-make, libcurl3 | libcurl4, libytnef0 (>= 1.9.3), libzip1 | libzip2 | libzip4 | libzip5, 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
|
||||
|
|
|
@ -14,9 +14,9 @@ Group: Productivity/Groupware
|
|||
Source: SOGo-%{sogo_version}.tar.gz
|
||||
Prefix: /usr
|
||||
AutoReqProv: off
|
||||
Requires: gnustep-base >= 1.23, sope%{sope_major_version}%{sope_minor_version}-core, httpd, sope%{sope_major_version}%{sope_minor_version}-core, sope%{sope_major_version}%{sope_minor_version}-appserver, sope%{sope_major_version}%{sope_minor_version}-ldap, sope%{sope_major_version}%{sope_minor_version}-cards >= %{sogo_version}, sope%{sope_major_version}%{sope_minor_version}-gdl1-contentstore >= %{sogo_version}, sope%{sope_major_version}%{sope_minor_version}-sbjson, lasso, libmemcached, memcached, libcurl, libzip, liboath, libsodium
|
||||
Requires: gnustep-base >= 1.23, sope%{sope_major_version}%{sope_minor_version}-core, httpd, sope%{sope_major_version}%{sope_minor_version}-core, sope%{sope_major_version}%{sope_minor_version}-appserver, sope%{sope_major_version}%{sope_minor_version}-ldap, sope%{sope_major_version}%{sope_minor_version}-cards >= %{sogo_version}, sope%{sope_major_version}%{sope_minor_version}-gdl1-contentstore >= %{sogo_version}, sope%{sope_major_version}%{sope_minor_version}-sbjson, lasso, libmemcached, memcached, libcurl, libzip, liboath, libsodium, ytnef
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}
|
||||
BuildRequires: gcc-objc gnustep-base gnustep-make sope%{sope_major_version}%{sope_minor_version}-appserver-devel sope%{sope_major_version}%{sope_minor_version}-core-devel sope%{sope_major_version}%{sope_minor_version}-ldap-devel sope%{sope_major_version}%{sope_minor_version}-mime-devel sope%{sope_major_version}%{sope_minor_version}-xml-devel sope%{sope_major_version}%{sope_minor_version}-gdl1-devel sope%{sope_major_version}%{sope_minor_version}-sbjson-devel lasso-devel libmemcached-devel sed libcurl-devel libzip-devel liboath-devel libsodium-devel
|
||||
BuildRequires: gcc-objc gnustep-base gnustep-make sope%{sope_major_version}%{sope_minor_version}-appserver-devel sope%{sope_major_version}%{sope_minor_version}-core-devel sope%{sope_major_version}%{sope_minor_version}-ldap-devel sope%{sope_major_version}%{sope_minor_version}-mime-devel sope%{sope_major_version}%{sope_minor_version}-xml-devel sope%{sope_major_version}%{sope_minor_version}-gdl1-devel sope%{sope_major_version}%{sope_minor_version}-sbjson-devel lasso-devel libmemcached-devel sed libcurl-devel libzip-devel liboath-devel libsodium-devel ytnef
|
||||
|
||||
%define saml2_cfg_opts "--enable-saml2"
|
||||
%define mfa_cfg_opts "--enable-mfa"
|
||||
|
|
Loading…
Reference in New Issue