AD73BE4B06CF88BF00226A2D,
AD071D1206CD2BCB00A9EEF4,
AD071D1306CD2BCB00A9EEF4,
+ ADA6333607140E0D0058C21C,
+ ADA6333707140E0D0058C21C,
);
isa = PBXGroup;
name = Classes;
refType = 4;
sourceTree = "<group>";
};
+ ADA6333607140E0D0058C21C = {
+ fileEncoding = 4;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.h;
+ path = "NSString+iCal.h";
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ ADA6333707140E0D0058C21C = {
+ fileEncoding = 4;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.objc;
+ path = "NSString+iCal.m";
+ refType = 4;
+ sourceTree = "<group>";
+ };
ADCDE53106ADA8AC00BFCE2B = {
fileEncoding = 5;
indentWidth = 8;
+2004-10-06 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * UIxAppointmentEditor.m: changed iCal template to use RFC2445
+ conforming line delimiters. Changed the testAction to aid in
+ debugging our iCal rendering. (v0.9.85)
+
2004-10-05 Marcus Mueller <znek@mulle-kybernetik.com>
* v0.9.84
- (NSString *)iCalStringTemplate {
static NSString *iCalStringTemplate = \
- @"BEGIN:VCALENDAR\n"
- @"METHOD:REQUEST\n"
- @"PRODID:OpenGroupware.org SOGo 0.9\n"
- @"VERSION:2.0\n"
- @"BEGIN:VEVENT\n"
- @"UID:%@\n"
- @"CLASS:PRIVATE\n"
- @"STATUS:CONFIRMED\n"
- @"DTSTAMP:%@\n"
- @"DTSTART:%@\n"
- @"DTEND:%@\n"
- @"TRANSP:OPAQUE\n"
- @"SEQUENCE:1\n"
- @"PRIORITY:5\n"
+ @"BEGIN:VCALENDAR\r\n"
+ @"METHOD:REQUEST\r\n"
+ @"PRODID:OpenGroupware.org SOGo 0.9\r\n"
+ @"VERSION:2.0\r\n"
+ @"BEGIN:VEVENT\r\n"
+ @"UID:%@\r\n"
+ @"CLASS:PRIVATE\r\n"
+ @"STATUS:CONFIRMED\r\n"
+ @"DTSTAMP:%@\r\n"
+ @"DTSTART:%@\r\n"
+ @"DTEND:%@\r\n"
+ @"TRANSP:OPAQUE\r\n"
+ @"SEQUENCE:1\r\n"
+ @"PRIORITY:5\r\n"
@"%@"
- @"END:VEVENT\n"
+ @"END:VEVENT\r\n"
@"END:VCALENDAR";
NSCalendarDate *lStartDate, *lEndDate;
- (NSString *)iCalParticipantsStringFromQueryParameters {
static NSString *iCalParticipantString = \
- @"ATTENDEE;ROLE=REQ-PARTICIPANT;CN=\"%@\":mailto:%@\n";
+ @"ATTENDEE;ROLE=REQ-PARTICIPANT;CN=\"%@\":mailto:%@\r\n";
return [self iCalStringFromQueryParameter:@"ps"
format:iCalParticipantString];
- (NSString *)iCalResourcesStringFromQueryParameters {
static NSString *iCalResourceString = \
- @"ATTENDEE;ROLE=NON-PARTICIPANT;CN=\"%@\":mailto:%@\n";
+ @"ATTENDEE;ROLE=NON-PARTICIPANT;CN=\"%@\":mailto:%@\r\n";
return [self iCalStringFromQueryParameter:@"rs"
format:iCalResourceString];
- (id)testAction {
/* for testing only */
- WORequest *req;
-
- NSLog(@"%s BEEN HERE!", __PRETTY_FUNCTION__);
+ WORequest *req;
+ SOGoAppointment *apt;
+ NSString *content;
req = [[self context] request];
- NSLog(@"%@", [req formValues]);
- [self logWithFormat:@"aptStartDate:%@ aptEndDate:%@",
- [self aptStartDate],
- [self aptEndDate]];
+ apt = [[SOGoAppointment alloc] initWithICalString:[self iCalString]];
+ [self saveValuesIntoAppointment:apt];
+ content = [apt iCalString];
+ [self logWithFormat:@"%s -- iCal:\n%@",
+ __PRETTY_FUNCTION__,
+ content];
+ [apt release];
return self;
}
# $Id$
-SUBMINOR_VERSION:=84
+SUBMINOR_VERSION:=85
# v0.9.84 requires libSOGoLogic v0.9.12
# v0.9.70 requires libNGExtensions v4.3.107
+2004-10-06 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * v0.9.13
+
+ * SOGoAppointmentICalRenderer.m: fixed the renderer to generate RFC2445
+ conforming output.
+
+ * NSString+iCal.[hm]: necessary utility methods for the renderer to
+ generate RFC2445 conforming content.
+ NOTE: this should eventually get moved to NGiCal.
+
2004-10-05 Marcus Mueller <znek@mulle-kybernetik.com>
* v0.9.12
libSOGoLogic_HEADER_FILES += \
SOGoAppointment.h \
SOGoAppointmentICalRenderer.h \
- AgenorUserManager.h
+ AgenorUserManager.h \
+ NSString+iCal.h
libSOGoLogic_OBJC_FILES += \
SOGoAppointment.m \
SOGoAppointmentICalRenderer.m \
- AgenorUserManager.m
+ AgenorUserManager.m \
+ NSString+iCal.m
-include GNUmakefile.preamble
include $(GNUSTEP_MAKEFILES)/library.make
--- /dev/null
+/*
+ Copyright (C) 2000-2004 SKYRIX Software AG
+
+ This file is part of OGo
+
+ OGo 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.
+*/
+// $Id$
+
+
+#ifndef __NSString_iCal_H_
+#define __NSString_iCal_H_
+
+
+#import <Foundation/Foundation.h>
+
+
+@interface NSString (SOGoiCal)
+
+- (NSString *)iCalDQUOTESafeString;
+- (NSString *)iCalSafeString;
+- (NSString *)iCalEscapedStringWithEscapeSet:(NSCharacterSet *)_es;
+
+@end
+
+#endif /* __NSString_iCal_H_ */
--- /dev/null
+/*
+ Copyright (C) 2000-2004 SKYRIX Software AG
+
+ This file is part of OGo
+
+ OGo 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.
+*/
+// $Id$
+
+
+#import "NSString+iCal.h"
+
+
+@implementation NSString (SOGoiCal)
+
+#if 0
+- (NSString *)iCalFoldedString {
+ /* RFC2445, 4.1 Content Lines
+
+ The iCalendar object is organized into individual lines of text,
+ called content lines. Content lines are delimited by a line break,
+ which is a CRLF sequence (US-ASCII decimal 13, followed by US-ASCII
+ decimal 10).
+ Lines of text SHOULD NOT be longer than 75 octets, excluding the line
+ break. Long content lines SHOULD be split into a multiple line
+ representations using a line "folding" technique. That is, a long
+ line can be split between any two characters by inserting a CRLF
+ immediately followed by a single linear white space character (i.e.,
+ SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9).
+ Any sequence of CRLF followed immediately by a single linear white space
+ character is ignored (i.e., removed) when processing the content type.
+ */
+}
+#endif
+
+- (NSString *)iCalDQUOTESafeString {
+ static NSCharacterSet *escapeSet = nil;
+
+ if(escapeSet == nil) {
+ escapeSet = [NSCharacterSet characterSetWithCharactersInString:@"\""];
+ [escapeSet retain];
+ }
+ return [self iCalEscapedStringWithEscapeSet:escapeSet];
+}
+
+- (NSString *)iCalSafeString {
+ static NSCharacterSet *escapeSet = nil;
+
+ if(escapeSet == nil) {
+ escapeSet = [NSCharacterSet characterSetWithCharactersInString:@"\n,;\""];
+ [escapeSet retain];
+ }
+ return [self iCalEscapedStringWithEscapeSet:escapeSet];
+}
+
+- (NSString *)iCalEscapedStringWithEscapeSet:(NSCharacterSet *)_es {
+ /* Escape unsafe characters */
+ NSMutableString *safeString;
+ NSRange r, er;
+ BOOL needsEscaping;
+ unsigned length;
+
+ length = [self length];
+ r = NSMakeRange(0, length);
+ er = [self rangeOfCharacterFromSet:_es options:0 range:r];
+ needsEscaping = er.length > 0 ? YES : NO;
+ if(!needsEscaping) {
+ return self; /* cheap */
+ }
+ /* wild guess */
+ safeString = [NSMutableString stringWithCapacity:length + 10];
+ if(needsEscaping) {
+ NSRange todoRange;
+ /*
+ r == previous range, upto er.location
+ er == escape range
+ todoRange == what we still need to scan
+ */
+ length = r.length;
+ do {
+ NSString *s;
+
+ r.length = (er.location - 1) - r.location;
+ s = [self substringWithRange:r];
+ [safeString appendString:s];
+ [safeString appendString:@"\\"];
+ if([self characterAtIndex:er.location] == '\n') {
+ s = @"n";
+ }
+ else {
+ s = [self substringWithRange:er];
+ }
+ [safeString appendString:s];
+ r.location = NSMaxRange(er);
+ todoRange.location = r.location;
+ todoRange.length = length - r.location;
+ er = [self rangeOfCharacterFromSet:_es
+ options:0
+ range:todoRange];
+ }
+ while(er.length > 0);
+ if(todoRange.length > 0) {
+ [safeString appendString:[self substringWithRange:todoRange]];
+ }
+ }
+ return safeString;
+}
+
+@end
// $Id$
#include "SOGoAppointment.h"
-#include "SOGoAppointmentICalRenderer.h"
#include <SaxObjC/SaxObjC.h>
#include <NGiCal/NGiCal.h>
#include <EOControl/EOControl.h>
+#include "SOGoAppointmentICalRenderer.h"
#include "common.h"
@interface SOGoAppointment (PrivateAPI)
#include "SOGoAppointmentICalRenderer.h"
#include "SOGoAppointment.h"
+#include "NSString+iCal.h"
#include <NGiCal/NGiCal.h>
#include "common.h"
calendar = [_apt calendar];
- [s appendString:@"BEGIN:VCALENDAR\nMETHOD:REQUEST\n"];
+ [s appendString:@"BEGIN:VCALENDAR\r\nMETHOD:REQUEST\r\n"];
[s appendString:@"PRODID:"];
[s appendString:[calendar isNotNull] ? [calendar prodId] : @"SOGo/0.9"];
- [s appendString:@"\n"];
+ [s appendString:@"\r\n"];
[s appendString:@"VERSION:"];
[s appendString:[calendar isNotNull] ? [calendar version] : @"2.0"];
- [s appendString:@"\n"];
+ [s appendString:@"\r\n"];
}
- (void)addPostambleForAppointment:(SOGoAppointment *)_apt
toString:(NSMutableString *)s
{
- [s appendString:@"END:VCALENDAR\n"];
+ [s appendString:@"END:VCALENDAR\r\n"];
}
- (void)addOrganizer:(iCalPerson *)p toString:(NSMutableString *)s {
[s appendString:@"ORGANIZER;CN=\""];
if ((x = [p cn]))
- [s appendString:x];
+ [s appendString:[x iCalDQUOTESafeString]];
[s appendString:@"\""];
if ((x = [p email])) {
[s appendString:@":"]; /* sic! */
- [s appendString:x];
+ [s appendString:[x iCalSafeString]];
}
- [s appendString:@"\n"];
+ [s appendString:@"\r\n"];
}
- (void)addAttendees:(NSArray *)persons toString:(NSMutableString *)s {
if ((x = [p role])) {
[s appendString:@"ROLE="];
- [s appendString:x];
+ [s appendString:[x iCalSafeString]];
[s appendString:@";"];
}
[s appendString:@"CN=\""];
if ((x = [p cn])) {
- [s appendString:x];
+ [s appendString:[x iCalDQUOTESafeString]];
}
[s appendString:@"\""];
if ([(x = [p email]) isNotNull]) {
[s appendString:@":"]; /* sic! */
- [s appendString:x];
+ [s appendString:[x iCalSafeString]];
}
- [s appendString:@"\n"];
+ [s appendString:@"\r\n"];
}
}
event = [_apt event];
- [s appendString:@"BEGIN:VEVENT\n"];
+ [s appendString:@"BEGIN:VEVENT\r\n"];
[s appendString:@"SUMMARY:"];
- [s appendString:[_apt summary]];
- [s appendString:@"\n"];
+ [s appendString:[[_apt summary] iCalSafeString]];
+ [s appendString:@"\r\n"];
if ([_apt hasLocation]) {
[s appendString:@"LOCATION:"];
- [s appendString:[_apt location]];
- [s appendString:@"\n"];
+ [s appendString:[[_apt location] iCalSafeString]];
+ [s appendString:@"\r\n"];
}
[s appendString:@"UID:"];
[s appendString:[_apt uid]];
- [s appendString:@"\n"];
+ [s appendString:@"\r\n"];
[s appendString:@"DTSTART:"];
[s appendString:[[_apt startDate] icalString]];
- [s appendString:@"\n"];
+ [s appendString:@"\r\n"];
if ([_apt hasEndDate]) {
[s appendString:@"DTEND:"];
[s appendString:[[_apt endDate] icalString]];
- [s appendString:@"\n"];
+ [s appendString:@"\r\n"];
}
if ([_apt hasDuration]) {
[s appendString:@"DURATION:"];
[s appendString:[event duration]];
- [s appendString:@"\n"];
+ [s appendString:@"\r\n"];
}
if([_apt hasPriority]) {
[s appendString:@"PRIORITY:"];
[s appendString:[_apt priority]];
- [s appendString:@"\n"];
+ [s appendString:@"\r\n"];
}
if([_apt hasCategories]) {
NSString *catString;
catString = [[_apt categories] componentsJoinedByString:@","];
[s appendString:@"CATEGORIES:"];
[s appendString:catString];
- [s appendString:@"\n"];
+ [s appendString:@"\r\n"];
}
if([_apt hasComment]) {
- [s appendString:@"COMMENT:"];
- [s appendString:[_apt comment]];
- [s appendString:@"\n"];
+ [s appendString:@"DESCRIPTION:"]; /* this is what iCal.app does */
+ [s appendString:[[_apt comment] iCalSafeString]];
+ [s appendString:@"\r\n"];
}
[s appendString:@"STATUS:"];
[s appendString:[_apt status]];
- [s appendString:@"\n"];
+ [s appendString:@"\r\n"];
/* what's all this? */
- [s appendString:@"TRANSP:OPAQUE\n"]; /* transparency */
- [s appendString:@"CLASS:PRIVATE\n"]; /* classification [like 'top secret'] */
+ [s appendString:@"TRANSP:OPAQUE\r\n"]; /* transparency */
+ [s appendString:@"CLASS:PRIVATE\r\n"]; /* classification [like 'top secret'] */
[self addOrganizer:[_apt organizer] toString:s];
[self addAttendees:[_apt attendees] toString:s];
/* postamble */
- [s appendString:@"END:VEVENT\n"];
+ [s appendString:@"END:VEVENT\r\n"];
}
- (NSString *)stringForAppointment:(SOGoAppointment *)_apt {
# $Id$
-SUBMINOR_VERSION:=12
+SUBMINOR_VERSION:=13
+
+# v0.9.13 requires libFoundation v1.0.62