Prevent sending 0-byte files. Fixes #8711.
[adiumx.git] / Source / AIChatLog.m
blob374858c55bb20809944bd1d3d2beb18186d85997
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 "AIChatLog.h"
18 #import "AIAbstractLogViewerWindowController.h"
19 #import "AILoggerPlugin.h"
20 #import "AICalendarDate.h"
22 @implementation AIChatLog
24 static NSCalendarDate *dateFromFileName(NSString *fileName);
26 - (id)initWithPath:(NSString *)inPath from:(NSString *)inFrom to:(NSString *)inTo serviceClass:(NSString *)inServiceClass
28     if ((self = [super init])) {
29                 path = [inPath retain];
30                 from = [inFrom retain];
31                 to = [inTo retain];
32                 serviceClass = [inServiceClass retain];
33                 rankingPercentage = 0;
34         }
36     return self;
39 - (id)initWithPath:(NSString *)inPath
41         NSString *parentPath = [path stringByDeletingLastPathComponent];
42         NSString *toUID = [parentPath lastPathComponent];
43         NSString *serviceAndFromUID = [[parentPath stringByDeletingLastPathComponent] lastPathComponent];
45         NSString *myServiceClass, *fromUID;
47         //Determine the service and fromUID - should be SERVICE.ACCOUNT_NAME
48         //Check against count to guard in case of old, malformed or otherwise odd folders & whatnot sitting in log base
49         NSArray *serviceAndFromUIDArray = [serviceAndFromUID componentsSeparatedByString:@"."];
50         
51         if ([serviceAndFromUIDArray count] >= 2) {
52                 myServiceClass = [serviceAndFromUIDArray objectAtIndex:0];
53                 
54                 //Use substringFromIndex so we include the rest of the string in the case of a UID with a . in it
55                 fromUID = [serviceAndFromUID substringFromIndex:([serviceClass length] + 1)]; //One off for the '.'
57         } else {
58                 //Fallback: blank non-nil serviceClass; folderName as the fromUID
59                 myServiceClass = @"";
60                 fromUID = serviceAndFromUID;
61         }
63         return [self initWithPath:inPath
64                                                  from:fromUID
65                                                    to:toUID
66                                  serviceClass:myServiceClass];
69 - (void)dealloc
71     [path release];
72     [from release];
73     [to release];
74         [serviceClass release];
75     [date release];
76     
77     [super dealloc];
80 - (NSString *)path{
81     return path;
83 - (NSString *)from{
84     return from;
86 - (NSString *)to{
87     return to;
89 - (NSString *)serviceClass{
90         return serviceClass;
92 - (NSCalendarDate *)date{
93         //Determine the date of this log lazily
94         if (!date) {
95                 date = [dateFromFileName([path lastPathComponent]) retain];
96         }
97                 
98     return date;
101 - (float)rankingPercentage
103         return rankingPercentage;
105 - (void)setRankingPercentage:(float)inRankingPercentage
107         rankingPercentage = inRankingPercentage;
110 - (void)setRankingValueOnArbitraryScale:(float)inRankingValue
112         rankingValue = inRankingValue;
114 - (float)rankingValueOnArbitraryScale
116         return rankingValue;
119 - (BOOL)isFromSameDayAsDate:(NSCalendarDate *)inDate
121         return [[self date] dayOfCommonEra] == [inDate dayOfCommonEra];
124 #pragma mark Sort Selectors
126 //Sort by To, then Date
127 - (NSComparisonResult)compareTo:(AIChatLog *)inLog
129     NSComparisonResult  result = [to caseInsensitiveCompare:[inLog to]];
130     if (result == NSOrderedSame) {
131                 NSTimeInterval          interval = [date timeIntervalSinceDate:[inLog date]];
132                 
133                 if (interval < 0) {
134                         result = NSOrderedAscending;
135                 } else if (interval > 0) {
136                         result = NSOrderedDescending;
137                 }
138         }
139         
140     return result;
142 - (NSComparisonResult)compareToReverse:(AIChatLog *)inLog
144     NSComparisonResult  result = [[inLog to] caseInsensitiveCompare:to];
145     if (result == NSOrderedSame) {
146                 NSTimeInterval          interval = [date timeIntervalSinceDate:[inLog date]];
147                 
148                 if (interval < 0) {
149                         result = NSOrderedAscending;
150                 } else if (interval > 0) {
151                         result = NSOrderedDescending;
152                 }
153         }
154         
155     return result;
157 //Sort by From, then Date
158 - (NSComparisonResult)compareFrom:(AIChatLog *)inLog
160     NSComparisonResult  result = [from caseInsensitiveCompare:[inLog from]];
161     if (result == NSOrderedSame) {
162                 NSTimeInterval          interval = [date timeIntervalSinceDate:[inLog date]];
163                 
164                 if (interval < 0) {
165                         result = NSOrderedAscending;
166                 } else if (interval > 0) {
167                         result = NSOrderedDescending;
168                 }
169         } 
170         
171     return result;
173 - (NSComparisonResult)compareFromReverse:(AIChatLog *)inLog
175     NSComparisonResult  result = [[inLog from] caseInsensitiveCompare:from];
176     if (result == NSOrderedSame) {
177                 NSTimeInterval          interval = [date timeIntervalSinceDate:[inLog date]];
178                 
179                 if (interval < 0) {
180                         result = NSOrderedAscending;
181                 } else if (interval > 0) {
182                         result = NSOrderedDescending;
183                 }
184         }
185     
186     return result;
189 //Sort by Date, then To
190 - (NSComparisonResult)compareDate:(AIChatLog *)inLog
192         NSComparisonResult  result;
193         NSTimeInterval          interval = [[self date] timeIntervalSinceDate:[inLog date]];
194         
195         if (interval < 0) {
196                 result = NSOrderedAscending;
197         } else if (interval > 0) {
198                 result = NSOrderedDescending;
199         } else {
200                 result = [to caseInsensitiveCompare:[inLog to]];
201     }
202         
203     return result;
205 - (NSComparisonResult)compareDateReverse:(AIChatLog *)inLog
207         NSComparisonResult  result;
208         NSTimeInterval          interval = [[inLog date] timeIntervalSinceDate:[self date]];
210         if (interval < 0) {
211                 result = NSOrderedAscending;
212         } else if (interval > 0) {
213                 result = NSOrderedDescending;
214         } else {
215                 result = [[inLog to] caseInsensitiveCompare:to];
216     }
217         
218     return result;
221 -(NSComparisonResult)compareRank:(AIChatLog *)inLog
223         NSComparisonResult  result;
224         float                           otherRankingPercentage = [inLog rankingPercentage];
225         
226         if (rankingPercentage > otherRankingPercentage) {
227                 result = NSOrderedDescending;           
228         } else if (rankingPercentage < otherRankingPercentage) {
229                 result = NSOrderedAscending;    
230         } else {
231                 result = [to caseInsensitiveCompare:[inLog to]];
232     }
233         
234         return result;
236 -(NSComparisonResult)compareRankReverse:(AIChatLog *)inLog
238         NSComparisonResult  result;
239         float                           otherRankingPercentage = [inLog rankingPercentage];
240         
241         if (rankingPercentage > otherRankingPercentage) {
242                 result = NSOrderedAscending;            
243         } else if (rankingPercentage < otherRankingPercentage) {
244                 result = NSOrderedDescending;                           
245         } else {
246                 result = [[inLog to] caseInsensitiveCompare:to];
247     }
248         
249         return result;
252 #pragma mark Date utilities
254 //Scan an Adium date string, supahfast C style
255 static BOOL scandate(const char *sample,
256                                          unsigned long *outyear, unsigned long *outmonth,  unsigned long *outdate,
257                                          BOOL *outHasTime, unsigned long *outhour, unsigned long *outminute, unsigned long *outsecond,
258                                          signed long *outtimezone)
260         BOOL success = YES;
261         unsigned long component;
263     //Read a date, followed by a '('.
264         //First, find the '('.
265         while (*sample != '(') {
266         if (!*sample) {
267                 success = NO;
268                 goto fail;
269                 } else {
270                         ++sample;
271                 }
272     }
273         
274         //current character is a '(' now, so skip over it.
275     ++sample; //start with the next character
276         
277     /*get the year*/ {
278                 while (*sample && (*sample < '0' || *sample > '9')) ++sample;
279                 if (!*sample) {
280                         success = NO;
281                         goto fail;
282                 }
283                 component = strtoul(sample, (char **)&sample, 10);
284                 if (outyear) *outyear = component;
285     }
286     
287     /*get the month*/ {
288                 while (*sample && (*sample < '0' || *sample > '9')) ++sample;
289                 if (!*sample) {
290                         success = NO;
291                         goto fail;
292                 }
293                 component = strtoul(sample, (char **)&sample, 10);
294                 if (outmonth) *outmonth = component;
295     }
296     
297     /*get the date*/ {
298                 while (*sample && (*sample < '0' || *sample > '9')) ++sample;
299                 if (!*sample) {
300                         success = NO;
301                         goto fail;
302                 }
303                 component = strtoul(sample, (char **)&sample, 10);
304                 if (outdate) *outdate = component;
305     }
307     if (*sample == 'T') {
308                 ++sample; //start with the next character
309                 if (outHasTime) *outHasTime = YES;
310                 
311                 /*get the hour*/ {
312                         while (*sample && (*sample < '0' || *sample > '9')) ++sample;
313                         if (!*sample) {
314                                 success = NO;
315                                 goto fail;
316                         }
317                         component = strtoul(sample, (char **)&sample, 10);
318                         if (outhour) *outhour = component;
319                 }
321                 /*get the minute*/ {
322                         while (*sample && (*sample < '0' || *sample > '9')) ++sample;
323                         if (!*sample) {
324                                 success = NO;
325                                 goto fail;
326                         }
327                         component = strtoul(sample, (char **)&sample, 10);
328                         if (outminute) *outminute = component;
329                 }
331                 /*get the second*/ {
332                         while (*sample && (*sample < '0' || *sample > '9')) ++sample;
333                         if (!*sample) {
334                                 success = NO;
335                                 goto fail;
336                         }
337                         component = strtoul(sample, (char **)&sample, 10);
338                         if (outsecond) *outsecond = component;
339                 }
341                 /*get the time zone*/ {
342                         while (*sample && ((*sample < '0' || *sample > '9') && *sample != '-' && *sample != '+')) ++sample;
343                         if (!*sample) {
344                                 success = NO;
345                                 goto fail;
346                         }
347                         signed long timezone_sign = 1;
348                         if(*sample == '+') {
349                                 ++sample;
350                         } else if(*sample == '-') {
351                                 timezone_sign = -1;
352                                 ++sample;
353                         } else if (*sample) {
354                                 //There's something here, but it's not a time zone. Bail.
355                                 success = NO;
356                                 goto fail;
357                         }
358                         signed long timezone_hr = 0;
359                         if (*sample >= '0' || *sample <= '9') {
360                                 timezone_hr += *(sample++) - '0';
361                         }
362                         if (*sample >= '0' || *sample <= '9') {
363                                 timezone_hr *= 10;
364                                 timezone_hr += *(sample++) - '0';
365                         }
366                         signed long timezone_min = 0;
367                         if (*sample >= '0' || *sample <= '9') {
368                                 timezone_min += *(sample++) - '0';
369                         }
370                         if (*sample >= '0' || *sample <= '9') {
371                                 timezone_min *= 10;
372                                 timezone_min += *(sample++) - '0';
373                         }
374                         if (outtimezone) *outtimezone = (timezone_hr * 60 + timezone_min) * timezone_sign;
375                 }
376         }
377         
378 fail:
379         return success;
382 //Given an Adium log file name, return an NSCalendarDate with year, month, and day specified
383 static NSCalendarDate *dateFromFileName(NSString *fileName)
385         unsigned long   year = 0;
386         unsigned long   month = 0;
387         unsigned long   day = 0;
388         unsigned long   hour = 0;
389         unsigned long   minute = 0;
390         unsigned long   second = 0;
391           signed long   timezone = NSNotFound;
392         BOOL                    hasTime = NO;
393           
394         if (scandate([fileName UTF8String], &year, &month, &day, &hasTime, &hour, &minute, &second, &timezone)) {
395                 if (year && month && day) {
396                         AICalendarDate *calendarDate;
397                         
398                         calendarDate = [AICalendarDate dateWithYear:year
399                                                                                                   month:month
400                                                                                                         day:day
401                                                                                                    hour:hour
402                                                                                                  minute:minute
403                                                                                                  second:second
404                                                                                            timeZone:((timezone == NSNotFound) ? nil : [NSTimeZone timeZoneForSecondsFromGMT:(timezone * 60)])];
405                         [calendarDate setGranularity:(hasTime ? AISecondGranularity : AIDayGranularity)];
407                         return calendarDate;
408                 }
409         }
410         
411         return nil;
414 @end