1/* SOGoCacheGCSFolder.m - this file is part of SOGo 2 * 3 * Copyright (C) 2012-2014 Inverse inc. 4 * 5 * This file is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 3, or (at your option) 8 * any later version. 9 * 10 * This file is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; see the file COPYING. If not, write to 17 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 * Boston, MA 02111-1307, USA. 19 */ 20 21#import <Foundation/NSArray.h> 22#import <Foundation/NSData.h> 23#import <Foundation/NSDictionary.h> 24#import <Foundation/NSException.h> 25#import <Foundation/NSPropertyList.h> 26#import <Foundation/NSString.h> 27#import <Foundation/NSValue.h> 28#import <Foundation/NSURL.h> 29 30#import <NGExtensions/NSObject+Logs.h> 31#import <NGExtensions/NSNull+misc.h> 32#import <GDLAccess/EOAdaptorChannel.h> 33#import <GDLContentStore/GCSChannelManager.h> 34 35#import <SOGo/NSArray+Utilities.h> 36#import <SOGo/NSString+Utilities.h> 37#import <SOGo/SOGoDomainDefaults.h> 38#import <SOGo/SOGoUser.h> 39#import "EOQualifier+SOGoCacheObject.h" 40#import "GCSSpecialQueries+SOGoCacheObject.h" 41 42#import "SOGoCacheGCSFolder.h" 43 44#undef DEBUG 45//#include <stdbool.h> 46//#include <talloc.h> 47//#include <util/time.h> 48//#include <mapistore/mapistore.h> 49//#include <mapistore/mapistore_errors.h> 50//#include <libmapiproxy.h> 51//#include <param.h> 52 53Class SOGoCacheGCSObjectK = Nil; 54 55@implementation SOGoCacheGCSFolder 56 57+ (void) initialize 58{ 59 SOGoCacheGCSObjectK = [SOGoCacheGCSObject class]; 60} 61 62- (id) init 63{ 64 if ((self = [super init])) 65 { 66 pathPrefix = nil; 67 } 68 69 return self; 70} 71 72- (id) initWithName: (NSString *) name inContainer: (id) newContainer 73{ 74 if ((self = [super initWithName: name inContainer: newContainer])) 75 { 76 objectType = MAPIFolderCacheObject; 77 aclMessage = [SOGoCacheGCSObject objectWithName: @"permissions" 78 inContainer: self]; 79 [aclMessage setObjectType: MAPIInternalCacheObject]; 80 [aclMessage retain]; 81 } 82 83 return self; 84} 85 86- (void) dealloc 87{ 88 [aclMessage release]; 89 [pathPrefix release]; 90 [super dealloc]; 91} 92 93- (BOOL) isFolderish 94{ 95 return YES; 96} 97 98- (void) setPathPrefix: (NSString *) newPathPrefix 99{ 100 ASSIGN (pathPrefix, newPathPrefix); 101} 102 103- (NSMutableString *) pathForChild: (NSString *) childName 104{ 105 NSMutableString *path; 106 107 path = [self path]; 108 [path appendFormat: @"/%@", childName]; 109 110 return path; 111} 112 113- (NSMutableString *) path 114{ 115 NSMutableString *path; 116 117 path = [super path]; 118 if (pathPrefix) 119 [path insertString: pathPrefix atIndex: 0]; 120 121 return path; 122} 123 124// - (SOGoMAPIDBMessage *) newMessage 125// { 126// NSString *newFilename; 127 128// newFilename = [NSString stringWithFormat: @"%@.plist", 129// [SOGoObject globallyUniqueObjectId]]; 130 131// return [SOGoMAPIDBMessage objectWithName: filename inContainer: self]; 132// } 133 134- (NSArray *) childKeysOfType: (SOGoCacheObjectType) type 135 includeDeleted: (BOOL) includeDeleted 136 matchingQualifier: (EOQualifier *) qualifier 137 andSortOrderings: (NSArray *) sortOrderings 138{ 139 NSMutableArray *childKeys; 140 NSMutableString *sql// , *qualifierClause 141 ; 142 NSString *childPathPrefix, *childPath, *childKey; 143 NSMutableArray *whereClause; 144 NSArray *records; 145 NSDictionary *record; 146 NSUInteger childPathPrefixLen, count, max; 147 SOGoCacheGCSObject *currentChild; 148 149 /* query construction */ 150 sql = [NSMutableString stringWithCapacity: 256]; 151 [sql appendFormat: @"SELECT * FROM %@", [self tableName]]; 152 153 whereClause = [NSMutableArray arrayWithCapacity: 2]; 154 [whereClause addObject: [NSString stringWithFormat: @"c_parent_path = '%@'", 155 [self path]]]; 156 [whereClause addObject: [NSString stringWithFormat: @"c_type = %d", type]]; 157 if (!includeDeleted) 158 [whereClause addObject: @"c_deleted = 0"]; 159 160 [sql appendFormat: @" WHERE %@", 161 [whereClause componentsJoinedByString: @" AND "]]; 162 163 childPathPrefix = [NSString stringWithFormat: @"%@/", [self path]]; 164 165 /* results */ 166 records = [self performSQLQuery: sql]; 167 if (records) 168 { 169 max = [records count]; 170 childKeys = [NSMutableArray arrayWithCapacity: max]; 171 childPathPrefixLen = [childPathPrefix length]; 172 for (count = 0; count < max; count++) 173 { 174 record = [records objectAtIndex: count]; 175 childPath = [record objectForKey: @"c_path"]; 176 childKey = [childPath substringFromIndex: childPathPrefixLen]; 177 if ([childKey rangeOfString: @"/"].location == NSNotFound) 178 { 179 if (qualifier) 180 { 181 currentChild = [SOGoCacheGCSObject objectWithName: childKey 182 inContainer: self]; 183 [currentChild setupFromRecord: record]; 184 if ([qualifier evaluateSOGoMAPIDBObject: currentChild]) 185 [childKeys addObject: childKey]; 186 } 187 else 188 [childKeys addObject: childKey]; 189 } 190 } 191 } 192 else 193 childKeys = nil; 194 195 return childKeys; 196} 197 198- (NSArray *) toManyRelationshipKeys 199{ 200 return [self childKeysOfType: MAPIFolderCacheObject 201 includeDeleted: NO 202 matchingQualifier: nil 203 andSortOrderings: nil]; 204} 205 206- (NSArray *) toOneRelationshipKeys 207{ 208 return [self childKeysOfType: MAPIMessageCacheObject 209 includeDeleted: NO 210 matchingQualifier: nil 211 andSortOrderings: nil]; 212} 213 214- (void) setNameInContainer: (NSString *) newName 215{ 216 NSMutableString *sql; 217 NSString *oldPath, *newPath, *path, *parentPath; 218 NSMutableArray *queries; 219 NSArray *records; 220 NSDictionary *record; 221 NSUInteger count, max; 222 223 /* change the paths in children records */ 224 if (nameInContainer) 225 oldPath = [self path]; 226 227 [super setNameInContainer: newName]; 228 229 if (nameInContainer) 230 { 231 newPath = [self path]; 232 233 sql = [NSMutableString stringWithFormat: 234 @"SELECT c_path, c_parent_path FROM %@" 235 @" WHERE c_path LIKE '%@/%%'", 236 [self tableName], oldPath]; 237 records = [self performSQLQuery: sql]; 238 max = [records count]; 239 queries = [NSMutableArray arrayWithCapacity: max + 1]; 240 if (max > 0) 241 { 242 for (count = 0; count < max; count++) 243 { 244 record = [records objectAtIndex: count]; 245 path = [record objectForKey: @"c_path"]; 246 sql = [NSMutableString stringWithFormat: @"UPDATE %@" 247 @" SET c_path = '%@'", 248 [self tableName], 249 [path stringByReplacingPrefix: oldPath 250 withPrefix: newPath]]; 251 parentPath = [record objectForKey: @"c_parent_path"]; 252 if ([parentPath isNotNull]) 253 [sql appendFormat: @", c_parent_path = '%@'", 254 [parentPath stringByReplacingPrefix: oldPath 255 withPrefix: newPath]]; 256 [sql appendFormat: @" WHERE c_path = '%@'", path]; 257 [queries addObject: sql]; 258 } 259 [self performBatchSQLQueries: queries]; 260 } 261 } 262} 263 264- (void) changePathTo: (NSString *) newPath 265{ 266 NSMutableString *sql// , *qualifierClause 267 ; 268 NSString *oldPath, *oldPathAsPrefix, *path, *parentPath; 269 NSMutableArray *queries; 270 NSArray *records; 271 NSDictionary *record; 272 NSUInteger count, max; 273 274 /* change the paths in children records */ 275 oldPath = [self path]; 276 oldPathAsPrefix = [NSString stringWithFormat: @"%@/", oldPath]; 277 278 sql = [NSMutableString stringWithFormat: 279 @"SELECT c_path, c_parent_path FROM %@" 280 @" WHERE c_path LIKE '%@%%'", 281 [self tableName], oldPathAsPrefix]; 282 records = [self performSQLQuery: sql]; 283 max = [records count]; 284 queries = [NSMutableArray arrayWithCapacity: max + 1]; 285 if (max > 0) 286 { 287 for (count = 0; count < max; count++) 288 { 289 record = [records objectAtIndex: count]; 290 path = [record objectForKey: @"c_path"]; 291 sql = [NSMutableString stringWithFormat: @"UPDATE %@" 292 @" SET c_path = '%@'", 293 [self tableName], 294 [path stringByReplacingPrefix: oldPath 295 withPrefix: newPath]]; 296 parentPath = [record objectForKey: @"c_parent_path"]; 297 if ([parentPath isNotNull]) 298 [sql appendFormat: @", c_parent_path = '%@'", 299 [parentPath stringByReplacingPrefix: oldPath 300 withPrefix: newPath]]; 301 [sql appendFormat: @" WHERE c_path = '%@'", path]; 302 [queries addObject: sql]; 303 } 304 [self performBatchSQLQueries: queries]; 305 } 306 307 /* change the path in this folder record */ 308 [super changePathTo: newPath]; 309} 310 311- (void) changePathTo: (NSString *) newPath intoNewContainer: (id) newContainer 312{ 313 [self changePathTo: newPath]; 314 container = newContainer; 315 if ([self doesRetainContainer]) 316 [container retain]; 317} 318 319// - (NSArray *) toOneRelationshipKeysMatchingQualifier: (EOQualifier *) qualifier 320// andSortOrderings: (NSArray *) sortOrderings 321// { 322// NSArray *allKeys; 323// NSMutableArray *keys; 324// NSUInteger count, max; 325// NSString *messageKey; 326// SOGoMAPIDBMessage *message; 327 328// if (sortOrderings) 329// [self warnWithFormat: @"sorting is not handled yet"]; 330 331// allKeys = [self toOneRelationshipKeys]; 332// if (qualifier) 333// { 334// [self logWithFormat: @"%s: getting restricted FAI keys", __PRETTY_FUNCTION__]; 335// max = [allKeys count]; 336// keys = [NSMutableArray arrayWithCapacity: max]; 337// for (count = 0; count < max; count++) 338// { 339// messageKey = [allKeys objectAtIndex: count]; 340// message = [self lookupName: messageKey 341// inContext: nil 342// acquire: NO]; 343// if ([qualifier evaluateMAPIVolatileMessage: message]) 344// [keys addObject: messageKey]; 345// } 346// } 347// else 348// keys = (NSMutableArray *) allKeys; 349 350// return keys; 351// } 352 353- (id) lookupName: (NSString *) childName 354 inContext: (WOContext *) woContext 355 acquire: (BOOL) acquire 356{ 357 id object; 358 Class objectClass; 359 NSString *childPath; 360 NSDictionary *record; 361 362 childPath = [self pathForChild: childName]; 363 record = [self lookupRecord: childPath newerThanVersion: -1]; 364 if (record) 365 { 366 if ([[record objectForKey: @"c_type"] intValue] == MAPIFolderCacheObject) 367 objectClass = object_getClass(self); 368 else 369 objectClass = SOGoCacheGCSObjectK; 370 371 object = [objectClass objectWithName: childName 372 inContainer: self]; 373 [object setupFromRecord: record]; 374 } 375 else 376 object = nil; 377 378 return object; 379} 380 381- (id) lookupFolder: (NSString *) folderName 382 inContext: (WOContext *) woContext 383{ 384 id object; 385 386 object = [SOGoCacheGCSFolder objectWithName: folderName 387 inContainer: self]; 388 [object reloadIfNeeded]; 389 390 return object; 391} 392 393// - (id) _fileAttributeForKey: (NSString *) key 394// { 395// NSDictionary *attributes; 396 397// attributes = [[NSFileManager defaultManager] 398// fileAttributesAtPath: directory 399// traverseLink: NO]; 400 401// return [attributes objectForKey: key]; 402// } 403 404// - (NSCalendarDate *) creationTime 405// { 406// return [self _fileAttributeForKey: NSFileCreationDate]; 407// } 408 409// - (NSCalendarDate *) lastModificationTime 410// { 411// return [self _fileAttributeForKey: NSFileModificationDate]; 412// } 413 414/* acl */ 415- (NSString *) defaultUserID 416{ 417 return @"default"; 418} 419 420- (NSMutableDictionary *) _aclEntries 421{ 422 NSMutableDictionary *aclEntries; 423 424 [aclMessage reloadIfNeeded]; 425 aclEntries = [aclMessage properties]; 426 if (![aclEntries objectForKey: @"users"]) 427 [aclEntries setObject: [NSMutableArray array] forKey: @"users"]; 428 if (![aclEntries objectForKey: @"entries"]) 429 [aclEntries setObject: [NSMutableDictionary dictionary] 430 forKey: @"entries"]; 431 432 return aclEntries; 433} 434 435- (void) addUserInAcls: (NSString *) user 436{ 437 NSMutableDictionary *acl; 438 NSMutableArray *users; 439 440 acl = [self _aclEntries]; 441 users = [acl objectForKey: @"users"]; 442 [users addObjectUniquely: user]; 443 [aclMessage save]; 444} 445 446- (void) removeAclsForUsers: (NSArray *) oldUsers 447{ 448 NSDictionary *acl; 449 NSMutableDictionary *entries; 450 NSMutableArray *users; 451 452 acl = [self _aclEntries]; 453 entries = [acl objectForKey: @"entries"]; 454 [entries removeObjectsForKeys: oldUsers]; 455 users = [acl objectForKey: @"users"]; 456 [users removeObjectsInArray: oldUsers]; 457 [aclMessage save]; 458} 459 460- (NSArray *) aclUsers 461{ 462 return [[self _aclEntries] objectForKey: @"users"]; 463} 464 465- (NSArray *) aclsForUser: (NSString *) uid 466{ 467 NSDictionary *entries; 468 469 entries = [[self _aclEntries] objectForKey: @"entries"]; 470 471 return [entries objectForKey: uid]; 472} 473 474- (void) setRoles: (NSArray *) roles 475 forUser: (NSString *) uid 476{ 477 NSMutableDictionary *acl; 478 NSMutableDictionary *entries; 479 480 acl = [self _aclEntries]; 481 entries = [acl objectForKey: @"entries"]; 482 [entries setObject: roles forKey: uid]; 483 [aclMessage save]; 484} 485 486@end 487