? colloquy-persistent-logging-2.diff ? 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 13:03:28 -0000 @@ -1,5 +1,6 @@ #import "JVChatTranscript.h" #import +#import @class NSView; @class MVTextView; @@ -34,6 +35,7 @@ NSMutableDictionary *_settings; NSMenu *_spillEncodingMenu; JVBuddy *_buddy; + NSFileHandle *_logFile; unsigned int _messageId; BOOL _firstMessage; @@ -45,6 +47,7 @@ int _historyIndex; float _sendHeight; + long _previousLogOffset; } - (id) initWithTarget:(NSString *) target forConnection:(MVChatConnection *) connection; @@ -66,6 +69,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 continuation:(BOOL)cont; - (void) processMessage:(NSMutableData *) message asAction:(BOOL) action fromUser:(NSString *) user; - (void) echoSentMessageToDisplay:(NSAttributedString *) message asAction:(BOOL) action; @@ -87,4 +91,5 @@ - (void) processMessage:(NSMutableAttributedString *) message asAction:(BOOL) action toChat:(JVDirectChat *) chat; - (void) userNamed:(NSString *) nickname isNowKnowAs:(NSString *) newNickname inView:(id ) 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 13:03:34 -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 element to the logfile + [self writeToLog: xmlDocGetRootElement(_xmlLog) withDoc: _xmlLog initializing:YES continuation:NO]; + [[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 continuation:NO]; + if( [_logLock tryLock] ) { xmlAddChild( xmlDocGetRootElement( _xmlLog ), xmlDocCopyNode( root, _xmlLog, 1 ) ); @@ -673,6 +694,7 @@ } - (void) addMessageToDisplay:(NSData *) message fromUser:(NSString *) user asAction:(BOOL) action { + BOOL continuation = NO; BOOL highlight = NO; xmlDocPtr doc = NULL, msgDoc = NULL; xmlNodePtr root = NULL, child = NULL, parent = NULL; @@ -758,6 +780,7 @@ } else _requiresFullMessage = YES; if( ! _requiresFullMessage && result && result -> nodesetval -> nodeNr ) { + continuation = YES; if( ! [[_styleParams objectForKey:@"subsequent"] isEqualToString:@"'yes'"] ) { [_styleParams setObject:@"'yes'" forKey:@"subsequent"]; if( _params ) [[self class] _freeXsltParamArray:_params]; @@ -768,6 +791,7 @@ root = xmlDocCopyNode( parent, doc, 1 ); xmlDocSetRootElement( doc, root ); } else { + continuation = NO; if( [[_styleParams objectForKey:@"subsequent"] isEqualToString:@"'yes'"] ) { [_styleParams removeObjectForKey:@"subsequent"]; if( _params ) [[self class] _freeXsltParamArray:_params]; @@ -816,6 +840,7 @@ } msgStr = [[NSString stringWithFormat:@"%@", messageString] UTF8String]; + msgDoc = xmlParseMemory( msgStr, strlen( msgStr ) ); child = xmlDocCopyNode( xmlDocGetRootElement( msgDoc ), doc, 1 ); @@ -824,6 +849,8 @@ if( highlight ) xmlSetProp( child, "highlight", "yes" ); xmlAddChild( root, child ); + [self writeToLog:root withDoc:doc initializing:NO continuation:continuation]; + xmlFreeDoc( msgDoc ); if( [_logLock tryLock] ) { @@ -857,6 +884,35 @@ [_windowController reloadListItem:self andChildren:NO]; } +- (void) writeToLog:(void *)root withDoc:(void *)doc initializing:(BOOL)init continuation:(BOOL)cont { + // 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 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 (cont) [_logFile seekToFileOffset:_previousLogOffset]; + else if (!init) { + [_logFile seekToFileOffset:[_logFile offsetInFile]-6]; + _previousLogOffset = [_logFile offsetInFile]; + } + _previousLogOffset = [_logFile offsetInFile]; + [_logFile writeData:[NSData dataWithBytesNoCopy:buf->content length:buf->use freeWhenDone: NO]]; + if (!init) [_logFile writeData:[@"" dataUsingEncoding:NSASCIIStringEncoding]]; + xmlBufferFree( buf ); + + // If we are initializing, we wrote a singleton tag and we need to back up over the /> + // and write > instead. + if (init) { + [_logFile seekToEndOfFile]; + [_logFile seekToFileOffset:[_logFile offsetInFile]-2]; + _previousLogOffset = [_logFile offsetInFile]; + [_logFile writeData:[@">" 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 +947,7 @@ return _newHighlightMessage; } + #pragma mark - #pragma mark Input Handling @@ -1797,4 +1854,5 @@ if( ! [self callScriptHandler:'uNcX' withArguments:args] ) [self doesNotRespondToSelector:_cmd]; } -@end \ No newline at end of file + +@end