? colloquy-persistent-logging.diff
Index: JVDirectChat.h
===================================================================
RCS file: /cvsroot/colloquy/colloquy/JVDirectChat.h,v
retrieving revision 1.19
diff -u -r1.19 JVDirectChat.h
--- JVDirectChat.h	3 Apr 2004 17:49:02 -0000	1.19
+++ JVDirectChat.h	5 Apr 2004 04:50:01 -0000
@@ -1,5 +1,6 @@
 #import "JVChatTranscript.h"
 #import <AppKit/NSNibDeclarations.h>
+#import <Foundation/NSFileHandle.h>
 
 @class NSView;
 @class MVTextView;
@@ -34,6 +35,7 @@
 	NSMutableDictionary *_settings;
 	NSMenu *_spillEncodingMenu;
 	JVBuddy *_buddy;
+	NSFileHandle *_logFile;
 
 	unsigned int _messageId;
 	BOOL _firstMessage;
@@ -66,6 +68,7 @@
 
 - (void) addEventMessageToDisplay:(NSString *) message withName:(NSString *) name andAttributes:(NSDictionary *) attributes;
 - (void) addMessageToDisplay:(NSData *) message fromUser:(NSString *) user asAction:(BOOL) action;
+- (void) writeToLog:(void *)root withDoc:(void *)doc initializing:(BOOL)init;
 - (void) processMessage:(NSMutableData *) message asAction:(BOOL) action fromUser:(NSString *) user;
 - (void) echoSentMessageToDisplay:(NSAttributedString *) message asAction:(BOOL) action;
 
@@ -87,4 +90,5 @@
 - (void) processMessage:(NSMutableAttributedString *) message asAction:(BOOL) action toChat:(JVDirectChat *) chat;
 
 - (void) userNamed:(NSString *) nickname isNowKnowAs:(NSString *) newNickname inView:(id <JVChatViewController>) view;
-@end
\ No newline at end of file
+
+@end
Index: JVDirectChat.m
===================================================================
RCS file: /cvsroot/colloquy/colloquy/JVDirectChat.m,v
retrieving revision 1.88
diff -u -r1.88 JVDirectChat.m
--- JVDirectChat.m	4 Apr 2004 04:45:32 -0000	1.88
+++ JVDirectChat.m	5 Apr 2004 04:50:07 -0000
@@ -139,6 +139,22 @@
 		source = [NSString stringWithFormat:@"%@/%@", [[[self connection] url] absoluteString], _target];
 		xmlSetProp( xmlDocGetRootElement( _xmlLog ), "source", [source UTF8String] );
 
+        // Set up log directories
+        NSString *logs = [[NSString stringWithFormat:@"~/Library/Application Support/%@/Logs", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"]] stringByExpandingTildeInPath];
+        NSFileManager *fileManager = [NSFileManager defaultManager];
+        if (![fileManager fileExistsAtPath:logs]) [fileManager createDirectoryAtPath:logs attributes:nil];
+        logs = [logs stringByAppendingPathComponent:[_connection server]];
+        if (![fileManager fileExistsAtPath:logs]) [fileManager createDirectoryAtPath:logs attributes:nil];
+        logs = [logs stringByAppendingPathComponent:_target];
+        if (![fileManager fileExistsAtPath:logs]) [fileManager createDirectoryAtPath:logs attributes:nil];
+        logs = [logs stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.colloquyTranscript", [[NSDate date] description]]];
+        // 'touch' the new logfile, otherwise fileHandleForUpdatingAtPath returns nil
+        [fileManager createFileAtPath:logs contents:[NSData data] attributes:nil];
+        _logFile = [[NSFileHandle fileHandleForUpdatingAtPath:logs] retain];
+
+        // Write the <log> element to the logfile
+		[self writeToLog: xmlDocGetRootElement(_xmlLog) withDoc: _xmlLog initializing:YES];
+
 		[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _didConnect: ) name:MVChatConnectionDidConnectNotification object:connection];
 		[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _didDisconnect: ) name:MVChatConnectionDidDisconnectNotification object:connection];
 
@@ -223,6 +239,8 @@
 	[_waitingAlertNames release];
 	[_settings release];
 	[_spillEncodingMenu release];
+	// TODO: Read in the logfile and write it back out again after adding the 'ended' attribute to the log node.
+	[_logFile release];
 
 	NSEnumerator *enumerator = [_waitingAlerts objectEnumerator];
 	id alert = nil;
@@ -244,6 +262,7 @@
 	_waitingAlertNames  = nil;
 	_settings			= nil;
 	_spillEncodingMenu  = nil;
+	_logFile = nil;
 
 	[super dealloc];
 }
@@ -647,6 +666,8 @@
 		_params = [[self class] _xsltParamArrayWithDictionary:_styleParams];
 	}
 
+    [self writeToLog:root withDoc:doc initializing:NO];
+
 	if( [_logLock tryLock] ) {
 		xmlAddChild( xmlDocGetRootElement( _xmlLog ), xmlDocCopyNode( root, _xmlLog, 1 ) );
 
@@ -816,6 +837,7 @@
 	}
 
 	msgStr = [[NSString stringWithFormat:@"<message>%@</message>", messageString] UTF8String];
+
 	msgDoc = xmlParseMemory( msgStr, strlen( msgStr ) );
 
 	child = xmlDocCopyNode( xmlDocGetRootElement( msgDoc ), doc, 1 );
@@ -824,6 +846,8 @@
 	if( highlight ) xmlSetProp( child, "highlight", "yes" );
 	xmlAddChild( root, child );
 
+    [self writeToLog:root withDoc:doc initializing:NO];
+
 	xmlFreeDoc( msgDoc );
 
 	if( [_logLock tryLock] ) {
@@ -857,6 +881,29 @@
 	[_windowController reloadListItem:self andChildren:NO];
 }
 
+- (void) writeToLog:(void *)root withDoc:(void *)doc initializing:(BOOL)init {
+    // Append a node to the logfile for this chat
+
+    xmlBufferPtr buf = xmlBufferCreate();
+    xmlNodeDump(buf, doc, root, 0, 0);
+
+    // To keep the XML valid at all times, we need to preserve a </log> close tag at the end of
+    // the file at all times. So, we seek to the end of the file minus 6 characters.
+    [_logFile seekToEndOfFile];
+    if (!init) [_logFile seekToFileOffset:[_logFile offsetInFile]-6];
+    [_logFile writeData:[NSData dataWithBytesNoCopy:buf->content length:buf->use freeWhenDone: NO]];
+    if (!init) [_logFile writeData:[@"</log>" dataUsingEncoding:NSASCIIStringEncoding]];
+    xmlBufferFree( buf );
+
+    // If we are initializing, we wrote a singleton <log> tag and we need to back up over the />
+    // and write ></log> instead.
+    if (init) {
+        [_logFile seekToEndOfFile];
+        [_logFile seekToFileOffset:[_logFile offsetInFile]-2];
+        [_logFile writeData:[@"></log>" dataUsingEncoding:NSASCIIStringEncoding]];
+    }
+}
+
 - (void) processMessage:(NSMutableData *) message asAction:(BOOL) action fromUser:(NSString *) user {
 	NSMethodSignature *signature = [NSMethodSignature methodSignatureWithReturnAndArgumentTypes:@encode( void ), @encode( NSMutableData * ), @encode( BOOL ), @encode( JVDirectChat * ), nil];
 	NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
@@ -891,6 +938,7 @@
 	return _newHighlightMessage;
 }
 
+
 #pragma mark -
 #pragma mark Input Handling
 
@@ -1797,4 +1845,5 @@
 	if( ! [self callScriptHandler:'uNcX' withArguments:args] )
 		[self doesNotRespondToSelector:_cmd];
 }
-@end
\ No newline at end of file
+
+@end
