Completed
Push — 16.1 ( 303587...2e5ca7 )
by Hadi
178:48 queued 162:48
created

mail_acl::_extract_acc_id()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * EGroupware - Mail Folder ACL- interface class
4
 *
5
 * @link http://www.egroupware.org
6
 * @package mail
7
 * @author Hadi Nategh [[email protected]]
8
 * @copyright (c) 2013-16 by EGroupware GmbH <info-AT-egroupware.org>
9
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
10
 * @version $Id$
11
 */
12
13
use EGroupware\Api;
14
use EGroupware\Api\Framework;
15
use EGroupware\Api\Etemplate;
16
use EGroupware\Api\Mail;
17
18
class mail_acl
19
{
20
	/**
21
	 * Methods callable via menuaction
22
	 *
23
	 * @var array
24
	 */
25
	var $public_functions = array(
26
		'edit'	=> True,
27
	);
28
29
	/**
30
	 * static used define abbreviations for common access rights
31
	 *
32
	 * @array
33
	 *
34
	 */
35
	var $aclRightsAbbrvs = array(
36
		'lrs'		=> array('label'=>'readable','title'=>'Allows a user to read the contents of the mailbox.'),
37
		'lprs'		=> array('label'=>'post','title'=>'Allows a user to read the mailbox and post to it through the delivery system by sending mail to the submission address of the mailbox.'),
38
		'ilprs'		=> array('label'=>'append','title'=>'Allows a user to read the mailbox and append messages to it, either via IMAP or through the delivery system.'),
39
		'ilprsw'	=> array('label'=>'write','title'=>'Allows a user to read the maibox, post to it, append messages to it, and delete messages or the mailbox itself. The only right not given is the right to change the ACL of the mailbox.'),
40
		'aeiklprstwx'=> array('label'=>'all','title'=>'The user has all possible rights on the mailbox. This is usually granted to users only on the mailboxes they own.'),
41
		'custom'	=> array('label'=>'custom','title'=>'User defined combination of rights for the ACL'),
42
	);
43
44
	/**
45
	 * imap object instantiated in constructor for account to edit
46
	 *
47
	 * @var Mail\Imap
48
	 */
49
	var $imap;
50
51
	/**
52
	 *
53
	 * @var mail_account
54
	 */
55
	var $current_account;
56
57
	/**
58
	 * Edit folder ACLs of account(s)
59
	 *
60
	 * @param string $content = null
61
	 * @param array $msg = ''
62
	 *
63
	 */
64
	function edit(array $content=null ,$msg='')
65
	{
66
		$tmpl = new Etemplate('mail.acl');
67
		if (!is_array($content))
68
		{
69
			$acc_id = $_GET['acc_id']?$_GET['acc_id']:$GLOBALS['egw_info']['user']['preferences']['mail']['ActiveProfileID'];
70
			if (isset($_GET['account_id']) && !isset($GLOBALS['egw_info']['user']['apps']['admin']))
71
			{
72
				Framework::window_close(lang('Permission denied'));
73
			}
74
			$account_id = $_GET['account_id'];
75
		}
76
		else
77
		{
78
			$acc_id = $content['acc_id'];
79
			$account_id = $content['account_id'];
80
		}
81
		$account = Mail\Account::read($acc_id, $account_id);
82
		$this->imap = $account->imapServer(isset($account_id) ? (int)$account_id : false);
83
84
		$mailbox = $_GET['mailbox']? base64_decode($_GET['mailbox']):
85
			preg_replace("/^".$acc_id."::/",'',$content['mailbox'][0]);
86
		if (empty($mailbox))
87
		{
88
			$mailbox = $this->imap->isAdminConnection ? $this->imap->getUserMailboxString($account_id) : 'INBOX';
89
		}
90
		if (!$this->imap->isAdminConnection)
91
		{
92
			$tmpl->setElementAttribute('mailbox', 'autocomplete_url', 'mail.mail_compose.ajax_searchFolder');
93
			$tmpl->setElementAttribute('mailbox', 'autocomplete_params', array('mailaccount' => $acc_id));
94
		}
95
		else
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
96
		{
97
			//Todo: Implement autocomplete_url function with admin stuffs consideration
98
		}
99
		// Unset the content if folder is changed, in order to read acl rights for new selected folder
100
		if (!is_array($content['button']) && is_array($content['mailbox']) && !is_array($content['grid']['delete'])) unset($content);
101
102
		if (!is_array($content))
103
		{
104
			if (!empty($mailbox))
105
			{
106
				$content['mailbox'] = $mailbox;
107
				$acl = (array)$this->retrive_acl($mailbox, $msg);
108
				$n = 1;
109
				foreach ($acl as $key => $value)
110
				{
111
					$virtuals = array_pop(array_values((array)$value));
0 ignored issues
show
Bug introduced by
array_values((array) $value) cannot be passed to array_pop() as the parameter $array expects a reference.
Loading history...
112
					$rights = array_shift(array_values((array)$value));
0 ignored issues
show
Bug introduced by
array_values((array) $value) cannot be passed to array_shift() as the parameter $array expects a reference.
Loading history...
113
114
					foreach ($rights as $right)
115
					{
116
						$content['grid'][$n]['acl_'. $right] = true;
117
					}
118
					$virtualD = array('e','t');
119
					$content['grid'][$n]['acl_c'] = array_diff($virtuals['c'],array_intersect($rights,$virtuals['c']))? false: true; //c=kx more information rfc4314, Obsolote Rights
120
					$content['grid'][$n]['acl_d'] = array_diff($virtualD,array_intersect($rights,$virtuals['d']))? false: true; //d=et more information rfc4314, Obsolote Rights
121
122
					sort($rights);
123
					$acl_abbrvs = implode('',$rights);
124
125
					if (array_key_exists($acl_abbrvs, $this->aclRightsAbbrvs))
126
					{
127
						$content['grid'][$n]['acl'] = $acl_abbrvs;
128
					}
129
					else
130
					{
131
						$content['grid'][$n]['acl'] = 'custom';
132
					}
133
					if (($user = $this->imap->getMailBoxAccountId($key)))
134
					{
135
						$content['grid'][$n++]['acc_id'] = $user;
136
					}
137
					else
138
					{
139
						$content['grid'][$n++]['acc_id'] = $key;
140
					}
141
				}
142
				//error_log(__METHOD__."() acl=".array2string($acl).' --> grid='.array2string($content['grid']));
143
			}
144
			//Set the acl entry in the last row with lrs as default ACL
145
			array_push($content['grid'], array(
146
				'acc_id'=>'',
147
				'acl_l' => true,
148
				'acl_r' => true,
149
				'acl_s' => true));
150
		}
151
		else
152
		{
153
			list($button) = @each($content['button']);
154
			if (!empty ($content['grid']['delete']))
155
			{
156
				$button = 'delete';
157
			}
158
			switch ($button)
159
			{
160
				case 'save':
161
				case 'apply':
162
					if ($content)
163
					{
164
						$validation_err = $this->update_acl($content,$msg);
0 ignored issues
show
Bug introduced by
It seems like $msg defined by parameter $msg on line 64 can also be of type array; however, mail_acl::update_acl() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
165
						if ($validation_err)
166
						{
167
							foreach ($validation_err as &$row)
168
							{
169
								$tmpl->set_validation_error('grid['.$row.']'.'[acc_id]', "You must fill this field!");
170
							}
171
						}
172
173
						//Add new row at the end
174
						if ($content['grid'][count($content['grid'])]['acc_id'])
175
							array_push($content['grid'], array('acc_id'=>''));
176
					}
177
					else
178
					{
179
						$msg .= "\n".lang("Error: Could not save ACL").' '.lang("reason!");
180
					}
181
					//Send message
182
					Framework::message($msg);
183
					if ($button == "apply") break;
184
					Framework::window_close();
185
					exit;
186
187
				case 'delete':
188
					$aclRvmCnt = $this->remove_acl($content, $msg);
0 ignored issues
show
Bug introduced by
It seems like $msg defined by parameter $msg on line 64 can also be of type array; however, mail_acl::remove_acl() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
189
					if (is_array($aclRvmCnt))
190
					{
191
						$content['grid'] = $aclRvmCnt;
192
					}
193
					else
194
					{
195
						error_log(__METHOD__.__LINE__. "()" . "The remove_acl suppose to return an array back, something is wrong there");
196
					}
197
					Framework::message($msg);
198
			}
199
		}
200
		$readonlys = $sel_options = array();
201
		$sel_options['acl'] = $this->aclRightsAbbrvs;
202
203
		//Make the account owner's fields all readonly as owner has all rights and should not be able to change them
204
		foreach($content['grid'] as $key => $fields)
205
		{
206
			if (self::_extract_acc_id($fields['acc_id']) == $this->imap->acc_imap_username)
207
			{
208
				foreach (array_keys($fields) as $index)
209
				{
210
					$readonlys['grid'][$key][$index] = true;
211
				}
212
				$readonlys['grid']['delete['.$key.']'] = true;
213
				$readonlys['grid'][$key]['acl_recursive'] = true;
214
				$preserv ['grid'][$key] = $fields;
215
				$preserv['grid'][$key]['acl_recursive'] = false;
216
			}
217
			if (count($content['grid']) != $key)
218
			{
219
				$preserv ['grid'][$key]['acc_id'] = self::_extract_acc_id($fields['acc_id']);
220
				$preserv['grid'][$key]['acl_recursive'] = false;
221
				$readonlys['grid'][$key]['acc_id'] = true;
222
			}
223
		}
224
		//Make entry row's delete button readonly
225
		$readonlys['grid']['delete['.count($content['grid']).']'] = true;
226
227
		$preserv['mailbox'] = $content['mailbox'];
228
		$preserv['acc_id'] = $acc_id;
229
		$preserv['account_id'] = $account_id;
230
		$content['grid']['account_type'] = $this->imap->supportsGroupAcl() ? 'both' : 'accounts';
231
232
		// set a custom autocomplete method for mailbox taglist
233
		if ($account_id)
234
		{
235
			$tmpl->setElementAttribute('mailbox', 'autocomplete_url', __CLASS__.'::ajax_folders');
236
			$tmpl->setElementAttribute('mailbox', 'autocomplete_params', array(
237
				'acc_id' => $acc_id,
238
				'account_id' => $account_id,
239
			));
240
		}
241
242
		$tmpl->exec('mail.mail_acl.edit', $content, $sel_options, $readonlys, $preserv,2);
243
	}
244
245
	/**
246
	 * Autocomplete for folder taglist
247
	 *
248
	 * @throws Api\Exception\NoPermission\Admin
249
	 */
250
	public static function ajax_folders()
251
	{
252
		if (!empty($_GET['account_id']) && !$GLOBALS['egw_info']['user']['apps']['admin'])
253
		{
254
			throw new Api\Exception\NoPermission\Admin;
255
		}
256
		$account = Mail\Account::read($_GET['acc_id'], $_GET['account_id']);
257
		$imap = $account->imapServer(!empty($_GET['account_id']) ? (int)$_GET['account_id'] : false);
258
		$mailbox = $imap->isAdminConnection ? $imap->getUserMailboxString($imap->isAdminConnection) : 'INBOX';
259
260
		$folders = array();
261
		foreach(self::getSubfolders($mailbox, $imap) as $folder)
262
		{
263
			if (stripos($folder, $_GET['query']) !== false)
264
			{
265
				$folders[] = array(
266
					'id' => $folder,
267
					'label' => $folder,
268
				);
269
			}
270
		}
271
		// switch regular JSON response handling off
272
		Api\Json\Request::isJSONRequest(false);
273
274
		header('Content-Type: application/json; charset=utf-8');
275
		echo json_encode($folders);
276
277
		exit;
278
	}
279
280
	/**
281
	 * Update ACL rights of a folder or including subfolders for an account(s)
282
	 *
283
	 * @param array $content content including the acl rights
284
	 * @param Boolean $recursive boolean flag FALSE|TRUE. If it is FALSE, only the folder take in to account, but in case of TRUE
285
	 *		the mailbox including all its subfolders will be considered.
286
	 * @param string $msg Message
287
	 *
288
	 */
289
	function update_acl ($content, &$msg)
290
	{
291
		$validator = array();
292
293
		foreach ($content['grid'] as $keys => $value)
294
		{
295
			$recursive = $value['acl_recursive'];
296
			unset($value['acc_id']);
297
			unset($value['acl_recursive']);
298
			unset($value['acl']);
299
300
			$options = array();
301
			foreach (array_keys($value) as $key)
302
			{
303
				if ($value[$key] == true)
304
				{
305
					$right = explode("acl_" ,$key);
306
					if ($right[1] === 'c') $right[1] = 'kx'; // c = kx , rfc 4314
307
					if ($right[1] === 'd') $right[1] = 'et'; // d = et , rfc 4314
308
					$options['rights'] .=  $right[1];
309
				}
310
			}
311
			$username = self::_extract_acc_id($content['grid'][$keys]['acc_id']);
312
313
			//error_log(__METHOD__."(".__LINE__.") setACL($content[mailbox], $username, ".array2string($options).", $recursive)");
314
			if (is_numeric($username) && ($u = $this->imap->getMailBoxUserName($username)))
315
			{
316
				$username = $u;
317
			}
318
			if (!empty($username))
319
			{
320
				//error_log(__METHOD__."() setACL($content[mailbox], $username, ".array2string($options).", $recursive)");
321
				if (($ret=$this->setACL($content['mailbox'], $username, $options, $recursive, $msg)))
322
				{
323
					$msg = lang("The Folder %1 's ACLs saved", $content['mailbox']);
324
325
				}
326
				else
327
				{
328
					$msg = lang('Error while setting ACL for folder %1!', $content['mailbox']).' '.$msg;
329
				}
330
			}
331
			else
332
			{
333
				if($keys !== count($content['grid']))
334
				{
335
					array_push($validator, $keys);
336
					$msg = lang("Error:Could not save the ACL! Because some names are empty!");
337
				}
338
			}
339
		}
340
		if (is_array($validator))
341
		{
342
			return $validator;
343
		}
344
	}
345
346
	/**
347
	 * Retrive Folder ACL rights
348
	 * @todo rights 'c' and 'd' should be fixed
349
	 */
350
	function retrive_acl ($mailbox, &$msg)
351
	{
352
		if (($acl = $this->getACL($mailbox)))
353
		 {
354
			$msg = lang('ACL rights retrived successfully');
355
			return $acl;
356
		 }
357
		 else
358
		 {
359
			$msg = lang('Get ACL rights failed from IMAP server!');
360
		 }
361
	}
362
363
	/**
364
	 * remove_acl
365
	 * This method take content of acl rights, and will delete the one from ACL IMAP,
366
	 * for selected folder and/or its subfolders
367
	 *
368
	 * @param Array $content content array of popup window
369
	 * @param string $msg message
370
	 *
371
	 * @return Array An array as new content for grid
372
	 */
373
	function remove_acl($content, &$msg)
374
	{
375
		$row_num = array_keys($content['grid']['delete'],"pressed");
376
		if ($row_num) $row_num = $row_num[0];
377
		$recursive = $content['grid'][$row_num]['acl_recursive'];
378
		$identifier = self::_extract_acc_id($content['grid'][$row_num]['acc_id']);
379
		$content['mailbox'] = is_array($content['mailbox'])? $content['mailbox'][0] : $content['mailbox'];
380
		if (is_numeric($identifier) && ($u = $this->imap->getMailBoxUserName($identifier)))
381
		{
382
			$identifier = $u;
383
		}
384
		//error_log(__METHOD__.__LINE__."(".$content['mailbox'].", ".$identifier.", ".$recursive.")");
385
		if(($res = $this->deleteACL($content['mailbox'], $identifier,$recursive)))
386
		{
387
			unset($content['grid'][$row_num]);
388
			unset($content['grid']['delete']);
389
			if ($recursive)
390
			{
391
				$msg = lang("The %1 's acl, including its subfolders, removed from the %2",$content['mailbox'],$identifier);
392
			}
393
			else
394
			{
395
				$msg = lang("The %1 's acl removed from the %2",$content['mailbox'],$identifier);
396
			}
397
398
			return array_combine(range(1, count($content['grid'])), array_values($content['grid']));
399
		}
400
		else
401
		{
402
			$msg = lang("An error happend while trying to remove ACL rights from the account %1!",$identifier);
403
			return false;
404
		}
405
	}
406
407
	/**
408
	 * Delete ACL rights of a folder or including subfolders from an account
409
	 *
410
	 * @param String $mailbox folder name that needs to be edited
411
	 * @param String $identifier The identifier to delete.
412
	 * @param Boolean $recursive boolean flag FALSE|TRUE. If it is FALSE, only the folder take in to account, but in case of TRUE
413
	 *		the mailbox including all its subfolders will be considered.
414
	 *
415
	 * @return Boolean FALSE in case of any exceptions and TRUE in case of success
416
	 */
417
	function deleteACL ($mailbox, $identifier, $recursive)
418
	{
419
		if ($recursive)
420
		{
421
			$folders = self::getSubfolders($mailbox, $this->imap);
422
		}
423
		else
424
		{
425
			$folders = (array)$mailbox;
426
		}
427
		foreach($folders as $sbFolders)
428
		{
429
			try
430
			{
431
				$this->imap->deleteACL($sbFolders, $identifier);
432
			}
433
			catch (Exception $e)
434
			{
435
				error_log(__METHOD__. "Could not delete ACL rights of folder " . $mailbox . " for account ". $identifier ."." .$e->getMessage());
436
				return false;
437
			}
438
		}
439
		return true;
440
	}
441
442
	/**
443
	 * Get subfolders of a mailbox
444
	 *
445
	 * @param string $mailbox structural folder name
446
	 * @param Mail\Imap $imap
447
	 * @return Array an array including all subfolders of given mailbox| returns an empty array in case of no subfolders
448
	 */
449
	protected static function getSubfolders($mailbox, Mail\Imap $imap)
450
	{
451
		$delimiter = $imap->getDelimiter();
452
		$nameSpace = $imap->getNameSpace();
453
		$prefix = $imap->getFolderPrefixFromNamespace($nameSpace, $mailbox);
454
		if (($subFolders = $imap->getMailBoxesRecursive($mailbox, $delimiter, $prefix)))
455
		{
456
			return $subFolders;
457
		}
458
		else
459
		{
460
			return array();
461
		}
462
	}
463
464
	/**
465
	 * Set ACL rights of a folder or including subfolders to an account
466
	 * @param String $mailbox folder name that needs to be edited
467
	 * @param String $identifier The identifier to set.
468
	 * @param Array $options Additional options:
469
	 * 				- rights: (string) The rights to alter or set.
470
	 * 				- action: (string, optional) If 'add' or 'remove', adds or removes the
471
	 * 				specified rights. Sets the rights otherwise.
472
	 * @param Boolean $recursive boolean flag FALSE|TRUE. If it is FALSE, only the folder take in to account, but in case of TRUE
473
	 *		the mailbox including all its subfolders will be considered.
474
	 * @param String $msg message
475
	 * @return Boolean FALSE in case of any exceptions and TRUE in case of success,
476
	 *
477
	 */
478
	function setACL($mailbox, $identifier,$options, $recursive, &$msg)
479
	{
480
		if ($recursive)
481
		{
482
			$folders = self::getSubfolders($mailbox, $this->imap);
483
		}
484
		else
485
		{
486
			$folders = (array)$mailbox;
487
		}
488
		foreach($folders as $sbFolders)
489
		{
490
			try
491
			{
492
				$this->imap->setACL($sbFolders,$identifier,$options);
493
			}
494
			catch (Exception $e)
495
			{
496
				$msg = $e->getMessage();
497
				error_log(__METHOD__. "Could not set ACL rights on folder " . $mailbox . " for account ". $identifier . "." .$e->getMessage());
498
				return false;
499
			}
500
		}
501
		return true;
502
	}
503
504
	/**
505
	 * Get ACL rights of a folder from an account
506
	 *
507
	 * @param String $mailbox folder name that needs to be read
508
	 * @return Boolean FALSE in case of any exceptions and if TRUE in case of success,
509
	 */
510
	function getACL ($mailbox)
511
	{
512
		try
513
		{
514
			$acl = $this->imap->getACL($mailbox);
515
			return $acl;
516
		} catch (Exception $e) {
517
			error_log(__METHOD__. "Could not get ACL rights from folder " . $mailbox . "." .$e->getMessage());
518
			return false;
519
		}
520
	}
521
522
	/**
523
	 * Method to get acc_id id value whether if is a flat value or an array
524
	 *
525
	 * @param type $acc_id acc_id value comming from client-side
526
	 *
527
	 * @return string returns acc_id in flat format
528
	 */
529
	private static function _extract_acc_id ($acc_id)
530
	{
531
		return is_array($acc_id)?$acc_id[0]:$acc_id;
532
	}
533
}
534