sogo/Tools/SOGoToolTruncateCalendar.m

273 lines
7.6 KiB
Objective-C

/* SOGoToolTruncateCalendar.m - this file is part of SOGo
*
* Copyright (C) 2009-2020 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This file 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSValue.h>
#import <EOControl/EOQualifier.h>
#import <GDLAccess/EOAdaptorChannel.h>
#import <GDLAccess/EOAdaptorContext.h>
#import <GDLContentStore/GCSFolderManager.h>
#import <GDLContentStore/GCSFolder.h>
#import "SOGoTool.h"
@interface SOGoToolTruncateCalendar : SOGoTool
{
BOOL recurrent;
}
@end
@implementation SOGoToolTruncateCalendar
+ (NSString *) command
{
return @"truncate-calendar";
}
+ (NSString *) description
{
return @"remove old calendar entries from the specified user calendar";
}
- (BOOL) parseArguments
{
BOOL rc;
int max;
rc = NO;
max = [arguments count];
if (max > 2)
{
recurrent = [[arguments objectAtIndex: 0] isEqualToString: @"-r"];
if (recurrent)
{
arguments = RETAIN([arguments subarrayWithRange: NSMakeRange(1, max-1)]);
max--;
}
if (max == 3)
rc = YES;
}
if (!rc)
[self usage];
return rc;
}
- (void) removeRecord: (NSString *) recordName
fromTable: (NSString *) tableName
andQuickTable: (NSString *) quickTableName
usingChannel: (EOAdaptorChannel *) channel
{
NSString *delSql;
NSCalendarDate *now;
/* We remove the records without regards to c_deleted because we really want
to recover table space. */
now = [NSCalendarDate date];
delSql = [NSString stringWithFormat: @"UPDATE %@"
@" SET c_deleted = 1, c_lastmodified = %lu,"
@" c_content = ''"
@" WHERE c_name = '%@'",
tableName,
(NSUInteger) [now timeIntervalSince1970],
recordName];
[channel evaluateExpressionX: delSql];
delSql = [NSString stringWithFormat: @"DELETE FROM %@"
@" WHERE c_name = '%@'",
quickTableName, recordName];
[channel evaluateExpressionX: delSql];
}
- (void) removeRecords: (NSArray *) recordNames
fromFolder: (GCSFolder *) folder
{
NSString *tableName, *quickTableName, *currentRecordName;
NSDictionary *currentRecord;
EOAdaptorChannel *channel;
EOAdaptorContext *context;
NSEnumerator *recordsEnum;
fprintf (stderr,
#if GS_64BIT_OLD
"Removing %d records...\n",
#else
"Removing %ld records...\n",
#endif
[recordNames count]);
channel = [folder acquireStoreChannel];
context = [channel adaptorContext];
[context beginTransaction];
tableName = [folder storeTableName];
quickTableName = [folder quickTableName];
recordsEnum = [recordNames objectEnumerator];
while ((currentRecord = [recordsEnum nextObject]))
{
currentRecordName = [currentRecord objectForKey: @"c_name"];
[self removeRecord: currentRecordName
fromTable: tableName andQuickTable: quickTableName
usingChannel: channel];
}
[context commitTransaction];
[folder releaseChannel: channel immediately: YES];
}
- (BOOL) truncateEntriesFromFolder: (GCSFolder *) folder
usingDate: (NSCalendarDate *) date
{
NSMutableArray *records;
EOQualifier *qualifier;
NSArray *fields;
NSString *qs;
BOOL rc;
records = [NSMutableArray array];
fields = [NSArray arrayWithObjects: @"c_name", nil];
if (recurrent)
{
// We fetch all events, including recurrent
qs = [NSString stringWithFormat: @"c_enddate <= %d AND c_component == 'vevent'", (int)[date timeIntervalSince1970]];
qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
[records addObjectsFromArray: [folder fetchFields: fields matchingQualifier: qualifier]];
}
else
{
// We fetch non-repetitive events
qs = [NSString stringWithFormat: @"c_enddate <= %d AND c_iscycle == 0 AND c_component == 'vevent'", (int)[date timeIntervalSince1970]];
qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
[records addObjectsFromArray: [folder fetchFields: fields matchingQualifier: qualifier]];
// We fetch repetitive events with a cycle end date
qs = [NSString stringWithFormat: @"c_cycleenddate <= %d AND c_iscycle == 1 AND c_component == 'vevent'", (int)[date timeIntervalSince1970]];
qualifier = [EOQualifier qualifierWithQualifierFormat: qs];
[records addObjectsFromArray: [folder fetchFields: fields matchingQualifier: qualifier]];
}
if (records)
{
rc = YES;
if ([records count])
{
[self removeRecords: records fromFolder: folder];
fprintf (stderr,
#if GS_64BIT_OLD
"Removed %d records.\n",
#else
"Removed %ld records.\n",
#endif
[records count]);
}
else
fprintf (stderr,
"No record to remove. All records kept.\n");
}
else
{
fprintf (stderr, "Unable to fetch required fields from folder.\n");
rc = NO;
}
return rc;
}
- (BOOL) processFolder: (NSString *) folderId
ofUser: (NSString *) username
date: (NSCalendarDate *) date
withFoM: (GCSFolderManager *) fom
{
NSString *folderPath;
GCSFolder *folder;
BOOL rc;
folderPath = [NSString stringWithFormat: @"/Users/%@/Calendar/%@",
username, folderId];
folder = [fom folderAtPath: folderPath];
if (folder)
rc = [self truncateEntriesFromFolder: folder usingDate: date];
else
{
fprintf (stderr, "Folder '%s' of user '%s' not found.\n",
[folderId UTF8String], [username UTF8String]);
rc = NO;
}
return rc;
}
- (BOOL) runWithFolder: (NSString *) folder
andUser: (NSString *) username
date: (NSString *) date
{
GCSFolderManager *fom;
NSCalendarDate *d;
NSString *s;
BOOL rc;
// We force parsing in the GMT timezone. If we don't do that, the date will be parsed
// in the default timezone.
s = [NSString stringWithFormat: @"%@ GMT", date];
d = [NSCalendarDate dateWithString: s calendarFormat: @"%Y-%m-%dT%H:%M:%S %Z"];
fom = [GCSFolderManager defaultFolderManager];
if (d && fom)
rc = [self processFolder: folder
ofUser: username
date: d
withFoM: fom];
else
rc = NO;
return rc;
}
- (void) usage
{
fprintf (stderr, "Usage: truncate-calendar [-r] USER FOLDER DATE\n\n"
" -r also delete recurrent events (including all occurences)\n"
" USER the owner of the calendar folder\n"
" FOLDER the id of the folder to clean up\n"
" DATE UTC datetime - events older than this date will be removed\n\n"
"Examples: sogo-tool truncate-calendar bob personal 2016-06-27T17:38:56\n"
" sogo-tool truncate-calendar -r bob personal `date +%%FT%%T` # deletes all events\n\n");
}
- (BOOL) run
{
return ([self parseArguments] && [self runWithFolder: [arguments objectAtIndex: 1]
andUser: [arguments objectAtIndex: 0]
date: [arguments objectAtIndex: 2]]);
}
@end