| Total Complexity | 347 |
| Total Lines | 1595 |
| Duplicated Lines | 0 % |
| Changes | 0 | ||
Complex classes like admin_mail often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use admin_mail, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 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; |
||
| 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"; |
||
| 245 | $hosts = array(); |
||
| 246 | foreach($ispdb['imap'] as $server) |
||
| 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) |
||
| 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); |
||
| 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); |
||
| 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(); |
||
| 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; |
||
| 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; |
||
| 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',''); |
||
| 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; |
||
| 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='') |
||
| 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(); |
||
| 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'])); |
||
| 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); |
||
| 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 |
||
| 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); |
||
| 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(); |
||
| 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(); |
||
| 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'))); |
||
| 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; |
||
| 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'])) |
||
| 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) |
||
| 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) |
||
| 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') |
||
| 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) |
||
| 1644 |
Let?s assume that you have a directory layout like this:
. |-- OtherDir | |-- Bar.php | `-- Foo.php `-- SomeDir `-- Foo.phpand let?s assume the following content of
Bar.php:If both files
OtherDir/Foo.phpandSomeDir/Foo.phpare 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.phpHowever, as
OtherDir/Foo.phpdoes 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: