1/* UIxFolderActions.m - this file is part of SOGo 2 * 3 * Copyright (C) 2007-2013 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 2, 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/NSDictionary.h> 22#import <Foundation/NSEnumerator.h> 23#import <Foundation/NSString.h> 24#import <Foundation/NSURL.h> 25#import <Foundation/NSValue.h> 26 27#import <NGObjWeb/WOContext.h> 28#import <NGObjWeb/WOContext+SoObjects.h> 29#import <NGObjWeb/WORequest.h> 30#import <NGObjWeb/WOResponse.h> 31#import <NGObjWeb/SoSecurityManager.h> 32#import <NGObjWeb/SoClassSecurityInfo.h> 33#import <NGObjWeb/NSException+HTTP.h> 34 35#import <SoObjects/SOGo/SOGoUserManager.h> 36#import <SoObjects/SOGo/NSArray+Utilities.h> 37#import <SoObjects/SOGo/SOGoContentObject.h> 38#import <SoObjects/SOGo/SOGoGCSFolder.h> 39#import <SoObjects/SOGo/SOGoParentFolder.h> 40#import <SoObjects/SOGo/SOGoPermissions.h> 41#import <SoObjects/SOGo/SOGoUser.h> 42#import <SoObjects/SOGo/SOGoUserSettings.h> 43 44#import "WODirectAction+SOGo.h" 45 46#import "UIxFolderActions.h" 47 48@implementation UIxFolderActions 49 50- (void) _setupContext 51{ 52 NSString *mailInvitationParam; 53 SOGoUser *activeUser; 54 55 activeUser = [context activeUser]; 56 login = [activeUser login]; 57 clientObject = [self clientObject]; 58 owner = [clientObject ownerInContext: nil]; 59 60 baseFolder = [[clientObject container] nameInContainer]; 61 62 um = [SOGoUserManager sharedUserManager]; 63 us = [activeUser userSettings]; 64 moduleSettings = [us objectForKey: baseFolder]; 65 if (!moduleSettings) 66 moduleSettings = [NSMutableDictionary dictionary]; 67 [us setObject: moduleSettings forKey: baseFolder]; 68 69 mailInvitationParam 70 = [[context request] formValueForKey: @"mail-invitation"]; 71 isMailInvitation = [mailInvitationParam boolValue]; 72} 73 74- (WOResponse *) _subscribeAction: (BOOL) reallyDo 75{ 76 WOResponse *response; 77 NSURL *mailInvitationURL; 78 79 response = [context response]; 80 [response setHeader: @"text/plain; charset=utf-8" 81 forKey: @"Content-Type"]; 82 83 [self _setupContext]; 84 if ([owner isEqualToString: login]) 85 { 86 [response setStatus: 403]; 87 [response appendContentString: 88 @"You cannot (un)subscribe to a folder that you own!"]; 89 } 90 else 91 { 92 [clientObject subscribeUserOrGroup: login 93 reallyDo: reallyDo 94 response: response]; 95 96 if (isMailInvitation) 97 { 98 mailInvitationURL 99 = [clientObject soURLToBaseContainerForCurrentUser]; 100 [response setStatus: 302]; 101 [response setHeader: [mailInvitationURL absoluteString] 102 forKey: @"location"]; 103 } 104 else 105 [response setStatus: 204]; 106 } 107 108 return response; 109} 110 111- (WOResponse *) subscribeAction 112{ 113 return [self _subscribeAction: YES]; 114} 115 116- (WOResponse *) unsubscribeAction 117{ 118 return [self _subscribeAction: NO]; 119} 120 121- (WOResponse *) canAccessContentAction 122{ 123 /* We want this action to be authorized managed by the SOPE's internal acl 124 handling. */ 125 return [self responseWith204]; 126// #warning IMPROVEMENTS REQUIRED! 127// NSArray *acls; 128// // NSEnumerator *userAcls; 129// // NSString *currentAcl; 130 131// [self _setupContext]; 132 133// // NSLog(@"canAccessContentAction %@, owner %@", subscriptionPointer, owner); 134 135// if ([login isEqualToString: owner] || [owner isEqualToString: @"nobody"]) { 136// return [self responseWith204]; 137// } 138// else { 139// acls = [clientObject aclsForUser: login]; 140// // userAcls = [acls objectEnumerator]; 141// // currentAcl = [userAcls nextObject]; 142// // while (currentAcl) { 143// // NSLog(@"ACL login %@, owner %@, folder %@: %@", 144// // login, owner, baseFolder, currentAcl); 145// // currentAcl = [userAcls nextObject]; 146// // } 147// if (([[clientObject folderType] isEqualToString: @"Contact"] 148// && [acls containsObject: SOGoRole_ObjectViewer]) || 149// ([[clientObject folderType] isEqualToString: @"Appointment"] 150// && [acls containsObject: SOGoRole_AuthorizedSubscriber])) { 151// return [self responseWith204]; 152// } 153// } 154 155// return [self responseWithStatus: 403]; 156} 157 158- (WOResponse *) _realFolderActivation: (BOOL) makeActive 159{ 160 NSMutableArray *folderSubscription; 161 NSString *folderName; 162 163 [self _setupContext]; 164 folderSubscription 165 = [moduleSettings objectForKey: @"InactiveFolders"]; 166 if (!folderSubscription) 167 { 168 folderSubscription = [NSMutableArray array]; 169 [moduleSettings setObject: folderSubscription forKey: @"InactiveFolders"]; 170 } 171 172 folderName = [clientObject nameInContainer]; 173 if (makeActive) 174 [folderSubscription removeObject: folderName]; 175 else 176 [folderSubscription addObjectUniquely: folderName]; 177 178 [us synchronize]; 179 180 return [self responseWith204]; 181} 182 183- (WOResponse *) activateFolderAction 184{ 185 return [self _realFolderActivation: YES]; 186} 187 188- (WOResponse *) deactivateFolderAction 189{ 190 return [self _realFolderActivation: NO]; 191} 192 193- (WOResponse *) renameFolderAction 194{ 195 WOResponse *response; 196 NSString *folderName; 197 198 folderName = [[context request] formValueForKey: @"name"]; 199 if ([folderName length] > 0) 200 { 201 clientObject = [self clientObject]; 202 [clientObject renameTo: folderName]; 203 response = [self responseWith204]; 204 } 205 else 206 { 207 response = [self responseWithStatus: 500]; 208 [response appendContentString: @"Missing 'name' parameter."]; 209 } 210 211 return response; 212} 213 214- (id) batchDeleteAction 215{ 216 WOResponse *response; 217 NSString *idsParam; 218 NSArray *ids; 219 220 idsParam = [[context request] formValueForKey: @"ids"]; 221 ids = [idsParam componentsSeparatedByString: @","]; 222 if ([ids count]) 223 { 224 clientObject = [self clientObject]; 225 [clientObject deleteEntriesWithIds: ids]; 226 response = [self responseWith204]; 227 } 228 else 229 { 230 response = [self responseWithStatus: 500]; 231 [response appendContentString: @"At least 1 id required."]; 232 } 233 234 return response; 235} 236 237- (NSException*) _moveContacts: (NSArray*) contactsId 238 toFolder: (NSString*) destinationFolderId 239 andKeepCopy: (BOOL) keepCopy 240{ 241 NSEnumerator *uids; 242 NSException *ex; 243 NSString *uid; 244 SOGoContentObject *currentChild; 245 SOGoGCSFolder *sourceFolder, *destinationFolder; 246 SOGoParentFolder *folders; 247 SoSecurityManager *sm; 248 unsigned int errorCount; 249 250 sm = [SoSecurityManager sharedSecurityManager]; 251 ex = nil; 252 errorCount = 0; 253 254 // Search the specified destination folder 255 sourceFolder = [self clientObject]; 256 folders = [sourceFolder container]; 257 destinationFolder = [folders lookupName: destinationFolderId 258 inContext: nil 259 acquire: NO]; 260 if (destinationFolder) 261 { 262 // Verify write access to the folder 263 ex = [sm validatePermission: SoPerm_AddDocumentsImagesAndFiles 264 onObject: destinationFolder 265 inContext: context]; 266 if (!ex) 267 { 268 uids = [contactsId objectEnumerator]; 269 while ((uid = [uids nextObject])) 270 { 271 // Search the currentChild ID 272 currentChild = [sourceFolder lookupName: uid 273 inContext: [self context] 274 acquire: NO]; 275 if ([currentChild isKindOfClass: [NSException class]]) 276 errorCount++; 277 else 278 { 279 if (keepCopy) 280 ex = [currentChild copyToFolder: destinationFolder]; 281 else 282 ex = [currentChild moveToFolder: destinationFolder]; 283 if (ex) 284 errorCount++; 285 } 286 } 287 } 288 } 289 else 290 ex = [NSException exceptionWithName: @"UnknownDestinationFolder" 291 reason: @"Unknown Destination Folder" 292 userInfo: nil]; 293 294 if (errorCount > 0) 295 // At least one currentChild was not copied 296 ex = [NSException exceptionWithHTTPStatus: 400 297 reason: @"Invalid Contact"]; 298 else if (ex != nil) 299 // Destination address book doesn't exist or is not writable 300 ex = [NSException exceptionWithHTTPStatus: 403 301 reason: [ex name]]; 302 303 return ex; 304} 305 306- (id <WOActionResults>) copyAction 307{ 308 WORequest *request; 309 id <WOActionResults> response; 310 NSString *destinationFolderId; 311 NSArray *contactsId; 312 NSException *ex; 313 314 request = [context request]; 315 316 if ((destinationFolderId = [request formValueForKey: @"folder"]) && 317 (contactsId = [request formValuesForKey: @"uid"])) 318 ex = [self _moveContacts: contactsId 319 toFolder: destinationFolderId 320 andKeepCopy: YES]; 321 else 322 ex = [NSException exceptionWithHTTPStatus: 400 323 reason: (@"missing 'folder' and/or" 324 @" 'uid' parameter")]; 325 326 if (ex) 327 response = (id) ex; 328 else 329 response = [self responseWith204]; 330 331 return response; 332} 333 334- (id <WOActionResults>) moveAction 335{ 336 WORequest *request; 337 id <WOActionResults> response; 338 NSString *destinationFolderId; 339 NSArray *contactsId; 340 NSException *ex; 341 342 request = [context request]; 343 344 if ((destinationFolderId = [request formValueForKey: @"folder"]) 345 && (contactsId = [request formValuesForKey: @"uid"])) 346 ex = [self _moveContacts: contactsId 347 toFolder: destinationFolderId 348 andKeepCopy: NO]; 349 else 350 ex = [NSException exceptionWithHTTPStatus: 400 351 reason: (@"missing 'folder' and/or" 352 @"'uid' parameter")]; 353 354 if (ex) 355 response = (id <WOActionResults>) ex; 356 else 357 response = [self responseWith204]; 358 359 return response; 360} 361 362- (id <WOActionResults>) subscribeUsersAction 363{ 364 id <WOActionResults> response; 365 NSString *uids; 366 NSArray *userIDs; 367 SOGoGCSFolder *folder; 368 NSException *ex; 369 int count, max; 370 371 uids = [[context request] formValueForKey: @"uids"]; 372 if ([uids length]) 373 { 374 userIDs = [uids componentsSeparatedByString: @","]; 375 folder = [self clientObject]; 376 max = [userIDs count]; 377 for (count = 0; count < max; count++) 378 [folder subscribeUserOrGroup: [userIDs objectAtIndex: count] 379 reallyDo: YES 380 response: nil]; 381 ex = nil; 382 } 383 else 384 ex = [NSException exceptionWithHTTPStatus: 400 385 reason: @"missing 'uids' parameter"]; 386 387 if (ex) 388 response = (id <WOActionResults>) ex; 389 else 390 response = [self responseWith204]; 391 392 return response; 393} 394 395@end 396