1 | <?php |
||||
2 | /** |
||||
3 | * EGroupware EMailAdmin: Wizard to create mail accounts |
||||
4 | * |
||||
5 | * @link http://www.egroupware.org |
||||
6 | * @package emailadmin |
||||
7 | * @author Ralf Becker <[email protected]> |
||||
8 | * @copyright (c) 2013-18 by Ralf Becker <[email protected]> |
||||
9 | * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License |
||||
10 | */ |
||||
11 | |||||
12 | use EGroupware\Api; |
||||
13 | use EGroupware\Api\Framework; |
||||
14 | use EGroupware\Api\Acl; |
||||
15 | use EGroupware\Api\Etemplate; |
||||
16 | use EGroupware\Api\Mail; |
||||
0 ignored issues
–
show
|
|||||
17 | |||||
18 | /** |
||||
19 | * Wizard to create mail accounts |
||||
20 | * |
||||
21 | * Wizard uses follow heuristic to search for IMAP accounts: |
||||
22 | * 1. query Mozilla ISPDB for domain from email (perfering SSL over STARTTLS over insecure connection) |
||||
23 | * 2. guessing and verifying in DNS server-names based on domain from email: |
||||
24 | * - (imap|smtp).$domain, mail.$domain |
||||
25 | * - MX is *.mail.protection.outlook.com use (outlook|smtp).office365.com |
||||
26 | * - MX for $domain |
||||
27 | * - replace host in MX with (imap|smtp) or mail |
||||
28 | */ |
||||
29 | class admin_mail |
||||
30 | { |
||||
31 | /** |
||||
32 | * Enable logging of IMAP communication to given path, eg. /tmp/autoconfig.log |
||||
33 | */ |
||||
34 | const DEBUG_LOG = null; |
||||
35 | /** |
||||
36 | * Connection timeout in seconds used in autoconfig, can and should be really short! |
||||
37 | */ |
||||
38 | const TIMEOUT = 3; |
||||
39 | /** |
||||
40 | * Prefix for callback names |
||||
41 | * |
||||
42 | * Used as static::APP_CLASS in etemplate::exec(), to allow mail app extending this class. |
||||
43 | */ |
||||
44 | const APP_CLASS = 'admin.admin_mail.'; |
||||
45 | |||||
46 | /** |
||||
47 | * 0: No SSL |
||||
48 | */ |
||||
49 | const SSL_NONE = Mail\Account::SSL_NONE; |
||||
50 | /** |
||||
51 | * 1: STARTTLS on regular tcp connection/port |
||||
52 | */ |
||||
53 | const SSL_STARTTLS = Mail\Account::SSL_STARTTLS; |
||||
54 | /** |
||||
55 | * 3: SSL (inferior to TLS!) |
||||
56 | */ |
||||
57 | const SSL_SSL = Mail\Account::SSL_SSL; |
||||
58 | /** |
||||
59 | * 2: require TLS version 1+, no SSL version 2 or 3 |
||||
60 | */ |
||||
61 | const SSL_TLS = Mail\Account::SSL_TLS; |
||||
62 | /** |
||||
63 | * 8: if set, verify certifcate (currently not implemented in Horde_Imap_Client!) |
||||
64 | */ |
||||
65 | const SSL_VERIFY = Mail\Account::SSL_VERIFY; |
||||
66 | |||||
67 | /** |
||||
68 | * Log exception including trace to error-log, instead of just displaying the message. |
||||
69 | * |
||||
70 | * @var boolean |
||||
71 | */ |
||||
72 | public static $debug = false; |
||||
73 | |||||
74 | /** |
||||
75 | * Methods callable via menuaction |
||||
76 | * |
||||
77 | * @var array |
||||
78 | */ |
||||
79 | public $public_functions = array( |
||||
80 | 'add' => true, |
||||
81 | 'edit' => true, |
||||
82 | 'ajax_activeAccounts' => true |
||||
83 | ); |
||||
84 | |||||
85 | /** |
||||
86 | * Supported ssl types including none |
||||
87 | * |
||||
88 | * @var array |
||||
89 | */ |
||||
90 | public static $ssl_types = array( |
||||
91 | self::SSL_TLS => 'TLS', // SSL with minimum TLS (no SSL v.2 or v.3), requires Horde_Imap_Client-2.16.0/Horde_Socket_Client-1.1.0 |
||||
92 | self::SSL_SSL => 'SSL', |
||||
93 | self::SSL_STARTTLS => 'STARTTLS', |
||||
94 | 'no' => 'no', |
||||
95 | ); |
||||
96 | /** |
||||
97 | * Convert ssl-type to Horde secure parameter |
||||
98 | * |
||||
99 | * @var array |
||||
100 | */ |
||||
101 | public static $ssl2secure = array( |
||||
102 | 'SSL' => 'ssl', |
||||
103 | 'STARTTLS' => 'tls', |
||||
104 | 'TLS' => 'tlsv1', // SSL with minimum TLS (no SSL v.2 or v.3), requires Horde_Imap_Client-2.16.0/Horde_Socket_Client-1.1.0 |
||||
105 | ); |
||||
106 | /** |
||||
107 | * Convert ssl-type to eMailAdmin acc_(imap|sieve|smtp)_ssl integer value |
||||
108 | * |
||||
109 | * @var array |
||||
110 | */ |
||||
111 | public static $ssl2type = array( |
||||
112 | 'TLS' => self::SSL_TLS, |
||||
113 | 'SSL' => self::SSL_SSL, |
||||
114 | 'STARTTLS' => self::SSL_STARTTLS, |
||||
115 | 'no' => self::SSL_NONE, |
||||
116 | ); |
||||
117 | |||||
118 | /** |
||||
119 | * Available IMAP login types |
||||
120 | * |
||||
121 | * @var array |
||||
122 | */ |
||||
123 | public static $login_types = array( |
||||
124 | '' => 'Username specified below for all', |
||||
125 | 'standard' => 'username from account', |
||||
126 | 'vmailmgr' => 'username@domainname', |
||||
127 | //'admin' => 'Username/Password defined by admin', |
||||
128 | 'uidNumber' => 'UserId@domain eg. u1234@domain', |
||||
129 | 'email' => 'EMail-address from account', |
||||
130 | ); |
||||
131 | |||||
132 | /** |
||||
133 | * Options for further identities |
||||
134 | * |
||||
135 | * @var array |
||||
136 | */ |
||||
137 | public static $further_identities = array( |
||||
138 | 0 => 'Forbid users to create identities', |
||||
139 | 1 => 'Allow users to create further identities', |
||||
140 | 2 => 'Allow users to create identities for aliases', |
||||
141 | ); |
||||
142 | |||||
143 | /** |
||||
144 | * List of domains know to not support Sieve |
||||
145 | * |
||||
146 | * Used to switch Sieve off by default, thought users can allways try switching it on. |
||||
147 | * Testing not existing Sieve with google takes a long time, as ports are open, |
||||
148 | * but not answering ... |
||||
149 | * |
||||
150 | * @var array |
||||
151 | */ |
||||
152 | public static $no_sieve_blacklist = array('gmail.com', 'googlemail.com', 'outlook.office365.com'); |
||||
153 | |||||
154 | /** |
||||
155 | * Is current use a mail administrator / has run rights for EMailAdmin |
||||
156 | * |
||||
157 | * @var boolean |
||||
158 | */ |
||||
159 | protected $is_admin = false; |
||||
160 | |||||
161 | /** |
||||
162 | * Constructor |
||||
163 | */ |
||||
164 | public function __construct() |
||||
165 | { |
||||
166 | $this->is_admin = isset($GLOBALS['egw_info']['user']['apps']['admin']); |
||||
167 | |||||
168 | // for some reason most translation for account-wizard are in mail |
||||
169 | Api\Translation::add_app('mail'); |
||||
170 | |||||
171 | // Horde use locale for translation of error messages |
||||
172 | Api\Preferences::setlocale(LC_MESSAGES); |
||||
173 | } |
||||
174 | |||||
175 | /** |
||||
176 | * Step 1: IMAP account |
||||
177 | * |
||||
178 | * @param array $content |
||||
179 | * @param type $msg |
||||
180 | */ |
||||
181 | public function add(array $content=array(), $msg='', $msg_type='success') |
||||
182 | { |
||||
183 | $tpl = new Etemplate('admin.mailwizard'); |
||||
184 | if (empty($content['account_id'])) |
||||
185 | { |
||||
186 | $content['account_id'] = $GLOBALS['egw_info']['user']['account_id']; |
||||
187 | } |
||||
188 | // add some defaults if not already set (+= does not overwrite existing values!) |
||||
189 | $content += array( |
||||
190 | 'ident_realname' => $GLOBALS['egw']->accounts->id2name($content['account_id'], 'account_fullname'), |
||||
191 | 'ident_email' => $GLOBALS['egw']->accounts->id2name($content['account_id'], 'account_email'), |
||||
192 | 'acc_imap_port' => 993, |
||||
193 | 'manual_class' => 'emailadmin_manual', |
||||
194 | ); |
||||
195 | Framework::message($msg ? $msg : (string)$_GET['msg'], $msg_type); |
||||
196 | |||||
197 | if (!empty($content['acc_imap_host']) || !empty($content['acc_imap_username'])) |
||||
198 | { |
||||
199 | $readonlys['button[manual]'] = true; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
200 | unset($content['manual_class']); |
||||
201 | } |
||||
202 | $tpl->exec(static::APP_CLASS.'autoconfig', $content, array( |
||||
203 | 'acc_imap_ssl' => self::$ssl_types, |
||||
204 | ), $readonlys, $content, 2); |
||||
205 | } |
||||
206 | |||||
207 | /** |
||||
208 | * Try to autoconfig an account |
||||
209 | * |
||||
210 | * @param array $content |
||||
211 | */ |
||||
212 | public function autoconfig(array $content) |
||||
213 | { |
||||
214 | // user pressed [Skip IMAP] --> jump to SMTP config |
||||
215 | if ($content['button'] && key($content['button']) == 'skip_imap') |
||||
216 | { |
||||
217 | unset($content['button']); |
||||
218 | if (!isset($content['acc_smtp_host'])) $content['acc_smtp_host'] = ''; // do manual mode right away |
||||
219 | return $this->smtp($content, lang('Skipping IMAP configuration!')); |
||||
220 | } |
||||
221 | $content['output'] = ''; |
||||
222 | $sel_options = $readonlys = array(); |
||||
223 | |||||
224 | $content['connected'] = $connected = false; |
||||
225 | if (empty($content['acc_imap_username'])) |
||||
226 | { |
||||
227 | $content['acc_imap_username'] = $content['ident_email']; |
||||
228 | } |
||||
229 | if (!empty($content['acc_imap_host'])) |
||||
230 | { |
||||
231 | $hosts = array($content['acc_imap_host'] => true); |
||||
232 | if ($content['acc_imap_port'] > 0 && !in_array($content['acc_imap_port'], array(143,993))) |
||||
233 | { |
||||
234 | $ssl_type = (string)array_search($content['acc_imap_ssl'], self::$ssl2type); |
||||
235 | if ($ssl_type === '') $ssl_type = 'insecure'; |
||||
236 | $hosts[$content['acc_imap_host']] = array( |
||||
237 | $ssl_type => $content['acc_imap_port'], |
||||
238 | ); |
||||
239 | } |
||||
240 | } |
||||
241 | elseif (($ispdb = self::mozilla_ispdb($content['ident_email'])) && count($ispdb['imap'])) |
||||
242 | { |
||||
243 | $content['ispdb'] = $ispdb; |
||||
244 | $content['output'] .= lang('Using data from Mozilla ISPDB for provider %1', $ispdb['displayName'])."\n"; |
||||
0 ignored issues
–
show
The call to
lang() has too many arguments starting with $ispdb['displayName'] .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||
245 | $hosts = array(); |
||||
246 | foreach($ispdb['imap'] as $server) |
||||
0 ignored issues
–
show
|
|||||
247 | { |
||||
248 | if (!isset($hosts[$server['hostname']])) |
||||
249 | { |
||||
250 | $hosts[$server['hostname']] = array('username' => $server['username']); |
||||
251 | } |
||||
252 | if (strtoupper($server['socketType']) == 'SSL') // try TLS first |
||||
253 | { |
||||
254 | $hosts[$server['hostname']]['TLS'] = $server['port']; |
||||
255 | } |
||||
256 | $hosts[$server['hostname']][strtoupper($server['socketType'])] = $server['port']; |
||||
257 | // make sure we prefer SSL over STARTTLS over insecure |
||||
258 | if (count($hosts[$server['hostname']]) > 2) |
||||
259 | { |
||||
260 | $hosts[$server['hostname']] = self::fix_ssl_order($hosts[$server['hostname']]); |
||||
261 | } |
||||
262 | } |
||||
263 | } |
||||
264 | else |
||||
265 | { |
||||
266 | $hosts = $this->guess_hosts($content['ident_email'], 'imap'); |
||||
267 | } |
||||
268 | |||||
269 | // iterate over all hosts and try to connect |
||||
270 | foreach($hosts as $host => $data) |
||||
271 | { |
||||
272 | $content['acc_imap_host'] = $host; |
||||
273 | // by default we check SSL, STARTTLS and at last an insecure connection |
||||
274 | if (!is_array($data)) $data = array('TLS' => 993, 'SSL' => 993, 'STARTTLS' => 143, 'insecure' => 143); |
||||
275 | |||||
276 | foreach($data as $ssl => $port) |
||||
277 | { |
||||
278 | if ($ssl === 'username') continue; |
||||
279 | |||||
280 | $content['acc_imap_ssl'] = (int)self::$ssl2type[$ssl]; |
||||
281 | |||||
282 | $e = null; |
||||
283 | try { |
||||
284 | $content['output'] .= "\n".Api\DateTime::to('now', 'H:i:s').": Trying $ssl connection to $host:$port ...\n"; |
||||
285 | $content['acc_imap_port'] = $port; |
||||
286 | |||||
287 | $imap = self::imap_client($content, self::TIMEOUT); |
||||
288 | |||||
289 | //$content['output'] .= array2string($imap->capability()); |
||||
290 | $imap->login(); |
||||
291 | $content['output'] .= "\n".lang('Successful connected to %1 server%2.', 'IMAP', ' '.lang('and logged in'))."\n"; |
||||
292 | if (!$imap->isSecureConnection()) |
||||
293 | { |
||||
294 | $content['output'] .= lang('Connection is NOT secure! Everyone can read eg. your credentials.')."\n"; |
||||
295 | $content['acc_imap_ssl'] = 'no'; |
||||
296 | } |
||||
297 | //$content['output'] .= "\n\n".array2string($imap->capability()); |
||||
298 | $content['connected'] = $connected = true; |
||||
299 | break 2; |
||||
300 | } |
||||
301 | catch(Horde_Imap_Client_Exception $e) |
||||
302 | { |
||||
303 | switch($e->getCode()) |
||||
304 | { |
||||
305 | case Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED: |
||||
306 | $content['output'] .= "\n".$e->getMessage()."\n"; |
||||
307 | break 3; // no need to try other SSL or non-SSL connections, if auth failed |
||||
308 | |||||
309 | case Horde_Imap_Client_Exception::SERVER_CONNECT: |
||||
310 | $content['output'] .= "\n".$e->getMessage()."\n"; |
||||
311 | if ($ssl == 'STARTTLS') break 2; // no need to try insecure connection on same port |
||||
312 | break; |
||||
313 | |||||
314 | default: |
||||
315 | $content['output'] .= "\n".get_class($e).': '.$e->getMessage().' ('.$e->getCode().')'."\n"; |
||||
316 | //$content['output'] .= $e->getTraceAsString()."\n"; |
||||
317 | } |
||||
318 | if (self::$debug) _egw_log_exception($e); |
||||
319 | } |
||||
320 | catch(Exception $e) { |
||||
321 | $content['output'] .= "\n".get_class($e).': '.$e->getMessage().' ('.$e->getCode().')'."\n"; |
||||
322 | //$content['output'] .= $e->getTraceAsString()."\n"; |
||||
323 | if (self::$debug) _egw_log_exception($e); |
||||
324 | } |
||||
325 | } |
||||
326 | } |
||||
327 | if ($connected) // continue with next wizard step: define folders |
||||
328 | { |
||||
329 | unset($content['button']); |
||||
330 | return $this->folder($content, lang('Successful connected to %1 server%2.', 'IMAP', ' '.lang('and logged in')). |
||||
331 | ($imap->isSecureConnection() ? '' : "\n".lang('Connection is NOT secure! Everyone can read eg. your credentials.'))); |
||||
332 | } |
||||
333 | // add validation error, if we can identify a field |
||||
334 | if (!$connected && $e instanceof Horde_Imap_Client_Exception) |
||||
0 ignored issues
–
show
|
|||||
335 | { |
||||
336 | switch($e->getCode()) |
||||
337 | { |
||||
338 | case Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED: |
||||
339 | Etemplate::set_validation_error('acc_imap_username', lang($e->getMessage())); |
||||
340 | Etemplate::set_validation_error('acc_imap_password', lang($e->getMessage())); |
||||
341 | break; |
||||
342 | |||||
343 | case Horde_Imap_Client_Exception::SERVER_CONNECT: |
||||
344 | Etemplate::set_validation_error('acc_imap_host', lang($e->getMessage())); |
||||
345 | break; |
||||
346 | } |
||||
347 | } |
||||
348 | $readonlys['button[manual]'] = true; |
||||
349 | unset($content['manual_class']); |
||||
350 | $sel_options['acc_imap_ssl'] = self::$ssl_types; |
||||
351 | $tpl = new Etemplate('admin.mailwizard'); |
||||
352 | $tpl->exec(static::APP_CLASS.'autoconfig', $content, $sel_options, $readonlys, $content, 2); |
||||
353 | } |
||||
354 | |||||
355 | /** |
||||
356 | * Step 2: Folder - let user select trash, sent, drafs and template folder |
||||
357 | * |
||||
358 | * @param array $content |
||||
359 | * @param string $msg ='' |
||||
360 | * @param Horde_Imap_Client_Socket $imap =null |
||||
361 | */ |
||||
362 | public function folder(array $content, $msg='', Horde_Imap_Client_Socket $imap=null) |
||||
363 | { |
||||
364 | if (isset($content['button'])) |
||||
365 | { |
||||
366 | $button = key($content['button']); |
||||
367 | unset($content['button']); |
||||
368 | switch($button) |
||||
369 | { |
||||
370 | case 'back': |
||||
371 | return $this->add($content); |
||||
0 ignored issues
–
show
Are you sure the usage of
$this->add($content) targeting admin_mail::add() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||||
372 | |||||
373 | case 'continue': |
||||
374 | return $this->sieve($content); |
||||
375 | } |
||||
376 | } |
||||
377 | $content['msg'] = $msg; |
||||
378 | if (!isset($imap)) $imap = self::imap_client ($content); |
||||
379 | |||||
380 | try { |
||||
381 | //_debug_array($content); |
||||
382 | $sel_options['acc_folder_sent'] = $sel_options['acc_folder_trash'] = |
||||
383 | $sel_options['acc_folder_draft'] = $sel_options['acc_folder_template'] = |
||||
384 | $sel_options['acc_folder_junk'] = $sel_options['acc_folder_archive'] = |
||||
385 | $sel_options['acc_folder_ham'] = self::mailboxes($imap, $content); |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
386 | } |
||||
387 | catch(Exception $e) { |
||||
388 | $content['msg'] = $e->getMessage(); |
||||
389 | if (self::$debug) _egw_log_exception($e); |
||||
390 | } |
||||
391 | |||||
392 | $tpl = new Etemplate('admin.mailwizard.folder'); |
||||
393 | $tpl->exec(static::APP_CLASS.'folder', $content, $sel_options, array(), $content); |
||||
394 | } |
||||
395 | |||||
396 | /** |
||||
397 | * Query mailboxes and (optional) detect special folders |
||||
398 | * |
||||
399 | * @param Horde_Imap_Client_Socket $imap |
||||
400 | * @param array &$content=null on return values for acc_folder_(sent|trash|draft|template) |
||||
401 | * @return array with folders as key AND value |
||||
402 | * @throws Horde_Imap_Client_Exception |
||||
403 | */ |
||||
404 | public static function mailboxes(Horde_Imap_Client_Socket $imap, array &$content=null) |
||||
405 | { |
||||
406 | // query all subscribed mailboxes |
||||
407 | $mailboxes = $imap->listMailboxes('*', Horde_Imap_Client::MBOX_SUBSCRIBED, array( |
||||
408 | 'special_use' => true, |
||||
409 | 'attributes' => true, // otherwise special_use is only queried, but not returned ;-) |
||||
410 | 'delimiter' => true, |
||||
411 | )); |
||||
412 | //_debug_array($mailboxes); |
||||
413 | // list mailboxes by special-use attributes |
||||
414 | $folders = $attributes = $all = array(); |
||||
0 ignored issues
–
show
|
|||||
415 | foreach($mailboxes as $mailbox => $data) |
||||
416 | { |
||||
417 | foreach($data['attributes'] as $attribute) |
||||
418 | { |
||||
419 | $attributes[$attribute][] = $mailbox; |
||||
420 | } |
||||
421 | $folders[$mailbox] = $mailbox.': '.implode(', ', $data['attributes']); |
||||
422 | } |
||||
423 | // pre-select send, trash, ... folder for user, by checking special-use attributes or common name(s) |
||||
424 | foreach(array( |
||||
425 | 'acc_folder_sent' => array('\\sent', 'sent'), |
||||
426 | 'acc_folder_trash' => array('\\trash', 'trash'), |
||||
427 | 'acc_folder_draft' => array('\\drafts', 'drafts'), |
||||
428 | 'acc_folder_template' => array('', 'templates'), |
||||
429 | 'acc_folder_junk' => array('\\junk', 'junk', 'spam'), |
||||
430 | 'acc_folder_ham' => array('', 'ham'), |
||||
431 | 'acc_folder_archive' => array('', 'archive'), |
||||
432 | ) as $name => $common_names) |
||||
433 | { |
||||
434 | // first check special-use attributes |
||||
435 | if (($special_use = array_shift($common_names))) |
||||
436 | { |
||||
437 | foreach((array)$attributes[$special_use] as $mailbox) |
||||
438 | { |
||||
439 | if (empty($content[$name]) || strlen($mailbox) < strlen($content[$name])) |
||||
440 | { |
||||
441 | $content[$name] = $mailbox; |
||||
442 | } |
||||
443 | } |
||||
444 | } |
||||
445 | // no special use folder found, try common names |
||||
446 | if (empty($content[$name])) |
||||
447 | { |
||||
448 | foreach($mailboxes as $mailbox => $data) |
||||
449 | { |
||||
450 | $delimiter = !empty($data['delimiter']) ? $data['delimiter'] : '.'; |
||||
451 | $name_parts = explode($delimiter, strtolower($mailbox)); |
||||
452 | if (array_intersect($name_parts, $common_names) && |
||||
453 | (empty($content[$name]) || strlen($mailbox) < strlen($content[$name]) && substr($content[$name], 0, 6) != 'INBOX'.$delimiter)) |
||||
454 | { |
||||
455 | //error_log(__METHOD__."() $mailbox --> ".substr($name, 11).' folder'); |
||||
456 | $content[$name] = $mailbox; |
||||
457 | } |
||||
458 | //else error_log(__METHOD__."() $mailbox does NOT match array_intersect(".array2string($name_parts).', '.array2string($common_names).')='.array2string(array_intersect($name_parts, $common_names))); |
||||
459 | } |
||||
460 | } |
||||
461 | $folders[(string)$content[$name]] .= ' --> '.substr($name, 11).' folder'; |
||||
462 | } |
||||
463 | // uncomment for infos about selection process |
||||
464 | //$content['folder_output'] = implode("\n", $folders); |
||||
465 | |||||
466 | return array_combine(array_keys($mailboxes), array_keys($mailboxes)); |
||||
467 | } |
||||
468 | |||||
469 | /** |
||||
470 | * Step 3: Sieve |
||||
471 | * |
||||
472 | * @param array $content |
||||
473 | * @param string $msg ='' |
||||
474 | */ |
||||
475 | public function sieve(array $content, $msg='') |
||||
476 | { |
||||
477 | static $sieve_ssl2port = array( |
||||
478 | self::SSL_TLS => 5190, |
||||
479 | self::SSL_SSL => 5190, |
||||
480 | self::SSL_STARTTLS => array(4190, 2000), |
||||
481 | self::SSL_NONE => array(4190, 2000), |
||||
482 | ); |
||||
483 | $content['msg'] = $msg; |
||||
484 | |||||
485 | if (isset($content['button'])) |
||||
486 | { |
||||
487 | $button = key($content['button']); |
||||
488 | unset($content['button']); |
||||
489 | switch($button) |
||||
490 | { |
||||
491 | case 'back': |
||||
492 | return $this->folder($content); |
||||
493 | |||||
494 | case 'continue': |
||||
495 | if (!$content['acc_sieve_enabled']) |
||||
496 | { |
||||
497 | return $this->smtp($content); |
||||
498 | } |
||||
499 | break; |
||||
500 | } |
||||
501 | } |
||||
502 | // first try: hide manual config |
||||
503 | if (!isset($content['acc_sieve_enabled'])) |
||||
504 | { |
||||
505 | list(, $domain) = explode('@', $content['acc_imap_username']); |
||||
506 | $content['acc_sieve_enabled'] = (int)!in_array($domain, self::$no_sieve_blacklist); |
||||
507 | $content['manual_class'] = 'emailadmin_manual'; |
||||
508 | } |
||||
509 | else |
||||
510 | { |
||||
511 | unset($content['manual_class']); |
||||
512 | $readonlys['button[manual]'] = true; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
513 | } |
||||
514 | // set default ssl and port |
||||
515 | if (!isset($content['acc_sieve_ssl'])) $content['acc_sieve_ssl'] = key(self::$ssl_types); |
||||
516 | if (empty($content['acc_sieve_port'])) $content['acc_sieve_port'] = $sieve_ssl2port[$content['acc_sieve_ssl']]; |
||||
517 | |||||
518 | // check smtp connection |
||||
519 | if ($button == 'continue') |
||||
520 | { |
||||
521 | $content['sieve_connected'] = false; |
||||
522 | $content['sieve_output'] = ''; |
||||
523 | unset($content['manual_class']); |
||||
524 | |||||
525 | if (empty($content['acc_sieve_host'])) |
||||
526 | { |
||||
527 | $content['acc_sieve_host'] = $content['acc_imap_host']; |
||||
528 | } |
||||
529 | // if use set non-standard port, use it |
||||
530 | if (!in_array($content['acc_sieve_port'], (array)$sieve_ssl2port[$content['acc_sieve_ssl']])) |
||||
531 | { |
||||
532 | $data = array($content['acc_sieve_ssl'] => $content['acc_sieve_port']); |
||||
533 | } |
||||
534 | else // otherwise try all standard ports |
||||
535 | { |
||||
536 | $data = $sieve_ssl2port; |
||||
537 | } |
||||
538 | foreach($data as $ssl => $ports) |
||||
539 | { |
||||
540 | foreach((array)$ports as $port) |
||||
541 | { |
||||
542 | $content['acc_sieve_ssl'] = $ssl; |
||||
543 | $ssl_label = self::$ssl_types[$ssl]; |
||||
544 | |||||
545 | $e = null; |
||||
0 ignored issues
–
show
|
|||||
546 | try { |
||||
547 | $content['sieve_output'] .= "\n".Api\DateTime::to('now', 'H:i:s').": Trying $ssl_label connection to $content[acc_sieve_host]:$port ...\n"; |
||||
548 | $content['acc_sieve_port'] = $port; |
||||
549 | $sieve = new Horde\ManageSieve(array( |
||||
550 | 'host' => $content['acc_sieve_host'], |
||||
551 | 'port' => $content['acc_sieve_port'], |
||||
552 | 'secure' => self::$ssl2secure[(string)array_search($content['acc_sieve_ssl'], self::$ssl2type)], |
||||
553 | 'timeout' => self::TIMEOUT, |
||||
554 | 'logger' => self::DEBUG_LOG ? new admin_mail_logger(self::DEBUG_LOG) : null, |
||||
555 | )); |
||||
556 | // connect to sieve server |
||||
557 | $sieve->connect(); |
||||
558 | $content['sieve_output'] .= "\n".lang('Successful connected to %1 server%2.', 'Sieve',''); |
||||
0 ignored issues
–
show
The call to
lang() has too many arguments starting with 'Sieve' .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||
559 | // and log in |
||||
560 | $sieve->login($content['acc_imap_username'], $content['acc_imap_password']); |
||||
561 | $content['sieve_output'] .= ' '.lang('and logged in')."\n"; |
||||
562 | $content['sieve_connected'] = true; |
||||
563 | |||||
564 | unset($content['button']); |
||||
565 | return $this->smtp($content, lang('Successful connected to %1 server%2.', 'Sieve', |
||||
566 | ' '.lang('and logged in'))); |
||||
567 | } |
||||
568 | catch(Horde\ManageSieve\Exception\ConnectionFailed $e) { |
||||
569 | $content['sieve_output'] .= "\n".$e->getMessage().' '.$e->details."\n"; |
||||
570 | } |
||||
571 | catch(Exception $e) { |
||||
572 | $content['sieve_output'] .= "\n".get_class($e).': '.$e->getMessage(). |
||||
573 | ($e->details ? ' '.$e->details : '').' ('.$e->getCode().')'."\n"; |
||||
574 | $content['sieve_output'] .= $e->getTraceAsString()."\n"; |
||||
575 | if (self::$debug) _egw_log_exception($e); |
||||
576 | } |
||||
577 | } |
||||
578 | } |
||||
579 | // not connected, and default ssl/port --> reset again to secure settings |
||||
580 | if ($data == $sieve_ssl2port) |
||||
581 | { |
||||
582 | $content['acc_sieve_ssl'] = key(self::$ssl_types); |
||||
583 | $content['acc_sieve_port'] = $sieve_ssl2port[$content['acc_sieve_ssl']]; |
||||
584 | } |
||||
585 | } |
||||
586 | // add validation error, if we can identify a field |
||||
587 | if (!$content['sieve_connected'] && $e instanceof Exception) |
||||
588 | { |
||||
589 | switch($e->getCode()) |
||||
590 | { |
||||
591 | case 61: // connection refused |
||||
592 | case 60: // connection timed out (imap.googlemail.com returns that for none-ssl/4190/2000) |
||||
593 | case 65: // no route ot host (imap.googlemail.com returns that for ssl/5190) |
||||
594 | Etemplate::set_validation_error('acc_sieve_host', lang($e->getMessage())); |
||||
595 | Etemplate::set_validation_error('acc_sieve_port', lang($e->getMessage())); |
||||
596 | break; |
||||
597 | } |
||||
598 | $content['msg'] = lang('No sieve support detected, either fix configuration manually or leave it switched off.'); |
||||
599 | $content['acc_sieve_enabled'] = 0; |
||||
600 | } |
||||
601 | $sel_options['acc_sieve_ssl'] = self::$ssl_types; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
602 | $tpl = new Etemplate('admin.mailwizard.sieve'); |
||||
603 | $tpl->exec(static::APP_CLASS.'sieve', $content, $sel_options, $readonlys, $content, 2); |
||||
604 | } |
||||
605 | |||||
606 | /** |
||||
607 | * Step 4: SMTP |
||||
608 | * |
||||
609 | * @param array $content |
||||
610 | * @param string $msg ='' |
||||
611 | */ |
||||
612 | public function smtp(array $content, $msg='') |
||||
613 | { |
||||
614 | static $smtp_ssl2port = array( |
||||
615 | self::SSL_NONE => 25, |
||||
616 | self::SSL_SSL => 465, |
||||
617 | self::SSL_TLS => 465, |
||||
618 | self::SSL_STARTTLS => 587, |
||||
619 | ); |
||||
620 | $content['msg'] = $msg; |
||||
621 | |||||
622 | if (isset($content['button'])) |
||||
623 | { |
||||
624 | $button = key($content['button']); |
||||
625 | unset($content['button']); |
||||
626 | switch($button) |
||||
627 | { |
||||
628 | case 'back': |
||||
629 | return $this->sieve($content); |
||||
630 | } |
||||
631 | } |
||||
632 | // first try: hide manual config |
||||
633 | if (!isset($content['acc_smtp_host'])) |
||||
634 | { |
||||
635 | $content['manual_class'] = 'emailadmin_manual'; |
||||
636 | } |
||||
637 | else |
||||
638 | { |
||||
639 | unset($content['manual_class']); |
||||
640 | $readonlys['button[manual]'] = true; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
641 | } |
||||
642 | // copy username/password from imap |
||||
643 | if (!isset($content['acc_smtp_username'])) $content['acc_smtp_username'] = $content['acc_imap_username']; |
||||
644 | if (!isset($content['acc_smtp_password'])) $content['acc_smtp_password'] = $content['acc_imap_password']; |
||||
645 | // set default ssl |
||||
646 | if (!isset($content['acc_smtp_ssl'])) $content['acc_smtp_ssl'] = key(self::$ssl_types); |
||||
647 | if (empty($content['acc_smtp_port'])) $content['acc_smtp_port'] = $smtp_ssl2port[$content['acc_smtp_ssl']]; |
||||
648 | |||||
649 | // check smtp connection |
||||
650 | if ($button == 'continue') |
||||
651 | { |
||||
652 | $content['smtp_connected'] = false; |
||||
653 | $content['smtp_output'] = ''; |
||||
654 | unset($content['manual_class']); |
||||
655 | |||||
656 | if (!empty($content['acc_smtp_host'])) |
||||
657 | { |
||||
658 | $hosts = array($content['acc_smtp_host'] => true); |
||||
659 | if ((string)$content['acc_smtp_ssl'] !== (string)self::SSL_TLS || $content['acc_smtp_port'] != $smtp_ssl2port[$content['acc_smtp_ssl']]) |
||||
660 | { |
||||
661 | $ssl_type = (string)array_search($content['acc_smtp_ssl'], self::$ssl2type); |
||||
662 | $hosts[$content['acc_smtp_host']] = array( |
||||
663 | $ssl_type => $content['acc_smtp_port'], |
||||
664 | ); |
||||
665 | } |
||||
666 | } |
||||
667 | elseif($content['ispdb'] && !empty($content['ispdb']['smtp'])) |
||||
668 | { |
||||
669 | $content['smtp_output'] .= lang('Using data from Mozilla ISPDB for provider %1', $content['ispdb']['displayName'])."\n"; |
||||
0 ignored issues
–
show
The call to
lang() has too many arguments starting with $content['ispdb']['displayName'] .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||
670 | $hosts = array(); |
||||
671 | foreach($content['ispdb']['smtp'] as $server) |
||||
672 | { |
||||
673 | if (!isset($hosts[$server['hostname']])) |
||||
674 | { |
||||
675 | $hosts[$server['hostname']] = array('username' => $server['username']); |
||||
676 | } |
||||
677 | if (strtoupper($server['socketType']) == 'SSL') // try TLS first |
||||
678 | { |
||||
679 | $hosts[$server['hostname']]['TLS'] = $server['port']; |
||||
680 | } |
||||
681 | $hosts[$server['hostname']][strtoupper($server['socketType'])] = $server['port']; |
||||
682 | // make sure we prefer SSL over STARTTLS over insecure |
||||
683 | if (count($hosts[$server['hostname']]) > 2) |
||||
684 | { |
||||
685 | $hosts[$server['hostname']] = self::fix_ssl_order($hosts[$server['hostname']]); |
||||
686 | } |
||||
687 | } |
||||
688 | } |
||||
689 | else |
||||
690 | { |
||||
691 | $hosts = $this->guess_hosts($content['ident_email'], 'smtp'); |
||||
692 | } |
||||
693 | foreach($hosts as $host => $data) |
||||
694 | { |
||||
695 | $content['acc_smtp_host'] = $host; |
||||
696 | if (!is_array($data)) |
||||
697 | { |
||||
698 | $data = array('TLS' => 465, 'SSL' => 465, 'STARTTLS' => 587, '' => 25); |
||||
699 | } |
||||
700 | foreach($data as $ssl => $port) |
||||
701 | { |
||||
702 | if ($ssl === 'username') continue; |
||||
703 | |||||
704 | $content['acc_smtp_ssl'] = (int)self::$ssl2type[$ssl]; |
||||
705 | |||||
706 | $e = null; |
||||
0 ignored issues
–
show
|
|||||
707 | try { |
||||
708 | $content['smtp_output'] .= "\n".Api\DateTime::to('now', 'H:i:s').": Trying $ssl connection to $host:$port ...\n"; |
||||
709 | $content['acc_smtp_port'] = $port; |
||||
710 | |||||
711 | $mail = new Horde_Mail_Transport_Smtphorde($params=array( |
||||
712 | 'username' => $content['acc_smtp_username'], |
||||
713 | 'password' => $content['acc_smtp_password'], |
||||
714 | 'host' => $content['acc_smtp_host'], |
||||
715 | 'port' => $content['acc_smtp_port'], |
||||
716 | 'secure' => self::$ssl2secure[(string)array_search($content['acc_smtp_ssl'], self::$ssl2type)], |
||||
717 | 'timeout' => self::TIMEOUT, |
||||
718 | 'debug' => self::DEBUG_LOG, |
||||
719 | )); |
||||
720 | // create smtp connection and authenticate, if credentials given |
||||
721 | $smtp = $mail->getSMTPObject(); |
||||
722 | $content['smtp_output'] .= "\n".lang('Successful connected to %1 server%2.', 'SMTP', |
||||
723 | (!empty($content['acc_smtp_username']) ? ' '.lang('and logged in') : ''))."\n"; |
||||
724 | if (!$smtp->isSecureConnection()) |
||||
725 | { |
||||
726 | if (!empty($content['acc_smtp_username'])) |
||||
727 | { |
||||
728 | $content['smtp_output'] .= lang('Connection is NOT secure! Everyone can read eg. your credentials.')."\n"; |
||||
729 | } |
||||
730 | $content['acc_smtp_ssl'] = 'no'; |
||||
731 | } |
||||
732 | // Horde_Smtp always try to use STARTTLS, adjust our ssl-parameter if successful |
||||
733 | elseif (!($content['acc_smtp_ssl'] > self::SSL_NONE)) |
||||
734 | { |
||||
735 | //error_log(__METHOD__."() new Horde_Mail_Transport_Smtphorde(".array2string($params).")->getSMTPObject()->isSecureConnection()=".array2string($smtp->isSecureConnection())); |
||||
736 | $content['acc_smtp_ssl'] = self::SSL_STARTTLS; |
||||
737 | } |
||||
738 | // try sending a mail to a different domain, if not authenticated, to see if that's required |
||||
739 | if (empty($content['acc_smtp_username'])) |
||||
740 | { |
||||
741 | $smtp->send($content['ident_email'], '[email protected]', ''); |
||||
742 | $content['smtp_output'] .= "\n".lang('Relay access checked')."\n"; |
||||
743 | } |
||||
744 | $content['smtp_connected'] = true; |
||||
745 | unset($content['button']); |
||||
746 | return $this->edit($content, lang('Successful connected to %1 server%2.', 'SMTP', |
||||
747 | empty($content['acc_smtp_username']) ? ' - '.lang('Relay access checked') : ' '.lang('and logged in'))); |
||||
748 | } |
||||
749 | // unfortunately LOGIN_AUTHENTICATIONFAILED and SERVER_CONNECT are thrown as Horde_Mail_Exception |
||||
750 | // while others are thrown as Horde_Smtp_Exception --> using common base Horde_Exception_Wrapped |
||||
751 | catch(Horde_Exception_Wrapped $e) |
||||
752 | { |
||||
753 | switch($e->getCode()) |
||||
754 | { |
||||
755 | case Horde_Smtp_Exception::LOGIN_AUTHENTICATIONFAILED: |
||||
756 | case Horde_Smtp_Exception::LOGIN_REQUIREAUTHENTICATION: |
||||
757 | case Horde_Smtp_Exception::UNSPECIFIED: |
||||
758 | $content['smtp_output'] .= "\n".$e->getMessage()."\n"; |
||||
759 | break; |
||||
760 | case Horde_Smtp_Exception::SERVER_CONNECT: |
||||
761 | $content['smtp_output'] .= "\n".$e->getMessage()."\n"; |
||||
762 | break; |
||||
763 | default: |
||||
764 | $content['smtp_output'] .= "\n".$e->getMessage().' ('.$e->getCode().')'."\n"; |
||||
765 | break; |
||||
766 | } |
||||
767 | if (self::$debug) _egw_log_exception($e); |
||||
768 | } |
||||
769 | catch(Horde_Smtp_Exception $e) |
||||
770 | { |
||||
771 | // prever $e->details over $e->getMessage() as it contains original message from SMTP server (eg. relay access denied) |
||||
772 | $content['smtp_output'] .= "\n".(empty($e->details) ? $e->getMessage().' ('.$e->getCode().')' : $e->details)."\n"; |
||||
773 | //$content['smtp_output'] .= $e->getTraceAsString()."\n"; |
||||
774 | if (self::$debug) _egw_log_exception($e); |
||||
775 | } |
||||
776 | catch(Exception $e) { |
||||
777 | $content['smtp_output'] .= "\n".get_class($e).': '.$e->getMessage().' ('.$e->getCode().')'."\n"; |
||||
778 | //$content['smtp_output'] .= $e->getTraceAsString()."\n"; |
||||
779 | if (self::$debug) _egw_log_exception($e); |
||||
780 | } |
||||
781 | } |
||||
782 | } |
||||
783 | } |
||||
784 | // add validation error, if we can identify a field |
||||
785 | if (!$content['smtp_connected'] && $e instanceof Horde_Exception_Wrapped) |
||||
786 | { |
||||
787 | switch($e->getCode()) |
||||
788 | { |
||||
789 | case Horde_Smtp_Exception::LOGIN_AUTHENTICATIONFAILED: |
||||
790 | case Horde_Smtp_Exception::LOGIN_REQUIREAUTHENTICATION: |
||||
791 | case Horde_Smtp_Exception::UNSPECIFIED: |
||||
792 | Etemplate::set_validation_error('acc_smtp_username', lang($e->getMessage())); |
||||
793 | Etemplate::set_validation_error('acc_smtp_password', lang($e->getMessage())); |
||||
794 | break; |
||||
795 | |||||
796 | case Horde_Smtp_Exception::SERVER_CONNECT: |
||||
797 | Etemplate::set_validation_error('acc_smtp_host', lang($e->getMessage())); |
||||
798 | Etemplate::set_validation_error('acc_smtp_port', lang($e->getMessage())); |
||||
799 | break; |
||||
800 | } |
||||
801 | } |
||||
802 | $sel_options['acc_smtp_ssl'] = self::$ssl_types; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
803 | $tpl = new Etemplate('admin.mailwizard.smtp'); |
||||
804 | $tpl->exec(static::APP_CLASS.'smtp', $content, $sel_options, $readonlys, $content, 2); |
||||
805 | } |
||||
806 | |||||
807 | /** |
||||
808 | * Edit mail account(s) |
||||
809 | * |
||||
810 | * Gets either called with GET parameter: |
||||
811 | * |
||||
812 | * a) account_id from admin >> Manage users to edit / add mail accounts for a user |
||||
813 | * --> shows selectbox to switch between different mail accounts of user and "create new account" |
||||
814 | * |
||||
815 | * b) via mail_wizard proxy class by regular mail user to edit (acc_id GET parameter) or create new mail account |
||||
816 | * |
||||
817 | * @param array $content =null |
||||
818 | * @param string $msg ='' |
||||
819 | * @param string $msg_type ='success' |
||||
820 | */ |
||||
821 | public function edit(array $content=null, $msg='', $msg_type='success') |
||||
822 | { |
||||
823 | // app is trying to tell something, while redirecting to wizard |
||||
824 | if (empty($content) && $_GET['acc_id'] && empty($msg) && !empty( $_GET['msg'])) |
||||
825 | { |
||||
826 | if (stripos($_GET['msg'],'fatal error:')!==false || $_GET['msg_type'] == 'error') $msg_type = 'error'; |
||||
827 | } |
||||
828 | if ($content['acc_id'] || (isset($_GET['acc_id']) && (int)$_GET['acc_id'] > 0) ) Mail::unsetCachedObjects($content['acc_id']?$content['acc_id']:$_GET['acc_id']); |
||||
829 | $tpl = new Etemplate('admin.mailaccount'); |
||||
830 | |||||
831 | if (!is_array($content) || !empty($content['acc_id']) && isset($content['old_acc_id']) && $content['acc_id'] != $content['old_acc_id']) |
||||
832 | { |
||||
833 | if (!is_array($content)) $content = array(); |
||||
0 ignored issues
–
show
|
|||||
834 | if ($this->is_admin && isset($_GET['account_id'])) |
||||
835 | { |
||||
836 | $content['called_for'] = (int)$_GET['account_id']; |
||||
837 | $content['accounts'] = iterator_to_array(Mail\Account::search($content['called_for'])); |
||||
838 | if ($content['accounts']) |
||||
839 | { |
||||
840 | $content['acc_id'] = key($content['accounts']); |
||||
841 | //error_log(__METHOD__.__LINE__.'.'.array2string($content['acc_id'])); |
||||
842 | // test if the "to be selected" acccount is imap or not |
||||
843 | if (is_array($content['accounts']) && count($content['accounts'])>1 && Mail\Account::is_multiple($content['acc_id'])) |
||||
844 | { |
||||
845 | try { |
||||
846 | $account = Mail\Account::read($content['acc_id'], $content['called_for']); |
||||
847 | //try to select the first account that is of type imap |
||||
848 | if (!$account->is_imap()) |
||||
849 | { |
||||
850 | $content['acc_id'] = key($content['accounts']); |
||||
851 | //error_log(__METHOD__.__LINE__.'.'.array2string($content['acc_id'])); |
||||
852 | } |
||||
853 | } |
||||
854 | catch(Api\Exception\NotFound $e) { |
||||
855 | if (self::$debug) _egw_log_exception($e); |
||||
856 | } |
||||
857 | } |
||||
858 | } |
||||
859 | if (!$content['accounts']) // no email account, call wizard |
||||
860 | { |
||||
861 | return $this->add(array('account_id' => (int)$_GET['account_id'])); |
||||
0 ignored issues
–
show
Are you sure the usage of
$this->add(array('accoun...t)$_GET['account_id'])) targeting admin_mail::add() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||||
862 | } |
||||
863 | $content['accounts']['new'] = lang('Create new account'); |
||||
864 | } |
||||
865 | if (isset($_GET['acc_id']) && (int)$_GET['acc_id'] > 0) |
||||
866 | { |
||||
867 | $content['acc_id'] = (int)$_GET['acc_id']; |
||||
868 | } |
||||
869 | // clear current account-data, as account has changed and we going to read selected one |
||||
870 | $content = array_intersect_key($content, array_flip(array('called_for', 'accounts', 'acc_id', 'tabs'))); |
||||
871 | |||||
872 | if ($content['acc_id'] > 0) |
||||
873 | { |
||||
874 | try { |
||||
875 | $account = Mail\Account::read($content['acc_id'], $this->is_admin && $content['called_for'] ? |
||||
876 | $content['called_for'] : $GLOBALS['egw_info']['user']['account_id']); |
||||
877 | $account->getUserData(); // quota, aliases, forwards etc. |
||||
878 | $content += $account->params; |
||||
879 | $content['acc_sieve_enabled'] = (string)($content['acc_sieve_enabled']); |
||||
880 | $content['notify_use_default'] = !$content['notify_account_id']; |
||||
881 | self::fix_account_id_0($content['account_id']); |
||||
882 | |||||
883 | // read identities (of current user) and mark std identity |
||||
884 | $content['identities'] = iterator_to_array(Mail\Account::identities($account, true, 'name', $content['called_for'])); |
||||
885 | $content['std_ident_id'] = $content['ident_id']; |
||||
886 | $content['identities'][$content['std_ident_id']] = lang('Standard identity'); |
||||
887 | // change self::SSL_NONE (=0) to "no" used in sel_options |
||||
888 | foreach(array('imap','smtp','sieve') as $type) |
||||
889 | { |
||||
890 | if (!$content['acc_'.$type.'_ssl']) $content['acc_'.$type.'_ssl'] = 'no'; |
||||
891 | } |
||||
892 | } |
||||
893 | catch(Api\Exception\NotFound $e) { |
||||
894 | if (self::$debug) _egw_log_exception($e); |
||||
895 | Framework::window_close(lang('Account not found!')); |
||||
896 | } |
||||
897 | catch(Exception $e) { |
||||
898 | if (self::$debug) _egw_log_exception($e); |
||||
899 | Framework::window_close($e->getMessage().' ('.get_class($e).': '.$e->getCode().')'); |
||||
900 | } |
||||
901 | } |
||||
902 | elseif ($content['acc_id'] === 'new') |
||||
903 | { |
||||
904 | $content['account_id'] = $content['called_for']; |
||||
905 | $content['old_acc_id'] = $content['acc_id']; // to not call add/wizard, if we return from to |
||||
906 | unset($content['tabs']); |
||||
907 | return $this->add($content); |
||||
0 ignored issues
–
show
Are you sure the usage of
$this->add($content) targeting admin_mail::add() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||||
908 | } |
||||
909 | } |
||||
910 | // some defaults for new accounts |
||||
911 | if (!isset($content['account_id']) || empty($content['acc_id']) || $content['acc_id'] === 'new') |
||||
912 | { |
||||
913 | if (!isset($content['account_id'])) $content['account_id'] = array($GLOBALS['egw_info']['user']['account_id']); |
||||
914 | $content['acc_user_editable'] = $content['acc_further_identities'] = true; |
||||
915 | $readonlys['ident_id'] = true; // need to create standard identity first |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
916 | } |
||||
917 | if (empty($content['acc_name'])) |
||||
918 | { |
||||
919 | $content['acc_name'] = $content['ident_email']; |
||||
920 | } |
||||
921 | // disable some stuff for non-emailadmins (all values are preserved!) |
||||
922 | if (!$this->is_admin) |
||||
923 | { |
||||
924 | $readonlys = array( |
||||
925 | 'account_id' => true, 'button[multiple]' => true, 'acc_user_editable' => true, |
||||
926 | 'acc_further_identities' => true, |
||||
927 | 'acc_imap_type' => true, 'acc_imap_logintype' => true, 'acc_domain' => true, |
||||
928 | 'acc_imap_admin_username' => true, 'acc_imap_admin_password' => true, |
||||
929 | 'acc_smtp_type' => true, 'acc_smtp_auth_session' => true, |
||||
930 | ); |
||||
931 | } |
||||
932 | // ensure correct values for single user mail accounts (we only hide them client-side) |
||||
933 | if (!($is_multiple = Mail\Account::is_multiple($content))) |
||||
934 | { |
||||
935 | $content['acc_imap_type'] = 'EGroupware\\Api\\Mail\\Imap'; |
||||
936 | unset($content['acc_imap_login_type']); |
||||
937 | $content['acc_smtp_type'] = 'EGroupware\\Api\\Mail\\Smtp'; |
||||
938 | unset($content['acc_smtp_auth_session']); |
||||
939 | unset($content['notify_use_default']); |
||||
940 | } |
||||
941 | // copy ident_email_alias selectbox back to regular name |
||||
942 | elseif (isset($content['ident_email_alias']) && !empty ($content['ident_email_alias'])) |
||||
943 | { |
||||
944 | $content['ident_email'] = $content['ident_email_alias']; |
||||
945 | } |
||||
946 | $edit_access = Mail\Account::check_access(Acl::EDIT, $content); |
||||
947 | |||||
948 | // disable notification save-default and use-default, if only one account or no edit-rights |
||||
949 | $tpl->disableElement('notify_save_default', !$is_multiple || !$edit_access); |
||||
950 | $tpl->disableElement('notify_use_default', !$is_multiple); |
||||
951 | |||||
952 | if (isset($content['button'])) |
||||
953 | { |
||||
954 | $button = key($content['button']); |
||||
955 | unset($content['button']); |
||||
956 | switch($button) |
||||
957 | { |
||||
958 | case 'wizard': |
||||
959 | // if we just came from wizard, go back to last page/step |
||||
960 | if (isset($content['smtp_connected'])) |
||||
961 | { |
||||
962 | return $this->smtp($content); |
||||
0 ignored issues
–
show
Are you sure the usage of
$this->smtp($content) targeting admin_mail::smtp() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||||
963 | } |
||||
964 | // otherwise start with first step |
||||
965 | return $this->autoconfig($content); |
||||
966 | |||||
967 | case 'delete_identity': |
||||
968 | // delete none-standard identity of current user |
||||
969 | if (($this->is_admin || $content['acc_further_identities']) && |
||||
970 | $content['ident_id'] > 0 && $content['std_ident_id'] != $content['ident_id']) |
||||
971 | { |
||||
972 | Mail\Account::delete_identity($content['ident_id']); |
||||
973 | $msg = lang('Identity deleted'); |
||||
974 | unset($content['identities'][$content['ident_id']]); |
||||
975 | $content['ident_id'] = $content['std_ident_id']; |
||||
976 | } |
||||
977 | break; |
||||
978 | |||||
979 | case 'save': |
||||
980 | case 'apply': |
||||
981 | try { |
||||
982 | // save none-standard identity for current user |
||||
983 | if ($content['acc_id'] && $content['acc_id'] !== 'new' && |
||||
984 | ($this->is_admin || $content['acc_further_identities']) && |
||||
985 | $content['std_ident_id'] != $content['ident_id']) |
||||
986 | { |
||||
987 | $content['ident_id'] = Mail\Account::save_identity(array( |
||||
988 | 'account_id' => $content['called_for'] ? $content['called_for'] : $GLOBALS['egw_info']['user']['account_id'], |
||||
989 | )+$content); |
||||
990 | $content['identities'][$content['ident_id']] = Mail\Account::identity_name($content); |
||||
991 | $msg = lang('Identity saved.'); |
||||
992 | if ($edit_access) $msg .= ' '.lang('Switch back to standard identity to save account.'); |
||||
993 | } |
||||
994 | elseif ($edit_access) |
||||
995 | { |
||||
996 | // if admin username/password given, check if it is valid |
||||
997 | $account = new Mail\Account($content); |
||||
998 | if ($account->acc_imap_administration) |
||||
999 | { |
||||
1000 | $imap = $account->imapServer(true); |
||||
1001 | if ($imap) $imap->checkAdminConnection(); |
||||
0 ignored issues
–
show
|
|||||
1002 | } |
||||
1003 | // test sieve connection, if not called for other user, enabled and credentials available |
||||
1004 | if (!$content['called_for'] && $account->acc_sieve_enabled && $account->acc_imap_username) |
||||
1005 | { |
||||
1006 | $account->imapServer()->retrieveRules(); |
||||
0 ignored issues
–
show
The method
retrieveRules() does not exist on EGroupware\Api\Mail\Imap . Since you implemented __call , consider adding a @method annotation.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
1007 | } |
||||
1008 | $new_account = !($content['acc_id'] > 0); |
||||
1009 | // check for deliveryMode="forwardOnly", if a forwarding-address is given |
||||
1010 | if ($content['acc_smtp_type'] != 'EGroupware\\Api\\Mail\\Smtp' && |
||||
1011 | $content['deliveryMode'] == Mail\Smtp::FORWARD_ONLY && |
||||
1012 | empty($content['mailForwardingAddress'])) |
||||
1013 | { |
||||
1014 | Etemplate::set_validation_error('mailForwardingAddress', lang('Field must not be empty !!!')); |
||||
1015 | throw new Api\Exception\WrongUserinput(lang('You need to specify a forwarding address, when checking "%1"!', lang('Forward only'))); |
||||
0 ignored issues
–
show
The call to
lang() has too many arguments starting with lang('Forward only') .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||
1016 | } |
||||
1017 | // set notifications to store according to checkboxes |
||||
1018 | if ($content['notify_save_default']) |
||||
1019 | { |
||||
1020 | $content['notify_account_id'] = 0; |
||||
1021 | } |
||||
1022 | elseif (!$content['notify_use_default']) |
||||
1023 | { |
||||
1024 | $content['notify_account_id'] = $content['called_for'] ? |
||||
1025 | $content['called_for'] : $GLOBALS['egw_info']['user']['account_id']; |
||||
1026 | } |
||||
1027 | // SMIME SAVE |
||||
1028 | if (isset($content['smimeKeyUpload'])) |
||||
1029 | { |
||||
1030 | $content['acc_smime_cred_id'] = self::save_smime_key($content, $tpl, $content['called_for']); |
||||
1031 | unset($content['smimeKeyUpload']); |
||||
1032 | } |
||||
1033 | self::fix_account_id_0($content['account_id'], true); |
||||
1034 | $content = Mail\Account::write($content, $content['called_for'] || !$this->is_admin ? |
||||
1035 | $content['called_for'] : $GLOBALS['egw_info']['user']['account_id']); |
||||
1036 | self::fix_account_id_0($content['account_id']); |
||||
1037 | $msg = lang('Account saved.'); |
||||
1038 | // user wants default notifications |
||||
1039 | if ($content['acc_id'] && $content['notify_use_default']) |
||||
1040 | { |
||||
1041 | // delete own ones |
||||
1042 | Mail\Notifications::delete($content['acc_id'], $content['called_for'] ? |
||||
1043 | $content['called_for'] : $GLOBALS['egw_info']['user']['account_id']); |
||||
1044 | // load default ones |
||||
1045 | $content = array_merge($content, Mail\Notifications::read($content['acc_id'], 0)); |
||||
1046 | } |
||||
1047 | // add new std identity entry |
||||
1048 | if ($new_account) |
||||
1049 | { |
||||
1050 | $content['std_ident_id'] = $content['ident_id']; |
||||
1051 | $content['identities'] = array( |
||||
1052 | $content['std_ident_id'] => lang('Standard identity')); |
||||
1053 | } |
||||
1054 | if (isset($content['accounts'])) |
||||
1055 | { |
||||
1056 | if (!isset($content['accounts'][$content['acc_id']])) // insert new account as top, not bottom |
||||
1057 | { |
||||
1058 | $content['accounts'] = array($content['acc_id'] => '') + $content['accounts']; |
||||
1059 | } |
||||
1060 | $content['accounts'][$content['acc_id']] = Mail\Account::identity_name($content, false); |
||||
1061 | } |
||||
1062 | } |
||||
1063 | else |
||||
1064 | { |
||||
1065 | if ($content['notify_use_default'] && $content['notify_account_id']) |
||||
1066 | { |
||||
1067 | // delete own ones |
||||
1068 | if (Mail\Notifications::delete($content['acc_id'], $content['called_for'] ? |
||||
1069 | $content['called_for'] : $GLOBALS['egw_info']['user']['account_id'])) |
||||
1070 | { |
||||
1071 | $msg = lang('Notification folders updated.'); |
||||
1072 | } |
||||
1073 | // load default ones |
||||
1074 | $content = array_merge($content, Mail\Notifications::read($content['acc_id'], 0)); |
||||
1075 | } |
||||
1076 | if (!$content['notify_use_default'] && is_array($content['notify_folders'])) |
||||
1077 | { |
||||
1078 | $content['notify_account_id'] = $content['called_for'] ? |
||||
1079 | $content['called_for'] : $GLOBALS['egw_info']['user']['account_id']; |
||||
1080 | if (Mail\Notifications::write($content['acc_id'], $content['notify_account_id'], |
||||
1081 | $content['notify_folders'])) |
||||
1082 | { |
||||
1083 | $msg = lang('Notification folders updated.'); |
||||
1084 | } |
||||
1085 | } |
||||
1086 | if ($content['acc_user_forward'] && !empty($content['acc_smtp_type']) && $content['acc_smtp_type'] != 'EGroupware\\Api\\Mail\\Smtp') |
||||
1087 | { |
||||
1088 | $account = new Mail\Account($content); |
||||
1089 | $account->smtpServer()->saveSMTPForwarding($content['called_for'] ? |
||||
1090 | $content['called_for'] : $GLOBALS['egw_info']['user']['account_id'], |
||||
1091 | $content['mailForwardingAddress'], |
||||
1092 | $content['forwardOnly'] ? null : 'yes'); |
||||
1093 | } |
||||
1094 | // smime (private) key uploaded by user himself |
||||
1095 | if (!empty($content['smimeKeyUpload'])) |
||||
1096 | { |
||||
1097 | $content['acc_smime_cred_id'] = self::save_smime_key($content, $tpl); |
||||
1098 | unset($content['smimeKeyUpload']); |
||||
1099 | } |
||||
1100 | } |
||||
1101 | } |
||||
1102 | catch (Horde_Imap_Client_Exception $e) |
||||
1103 | { |
||||
1104 | _egw_log_exception($e); |
||||
1105 | $tpl->set_validation_error('acc_imap_admin_username', $msg=lang($e->getMessage()).($e->details?', '.lang($e->details):'')); |
||||
1106 | $msg_type = 'error'; |
||||
1107 | $content['tabs'] = 'admin.mailaccount.imap'; // should happen automatic |
||||
1108 | break; |
||||
1109 | } |
||||
1110 | catch (Horde\ManageSieve\Exception\ConnectionFailed $e) |
||||
1111 | { |
||||
1112 | _egw_log_exception($e); |
||||
1113 | $tpl->set_validation_error('acc_sieve_port', $msg=lang($e->getMessage())); |
||||
1114 | $msg_type = 'error'; |
||||
1115 | $content['tabs'] = 'admin.mailaccount.sieve'; // should happen automatic |
||||
1116 | break; |
||||
1117 | } |
||||
1118 | catch (Exception $e) { |
||||
1119 | $msg = lang('Error saving account!')."\n".$e->getMessage(); |
||||
1120 | $button = 'apply'; |
||||
1121 | $msg_type = 'error'; |
||||
1122 | } |
||||
1123 | if ($content['acc_id']) Mail::unsetCachedObjects($content['acc_id']); |
||||
1124 | if (stripos($msg,'fatal error:')!==false) $msg_type = 'error'; |
||||
1125 | Framework::refresh_opener($msg, 'emailadmin', $content['acc_id'], $new_account ? 'add' : 'update', null, null, null, $msg_type); |
||||
1126 | if ($button == 'save') Framework::window_close(); |
||||
1127 | break; |
||||
1128 | |||||
1129 | case 'delete': |
||||
1130 | if (!Mail\Account::check_access(Acl::DELETE, $content)) |
||||
1131 | { |
||||
1132 | $msg = lang('Permission denied!'); |
||||
1133 | $msg_type = 'error'; |
||||
1134 | } |
||||
1135 | elseif (Mail\Account::delete($content['acc_id']) > 0) |
||||
1136 | { |
||||
1137 | if ($content['acc_id']) Mail::unsetCachedObjects($content['acc_id']); |
||||
1138 | Framework::refresh_opener(lang('Account deleted.'), 'emailadmin', $content['acc_id'], 'delete'); |
||||
1139 | Framework::window_close(); |
||||
1140 | } |
||||
1141 | else |
||||
1142 | { |
||||
1143 | $msg = lang('Failed to delete account!'); |
||||
1144 | $msg_type = 'error'; |
||||
1145 | } |
||||
1146 | } |
||||
1147 | } |
||||
1148 | // SMIME UPLOAD/DELETE/EXPORT control |
||||
1149 | $content['hide_smime_upload'] = false; |
||||
1150 | if (!empty($content['acc_smime_cred_id'])) |
||||
1151 | { |
||||
1152 | if (!empty($content['smime_delete_p12']) && |
||||
1153 | Mail\Credentials::delete ( |
||||
1154 | $content['acc_id'], |
||||
1155 | $content['called_for'] ? $content['called_for'] : $GLOBALS['egw_info']['user']['account_id'], |
||||
1156 | Mail\Credentials::SMIME |
||||
1157 | )) |
||||
1158 | { |
||||
1159 | unset($content['acc_smime_password'], $content['smimeKeyUpload'], $content['smime_delete_p12'], $content['acc_smime_cred_id']); |
||||
1160 | $content['hide_smime_upload'] = false; |
||||
1161 | } |
||||
1162 | else |
||||
1163 | { |
||||
1164 | // do NOT send smime private key to client side, it's unnecessary and binary blob breaks json encoding |
||||
1165 | $content['acc_smime_password'] = Mail\Credentials::UNAVAILABLE; |
||||
1166 | |||||
1167 | $content['hide_smime_upload'] = true; |
||||
1168 | } |
||||
1169 | } |
||||
1170 | |||||
1171 | // disable delete button for new, not yet saved entries, if no delete rights or a non-standard identity selected |
||||
1172 | $readonlys['button[delete]'] = empty($content['acc_id']) || |
||||
1173 | !Mail\Account::check_access(Acl::DELETE, $content) || |
||||
1174 | $content['ident_id'] != $content['std_ident_id']; |
||||
1175 | |||||
1176 | // if account is for multiple user, change delete confirmation to reflect that |
||||
1177 | if (Mail\Account::is_multiple($content)) |
||||
1178 | { |
||||
1179 | $tpl->setElementAttribute('button[delete]', 'onclick', "et2_dialog.confirm(widget,'This is NOT a personal mail account!\\n\\nAccount will be deleted for ALL users!\\n\\nAre you really sure you want to do that?','Delete this account')"); |
||||
1180 | } |
||||
1181 | |||||
1182 | // if no edit access, make whole dialog readonly |
||||
1183 | if (!$edit_access) |
||||
1184 | { |
||||
1185 | $readonlys['__ALL__'] = true; |
||||
1186 | $readonlys['button[cancel]'] = false; |
||||
1187 | // allow to edit notification-folders |
||||
1188 | $readonlys['button[save]'] = $readonlys['button[apply]'] = |
||||
1189 | $readonlys['notify_folders'] = $readonlys['notify_use_default'] = false; |
||||
1190 | // allow to edit sMime stuff |
||||
1191 | $readonlys['smimeGenerate'] = $readonlys['smimeKeyUpload'] = $readonlys['smime_pkcs12_password'] = |
||||
1192 | $readonlys['smime_export_p12'] = $readonlys['smime_delete_p12'] = false; |
||||
1193 | } |
||||
1194 | |||||
1195 | $sel_options['acc_imap_ssl'] = $sel_options['acc_sieve_ssl'] = |
||||
1196 | $sel_options['acc_smtp_ssl'] = self::$ssl_types; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
1197 | |||||
1198 | // admin access to account with no credentials available |
||||
1199 | if ($this->is_admin && (empty($content['acc_imap_username']) || empty($content['acc_imap_host']) || $content['called_for'])) |
||||
1200 | { |
||||
1201 | // cant connection to imap --> allow free entries in taglists |
||||
1202 | foreach(array('acc_folder_sent', 'acc_folder_trash', 'acc_folder_draft', 'acc_folder_template', 'acc_folder_junk') as $folder) |
||||
1203 | { |
||||
1204 | $tpl->setElementAttribute($folder, 'allowFreeEntries', true); |
||||
1205 | } |
||||
1206 | } |
||||
1207 | else |
||||
1208 | { |
||||
1209 | try { |
||||
1210 | $sel_options['acc_folder_sent'] = $sel_options['acc_folder_trash'] = |
||||
1211 | $sel_options['acc_folder_draft'] = $sel_options['acc_folder_template'] = |
||||
1212 | $sel_options['acc_folder_junk'] = $sel_options['acc_folder_archive'] = |
||||
1213 | $sel_options['notify_folders'] = $sel_options['acc_folder_ham'] = |
||||
1214 | self::mailboxes(self::imap_client ($content)); |
||||
1215 | // Allow folder notification on INBOX for popup_only |
||||
1216 | if ($GLOBALS['egw_info']['user']['preferences']['notifications']['notification_chain'] == 'popup_only') |
||||
1217 | { |
||||
1218 | $sel_options['notify_folders']['INBOX'] = lang('INBOX'); |
||||
1219 | } |
||||
1220 | } |
||||
1221 | catch(Exception $e) { |
||||
1222 | if (self::$debug) _egw_log_exception($e); |
||||
1223 | // let user know what the problem is and that he can fix it using wizard or deleting |
||||
1224 | $msg = lang($e->getMessage())."\n\n".lang('You can use wizard to fix account settings or delete account.'); |
||||
1225 | $msg_type = 'error'; |
||||
1226 | // cant connection to imap --> allow free entries in taglists |
||||
1227 | foreach(array('acc_folder_sent', 'acc_folder_trash', 'acc_folder_draft', 'acc_folder_template', 'acc_folder_junk') as $folder) |
||||
1228 | { |
||||
1229 | $tpl->setElementAttribute($folder, 'allowFreeEntries', true); |
||||
1230 | } |
||||
1231 | } |
||||
1232 | } |
||||
1233 | |||||
1234 | $sel_options['acc_imap_type'] = Mail\Types::getIMAPServerTypes(false); |
||||
1235 | $sel_options['acc_smtp_type'] = Mail\Types::getSMTPServerTypes(false); |
||||
1236 | $sel_options['acc_imap_logintype'] = self::$login_types; |
||||
1237 | $sel_options['ident_id'] = $content['identities']; |
||||
1238 | $sel_options['acc_id'] = $content['accounts']; |
||||
1239 | $sel_options['acc_further_identities'] = self::$further_identities; |
||||
1240 | |||||
1241 | // user is allowed to create or edit further identities |
||||
1242 | if ($edit_access || $content['acc_further_identities']) |
||||
1243 | { |
||||
1244 | $sel_options['ident_id']['new'] = lang('Create new identity'); |
||||
1245 | $readonlys['ident_id'] = false; |
||||
1246 | |||||
1247 | // if no edit-access and identity is not standard identity --> allow to edit identity |
||||
1248 | if (!$edit_access && $content['ident_id'] != $content['std_ident_id']) |
||||
1249 | { |
||||
1250 | $readonlys += array( |
||||
1251 | 'button[save]' => false, 'button[apply]' => false, |
||||
1252 | 'button[placeholders]' => false, |
||||
1253 | 'ident_name' => false, |
||||
1254 | 'ident_realname' => false, 'ident_email' => false, 'ident_email_alias' => false, |
||||
1255 | 'ident_org' => false, 'ident_signature' => false, |
||||
1256 | ); |
||||
1257 | } |
||||
1258 | if ($content['ident_id'] != $content['old_ident_id'] && |
||||
1259 | ($content['old_ident_id'] || $content['ident_id'] != $content['std_ident_id'])) |
||||
1260 | { |
||||
1261 | if ($content['ident_id'] > 0) |
||||
1262 | { |
||||
1263 | $identity = Mail\Account::read_identity($content['ident_id'], false, $content['called_for']); |
||||
1264 | unset($identity['account_id']); |
||||
1265 | $content = array_merge($content, $identity, array('ident_email_alias' => $identity['ident_email'])); |
||||
1266 | } |
||||
1267 | else |
||||
1268 | { |
||||
1269 | $content['ident_name'] = $content['ident_realname'] = $content['ident_email'] = |
||||
1270 | $content['ident_email_alias'] = $content['ident_org'] = $content['ident_signature'] = ''; |
||||
1271 | } |
||||
1272 | if (empty($msg) && $edit_access && $content['ident_id'] && $content['ident_id'] != $content['std_ident_id']) |
||||
1273 | { |
||||
1274 | $msg = lang('Switch back to standard identity to save other account data.'); |
||||
1275 | $msg_type = 'help'; |
||||
1276 | } |
||||
1277 | $content['old_ident_id'] = $content['ident_id']; |
||||
1278 | } |
||||
1279 | } |
||||
1280 | $content['old_acc_id'] = $content['acc_id']; |
||||
1281 | |||||
1282 | // if only aliases are allowed for futher identities, add them as options |
||||
1283 | // allow admins to always add arbitrary aliases |
||||
1284 | if ($content['acc_further_identities'] == 2 && !$this->is_admin) |
||||
1285 | { |
||||
1286 | $sel_options['ident_email_alias'] = array_merge( |
||||
1287 | array('' => $content['mailLocalAddress'].' ('.lang('Default').')'), |
||||
1288 | array_combine($content['mailAlternateAddress'], $content['mailAlternateAddress'])); |
||||
1289 | // if admin explicitly set a non-alias, we need to add it to aliases to keep it after storing signature by user |
||||
1290 | if ($content['ident_email'] !== $content['mailLocalAddress'] && !isset($sel_options['ident_email_alias'][$content['ident_email']])) |
||||
1291 | { |
||||
1292 | $sel_options['ident_email_alias'][$content['ident_email']] = $content['ident_email']; |
||||
1293 | } |
||||
1294 | // copy ident_email to select-box ident_email_alias, as et2 requires unique ids |
||||
1295 | $content['ident_email_alias'] = $content['ident_email']; |
||||
1296 | $content['select_ident_mail'] = true; |
||||
1297 | } |
||||
1298 | |||||
1299 | // only allow to delete further identities, not a standard identity |
||||
1300 | $readonlys['button[delete_identity]'] = !($content['ident_id'] > 0 && $content['ident_id'] != $content['std_ident_id']); |
||||
1301 | |||||
1302 | // disable aliases tab for default smtp class EGroupware\Api\Mail\Smtp |
||||
1303 | $readonlys['tabs']['admin.mailaccount.aliases'] = !$content['acc_smtp_type'] || |
||||
1304 | $content['acc_smtp_type'] == 'EGroupware\\Api\\Mail\\Smtp'; |
||||
1305 | if ($readonlys['tabs']['admin.mailaccount.aliases']) |
||||
1306 | { |
||||
1307 | unset($sel_options['acc_further_identities'][2]); // can limit identities to aliases without aliases ;-) |
||||
1308 | } |
||||
1309 | |||||
1310 | // allow smtp class to disable certain features in alias tab |
||||
1311 | if ($content['acc_smtp_type'] && class_exists($content['acc_smtp_type']) && |
||||
1312 | is_a($content['acc_smtp_type'], 'EGroupware\\Api\\Mail\\Smtp\\Ldap', true)) |
||||
1313 | { |
||||
1314 | $content['no_forward_available'] = !constant($content['acc_smtp_type'].'::FORWARD_ATTR'); |
||||
1315 | if (!constant($content['acc_smtp_type'].'::FORWARD_ONLY_ATTR')) |
||||
1316 | { |
||||
1317 | $readonlys['deliveryMode'] = true; |
||||
1318 | } |
||||
1319 | } |
||||
1320 | |||||
1321 | // account allows users to change forwards |
||||
1322 | if (!$edit_access && !$readonlys['tabs']['admin.mailaccount.aliases'] && $content['acc_user_forward']) |
||||
1323 | { |
||||
1324 | $readonlys['mailForwardingAddress'] = false; |
||||
1325 | } |
||||
1326 | |||||
1327 | // allow imap classes to disable certain tabs or fields |
||||
1328 | if (($class = Mail\Account::getIcClass($content['acc_imap_type'])) && class_exists($class) && |
||||
1329 | ($imap_ro = call_user_func(array($class, 'getUIreadonlys')))) |
||||
1330 | { |
||||
1331 | $readonlys = array_merge($readonlys, $imap_ro, array( |
||||
1332 | 'tabs' => array_merge((array)$readonlys['tabs'], (array)$imap_ro['tabs']), |
||||
1333 | )); |
||||
1334 | } |
||||
1335 | Framework::message($msg ? $msg : (string)$_GET['msg'], $msg_type); |
||||
1336 | |||||
1337 | if (is_array($content['account_id']) && count($content['account_id']) > 1) |
||||
1338 | { |
||||
1339 | $tpl->setElementAttribute('account_id', 'multiple', true); |
||||
1340 | $readonlys['button[multiple]'] = true; |
||||
1341 | } |
||||
1342 | // when called by admin for existing accounts, display further administrative actions |
||||
1343 | if ($content['called_for'] && $content['acc_id'] > 0) |
||||
1344 | { |
||||
1345 | $admin_actions = array(); |
||||
1346 | foreach(Api\Hooks::process(array( |
||||
1347 | 'location' => 'emailadmin_edit', |
||||
1348 | 'account_id' => $content['called_for'], |
||||
1349 | 'acc_id' => $content['acc_id'], |
||||
1350 | )) as $actions) |
||||
1351 | { |
||||
1352 | if ($actions) $admin_actions = array_merge($admin_actions, $actions); |
||||
1353 | } |
||||
1354 | if ($admin_actions) $tpl->setElementAttribute('admin_actions', 'actions', $admin_actions); |
||||
1355 | } |
||||
1356 | $content['admin_actions'] = (bool)$admin_actions; |
||||
1357 | |||||
1358 | //try to fix identities with no domain part set e.g. alias as identity |
||||
1359 | if (!strpos($content['ident_email'], '@')) |
||||
1360 | { |
||||
1361 | $content['ident_email'] = Mail::fixInvalidAliasAddress (Api\Accounts::id2name($content['acc_imap_account_id'], 'account_email'), $content['ident_email']); |
||||
1362 | } |
||||
1363 | |||||
1364 | $tpl->exec(static::APP_CLASS.'edit', $content, $sel_options, $readonlys, $content, 2); |
||||
1365 | } |
||||
1366 | |||||
1367 | /** |
||||
1368 | * Saves the smime key |
||||
1369 | * |
||||
1370 | * @param array $content |
||||
1371 | * @param Etemplate $tpl |
||||
1372 | * @param int $account_id =null account to save smime key for, default current user |
||||
1373 | * @return int cred_id or null on error |
||||
1374 | */ |
||||
1375 | private static function save_smime_key(array $content, Etemplate $tpl, $account_id=null) |
||||
1376 | { |
||||
1377 | if (($pkcs12 = file_get_contents($content['smimeKeyUpload']['tmp_name']))) |
||||
1378 | { |
||||
1379 | $cert_info = Mail\Smime::extractCertPKCS12($pkcs12, $content['smime_pkcs12_password']); |
||||
1380 | if (is_array($cert_info) && !empty($cert_info['cert'])) |
||||
0 ignored issues
–
show
|
|||||
1381 | { |
||||
1382 | // save public key |
||||
1383 | $smime = new Mail\Smime; |
||||
1384 | $email = $smime->getEmailFromKey($cert_info['cert']); |
||||
1385 | $AB_bo = new addressbook_bo(); |
||||
1386 | $AB_bo->set_smime_keys(array( |
||||
1387 | $email => $cert_info['cert'] |
||||
1388 | )); |
||||
1389 | // save private key |
||||
1390 | if (!isset($account_id)) $account_id = $GLOBALS['egw_info']['user']['account_id']; |
||||
1391 | return Mail\Credentials::write($content['acc_id'], $email, $pkcs12, Mail\Credentials::SMIME, $account_id); |
||||
1392 | } |
||||
1393 | $tpl->set_validation_error('smimeKeyUpload', lang('Could not extract private key from given p12 file. Either the p12 file is broken or password is wrong!')); |
||||
1394 | } |
||||
1395 | return null; |
||||
1396 | } |
||||
1397 | |||||
1398 | /** |
||||
1399 | * Replace 0 with '' or back |
||||
1400 | * |
||||
1401 | * @param string|array &$account_id on return always array |
||||
1402 | * @param boolean $back =false |
||||
1403 | */ |
||||
1404 | private static function fix_account_id_0(&$account_id=null, $back=false) |
||||
1405 | { |
||||
1406 | if (!isset($account_id)) return; |
||||
1407 | |||||
1408 | if (!is_array($account_id)) |
||||
1409 | { |
||||
1410 | $account_id = explode(',', $account_id); |
||||
1411 | } |
||||
1412 | if (($k = array_search($back?'':'0', $account_id)) !== false) |
||||
1413 | { |
||||
1414 | $account_id[$k] = $back ? '0' : ''; |
||||
1415 | } |
||||
1416 | } |
||||
1417 | |||||
1418 | /** |
||||
1419 | * Instanciate imap-client |
||||
1420 | * |
||||
1421 | * @param array $content |
||||
1422 | * @param int $timeout =null default use value returned by Mail\Imap::getTimeOut() |
||||
1423 | * @return Horde_Imap_Client_Socket |
||||
1424 | */ |
||||
1425 | protected static function imap_client(array $content, $timeout=null) |
||||
1426 | { |
||||
1427 | return new Horde_Imap_Client_Socket(array( |
||||
1428 | 'username' => $content['acc_imap_username'], |
||||
1429 | 'password' => $content['acc_imap_password'], |
||||
1430 | 'hostspec' => $content['acc_imap_host'], |
||||
1431 | 'port' => $content['acc_imap_port'], |
||||
1432 | 'secure' => self::$ssl2secure[(string)array_search($content['acc_imap_ssl'], self::$ssl2type)], |
||||
1433 | 'timeout' => $timeout > 0 ? $timeout : Mail\Imap::getTimeOut(), |
||||
1434 | 'debug' => self::DEBUG_LOG, |
||||
1435 | )); |
||||
1436 | } |
||||
1437 | |||||
1438 | /** |
||||
1439 | * Reorder SSL types to make sure we start with TLS, SSL, STARTTLS and insecure last |
||||
1440 | * |
||||
1441 | * @param array $data ssl => port pairs plus other data like value for 'username' |
||||
1442 | * @return array |
||||
1443 | */ |
||||
1444 | protected static function fix_ssl_order($data) |
||||
1445 | { |
||||
1446 | $ordered = array(); |
||||
1447 | foreach(array_merge(array('TLS', 'SSL', 'STARTTLS'), array_keys($data)) as $key) |
||||
1448 | { |
||||
1449 | if (array_key_exists($key, $data)) $ordered[$key] = $data[$key]; |
||||
1450 | } |
||||
1451 | return $ordered; |
||||
1452 | } |
||||
1453 | |||||
1454 | /** |
||||
1455 | * Query Mozilla's ISPDB |
||||
1456 | * |
||||
1457 | * Some providers eg. 1-and-1 do not report their hosted domains to ISPDB, |
||||
1458 | * therefore we try it with the found MX and it's domain-part (host-name removed). |
||||
1459 | * |
||||
1460 | * @param string $domain domain or email |
||||
1461 | * @param boolean $try_mx =true if domain itself is not found, try mx or domain-part (host removed) of mx |
||||
1462 | * @return array with values for keys 'displayName', 'imap', 'smtp', 'pop3', which each contain |
||||
1463 | * array of arrays with values for keys 'hostname', 'port', 'socketType'=(SSL|STARTTLS), 'username'=%EMAILADDRESS% |
||||
1464 | */ |
||||
1465 | protected static function mozilla_ispdb($domain, $try_mx=true) |
||||
1466 | { |
||||
1467 | if (strpos($domain, '@') !== false) list(,$domain) = explode('@', $domain); |
||||
1468 | |||||
1469 | $url = 'https://autoconfig.thunderbird.net/v1.1/'.$domain; |
||||
1470 | try { |
||||
1471 | $xml = @simplexml_load_file($url); |
||||
1472 | if (!$xml->emailProvider) throw new Api\Exception\NotFound(); |
||||
1473 | $provider = array( |
||||
1474 | 'displayName' => (string)$xml->emailProvider->displayName, |
||||
1475 | ); |
||||
1476 | foreach($xml->emailProvider->children() as $tag => $server) |
||||
1477 | { |
||||
1478 | if (!in_array($tag, array('incomingServer', 'outgoingServer'))) continue; |
||||
1479 | foreach($server->attributes() as $name => $value) |
||||
1480 | { |
||||
1481 | if ($name == 'type') $type = (string)$value; |
||||
1482 | } |
||||
1483 | $data = array(); |
||||
1484 | foreach($server as $name => $value) |
||||
1485 | { |
||||
1486 | foreach($value->children() as $tag => $val) |
||||
0 ignored issues
–
show
|
|||||
1487 | { |
||||
1488 | $data[$name][$tag] = (string)$val; |
||||
1489 | } |
||||
1490 | if (!isset($data[$name])) $data[$name] = (string)$value; |
||||
1491 | } |
||||
1492 | $provider[$type][] = $data; |
||||
1493 | } |
||||
1494 | } |
||||
1495 | catch(Exception $e) { |
||||
1496 | // ignore own not-found exception or xml parsing execptions |
||||
1497 | unset($e); |
||||
1498 | |||||
1499 | if ($try_mx && ($dns = dns_get_record($domain, DNS_MX))) |
||||
1500 | { |
||||
1501 | $domain = $dns[0]['target']; |
||||
1502 | if (!($provider = self::mozilla_ispdb($domain, false))) |
||||
1503 | { |
||||
1504 | list(,$domain) = explode('.', $domain, 2); |
||||
1505 | $provider = self::mozilla_ispdb($domain, false); |
||||
1506 | } |
||||
1507 | } |
||||
1508 | else |
||||
1509 | { |
||||
1510 | $provider = array(); |
||||
1511 | } |
||||
1512 | } |
||||
1513 | //error_log(__METHOD__."('$email') returning ".array2string($provider)); |
||||
1514 | return $provider; |
||||
1515 | } |
||||
1516 | |||||
1517 | /** |
||||
1518 | * Guess possible server hostnames from email address: |
||||
1519 | * - $type.$domain, mail.$domain |
||||
1520 | * - replace host in MX with imap or mail |
||||
1521 | * - MX for $domain |
||||
1522 | * |
||||
1523 | * @param string $email email address |
||||
1524 | * @param string $type ='imap' 'imap' or 'smtp', used as hostname beside 'mail' |
||||
1525 | * @return array of hostname => true pairs |
||||
1526 | */ |
||||
1527 | protected function guess_hosts($email, $type='imap') |
||||
1528 | { |
||||
1529 | list(,$domain) = explode('@', $email); |
||||
1530 | |||||
1531 | $hosts = array(); |
||||
1532 | |||||
1533 | // try usuall names |
||||
1534 | $hosts[$type.'.'.$domain] = true; |
||||
1535 | $hosts['mail.'.$domain] = true; |
||||
1536 | if ($type == 'smtp') $hosts['send.'.$domain] = true; |
||||
1537 | |||||
1538 | if (($dns = dns_get_record($domain, DNS_MX))) |
||||
1539 | { |
||||
1540 | //error_log(__METHOD__."('$email') dns_get_record('$domain', DNS_MX) returned ".array2string($dns)); |
||||
1541 | // hosts for office365 are outlook|smpt.office365.com for MX *.mail.protection.outlook.com |
||||
1542 | if (substr($dns[0]['target'], -28) == '.mail.protection.outlook.com') |
||||
1543 | { |
||||
1544 | $hosts[($type == 'imap' ? 'outlook' : 'smtp').'.office365.com'] = true; |
||||
1545 | } |
||||
1546 | $hosts[preg_replace('/^[^.]+/', $type, $dns[0]['target'])] = true; |
||||
1547 | $hosts[preg_replace('/^[^.]+/', 'mail', $dns[0]['target'])] = true; |
||||
1548 | if ($type == 'smtp') $hosts[preg_replace('/^[^.]+/', 'send', $dns[0]['target'])] = true; |
||||
1549 | $hosts[$dns[0]['target']] = true; |
||||
1550 | } |
||||
1551 | |||||
1552 | // verify hosts in dns |
||||
1553 | foreach(array_keys($hosts) as $host) |
||||
1554 | { |
||||
1555 | if (!dns_get_record($host, DNS_A)) unset($hosts[$host]); |
||||
1556 | } |
||||
1557 | //error_log(__METHOD__."('$email') returning ".array2string($hosts)); |
||||
1558 | return $hosts; |
||||
1559 | } |
||||
1560 | |||||
1561 | /** |
||||
1562 | * Set mail account status wheter to 'active' or '' (inactive) |
||||
1563 | * |
||||
1564 | * @param array $_data account an array of data called via long task running dialog |
||||
1565 | * $_data:array ( |
||||
1566 | * id => account_id, |
||||
1567 | * qouta => quotaLimit, |
||||
1568 | * domain => mailLocalAddress, |
||||
1569 | * status => mail activation status('active'|'') |
||||
1570 | * ) |
||||
1571 | * @param string $etemplate_exec_id to check against CSRF |
||||
1572 | * @return json response |
||||
1573 | */ |
||||
1574 | public function ajax_activeAccounts($_data, $etemplate_exec_id) |
||||
1575 | { |
||||
1576 | Api\Etemplate\Request::csrfCheck($etemplate_exec_id, __METHOD__, func_get_args()); |
||||
1577 | |||||
1578 | if (!$this->is_admin) die('no rights to be here!'); |
||||
0 ignored issues
–
show
|
|||||
1579 | $response = Api\Json\Response::get(); |
||||
1580 | if (($account = $GLOBALS['egw']->accounts->read($_data['id']))) |
||||
1581 | { |
||||
1582 | if ($_data['quota'] !== '' || $_data['accountStatus'] !== '' |
||||
1583 | || strpos($_data['domain'], '.')) |
||||
1584 | { |
||||
1585 | $emailadmin = Mail\Account::get_default(); |
||||
1586 | if (!Mail\Account::is_multiple($emailadmin)) |
||||
1587 | { |
||||
1588 | $msg = lang('No default account found!'); |
||||
1589 | return $response->data($msg); |
||||
1590 | } |
||||
1591 | |||||
1592 | $ea_account = Mail\Account::read($emailadmin->acc_id, $_data['id']); |
||||
1593 | if (($userData = $ea_account->getUserData ())) |
||||
1594 | { |
||||
1595 | $userData = array( |
||||
1596 | 'acc_smtp_type' => $ea_account->acc_smtp_type, |
||||
1597 | 'accountStatus' => $_data['status'], |
||||
1598 | 'quotaLimit' => $_data['qouta']? $_data['qouta']: $userData['qoutaLimit'], |
||||
1599 | 'mailLocalAddress' => $userData['mailLocalAddress'] |
||||
1600 | ); |
||||
1601 | |||||
1602 | if (strpos($_data['domain'], '.') !== false) |
||||
1603 | { |
||||
1604 | $userData['mailLocalAddress'] = preg_replace('/@'.preg_quote($ea_account->acc_domain).'$/', '@'.$_data['domain'], $userData['mailLocalAddress']); |
||||
1605 | |||||
1606 | foreach($userData['mailAlternateAddress'] as &$alias) |
||||
1607 | { |
||||
1608 | $alias = preg_replace('/@'.preg_quote($ea_account->acc_domain).'$/', '@'.$_data['domain'], $alias); |
||||
1609 | } |
||||
1610 | } |
||||
1611 | // fullfill the saveUserData requirements |
||||
1612 | $userData += $ea_account->params; |
||||
1613 | $ea_account->saveUserData($_data['id'], $userData); |
||||
1614 | $msg = '#'.$_data['id'].' '.$account['account_fullname']. ' '.($userData['accountStatus'] == 'active'? lang('activated'):lang('deactivated')); |
||||
1615 | } |
||||
1616 | else |
||||
1617 | { |
||||
1618 | $msg .= lang('No profile defined for user %1', '#'.$_data['id'].' '.$account['account_fullname']."\n"); |
||||
0 ignored issues
–
show
The call to
lang() has too many arguments starting with '#' . $_data['id'] . ' '...ccount_fullname'] . ' ' .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() Comprehensibility
Best Practice
introduced
by
|
|||||
1619 | |||||
1620 | } |
||||
1621 | } |
||||
1622 | } |
||||
1623 | $response->data($msg); |
||||
1624 | } |
||||
1625 | } |
||||
1626 | |||||
1627 | /** |
||||
1628 | * Trivial file logger, as Horde\ManageSieve does not support just a file |
||||
1629 | */ |
||||
1630 | class admin_mail_logger |
||||
1631 | { |
||||
1632 | private $fp; |
||||
1633 | |||||
1634 | public function __construct($log) |
||||
1635 | { |
||||
1636 | $this->fp = is_resource($log) ? $log : fopen($log, 'a'); |
||||
1637 | } |
||||
1638 | |||||
1639 | public function debug($msg) |
||||
1640 | { |
||||
1641 | fwrite($this->fp, $msg."\n"); |
||||
1642 | } |
||||
1643 | } |
||||
1644 |
Let?s assume that you have a directory layout like this:
and let?s assume the following content of
Bar.php
:If both files
OtherDir/Foo.php
andSomeDir/Foo.php
are loaded in the same runtime, you will see a PHP error such as the following:PHP Fatal error: Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php
However, as
OtherDir/Foo.php
does not necessarily have to be loaded and the error is only triggered if it is loaded beforeOtherDir/Bar.php
, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias: