When we receive a distributed notification, any objects added to the autorelease...
[adiumx.git] / Source / ESDebugController.m
blobb003755d1e0feffe51432b1bb0c25e66d0beca0d
1 /* 
2  * Adium is the legal property of its developers, whose names are listed in the copyright file included
3  * with this source distribution.
4  * 
5  * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6  * General Public License as published by the Free Software Foundation; either version 2 of the License,
7  * or (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
10  * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
11  * Public License for more details.
12  * 
13  * You should have received a copy of the GNU General Public License along with this program; if not,
14  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
15  */
17 #import "ESDebugController.h"
18 #import "ESDebugWindowController.h"
20 #import <Adium/AIMenuControllerProtocol.h>
21 #import <Adium/AIPreferenceControllerProtocol.h>
22 #import <AIUtilities/AIMenuAdditions.h>
24 #include <fcntl.h>  //open(2)
25 #include <unistd.h> //close(2)
26 #include <errno.h>  //errno
27 #include <string.h> //strerror(3)
29 #define CACHED_DEBUG_LOGS               100             //Number of logs to keep at any given time
30 #define KEY_DEBUG_WINDOW_OPEN   @"Debug Window Open"
32 @implementation ESDebugController
34 #ifdef DEBUG_BUILD
36 static ESDebugController        *sharedDebugController = nil;
38 - (id)init
40         if (sharedDebugController)
41                 self = sharedDebugController;
42         else {  
43                 if ((self = [super init])) {
44                         sharedDebugController = self;
46                         debugLogArray = [[NSMutableArray alloc] init];          
47                 }
48         }
49         return self;
52 - (void)controllerDidLoad
54         //Contact list menu tem
55         NSMenuItem *menuItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:AILocalizedString(@"Debug Window",nil)
56                                                                                                                                                                 target:self
57                                                                                                                                                                 action:@selector(showDebugWindow:)
58                                                                                                                                                  keyEquivalent:@""];
59         [[adium menuController] addMenuItem:menuItem toLocation:LOC_Adium_About];
60         [menuItem release];
62         //Restore the debug window if it was open when we quit last time
63         if ([[[adium preferenceController] preferenceForKey:KEY_DEBUG_WINDOW_OPEN
64                                                                                                   group:GROUP_DEBUG] boolValue]) {
65                 [ESDebugWindowController showDebugWindow];
66         }
67         
68         [[adium preferenceController] registerPreferenceObserver:self forGroup:GROUP_DEBUG];
71 - (void)controllerWillClose
73         //Save the open state of the debug window
74         [[adium preferenceController] setPreference:([ESDebugWindowController debugWindowIsOpen] ?
75                                                                                                  [NSNumber numberWithBool:YES] :
76                                                                                                  nil)
77                                                                                  forKey:KEY_DEBUG_WINDOW_OPEN
78                                                                                   group:GROUP_DEBUG];
79         [ESDebugWindowController closeDebugWindow];
82 + (ESDebugController *)sharedDebugController
84         return sharedDebugController;
87 - (void)dealloc
89         [debugLogArray release];
90         [debugLogFile closeFile];
91         [debugLogFile release];
93         sharedDebugController = nil;
95         [super dealloc];
98 - (void)showDebugWindow:(id)sender
100         [NSApp activateIgnoringOtherApps:YES];
101         [ESDebugWindowController showDebugWindow];
104 - (void)addMessage:(NSString *)actualMessage
106         if ((![actualMessage hasSuffix:@"\n"]) && (![actualMessage hasSuffix:@"\r"])) {
107                 actualMessage = [actualMessage stringByAppendingString:@"\n"];
108         }
110         [debugLogArray addObject:actualMessage];
112         if (debugLogFile) {
113                 [debugLogFile writeData:[actualMessage dataUsingEncoding:NSUTF8StringEncoding]];
114         }
116         //Keep debugLogArray to a reasonable size
117         if ([debugLogArray count] > CACHED_DEBUG_LOGS) [debugLogArray removeObjectAtIndex:0];
118         
119         [ESDebugWindowController addedDebugMessage:actualMessage];
122 - (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key
123                                                         object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
125         if (firstTime || [key isEqualToString:KEY_DEBUG_WRITE_LOG]) {
126                 BOOL    writeLogs = [[prefDict objectForKey:KEY_DEBUG_WRITE_LOG] boolValue];
127                 if (writeLogs) {
128                         [self debugLogFile];
129                         
130                 } else {
131                         [debugLogFile release]; debugLogFile = nil;
132                 }
133         }
136 - (NSArray *)debugLogArray
138         return debugLogArray;
140 - (void)clearDebugLogArray
142         [debugLogArray removeAllObjects]; 
145 - (NSFileHandle *)debugLogFile
147         if (!debugLogFile) {
148                 NSFileManager *mgr = [NSFileManager defaultManager];
149                 NSCalendarDate *date = [NSCalendarDate calendarDate];
150                 NSString *folder, *dateString, *filename, *pathname;
151                 unsigned counter = 0;
152                 int fd;
153                 
154                 //make sure the containing folder for debug logs exists.
155                 folder = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, /*expandTilde*/ YES) objectAtIndex:0];
156                 folder = [folder stringByAppendingPathComponent:@"Logs"];
157                 folder = [folder stringByAppendingPathComponent:@"Adium Debug"];
158                 BOOL success = [mgr createDirectoryAtPath:folder attributes:nil];
159                 if((!success) && (errno != EEXIST)) {
160                         /*raise an exception if the folder could not be created,
161                         *       but not if that was because it already exists.
162                         */
163                         NSAssert2(success, @"Could not create folder %@: %s", folder, strerror(errno));
164                 }
165                 
166                 /*get today's date, for the filename.
167                         *the date is in YYYY-MM-DD format. duplicates are disambiguated with
168                         *' 1', ' 2', ' 3', etc. appendages.
169                         */
170                 filename = dateString = [date descriptionWithCalendarFormat:@"%Y-%m-%d"];
171                 while([mgr fileExistsAtPath:(pathname = [folder stringByAppendingPathComponent:[filename stringByAppendingPathExtension:@"log"]])]) {
172                         filename = [dateString stringByAppendingFormat:@" %u", ++counter];
173                 }
174                 
175                 //create (if necessary) and open the file as writable, in append mode.
176                 fd = open([pathname fileSystemRepresentation], O_CREAT | O_WRONLY | O_APPEND, 0644);
177                 NSAssert2(fd > -1, @"could not create %@ nor open it for writing: %s", pathname, strerror(errno));
178                 
179                 //note: the file handle takes ownership of fd.
180                 /*
181                  * From the docs:  "The object creating an NSFileHandle using this method owns fileDescriptor and is responsible for its disposition."
182                  * which seems to indicate that the file handle does not take ownership of fd. Just for the record. -eds
183                  */
184                 debugLogFile = [[NSFileHandle alloc] initWithFileDescriptor:fd];
185                 if(!debugLogFile) close(fd);
186                 NSAssert1(debugLogFile != nil, @"could not create file handle for %@", pathname);
187                 
188                 //write header (separates this session from previous sessions).
189                 [debugLogFile writeData:[[NSString stringWithFormat:@"Opened debug log at %@\n", date] dataUsingEncoding:NSUTF8StringEncoding]];
190         }
192         return debugLogFile;
195 #else
196         - (void)controllerDidLoad {};
197         - (void)controllerWillClose {};
198 #endif /* DEBUG_BUILD */
200 @end