1<?php 2/* 3 4 This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) 5 Copyright (C) 2010 - 2011 Pavel Pozdniak 6 2010 - 2020 Roland Gruber 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21*/ 22 23/** 24 * Manages mailboxes on an IMAP server. 25 * 26 * @package modules 27 * @author Pavel Pozdniak 28 * @author Roland Gruber 29 */ 30 31/** 32 * Manages mailboxes on an IMAP server. 33 * 34 * @package modules 35 * @author Pavel Pozdniak 36 * @author Roland Gruber 37 */ 38class imapAccess extends baseModule { 39 40 /** quota limit from profile */ 41 private $profileQuotaLimit = null; 42 43 /** user name */ 44 private $user; 45 /** email address */ 46 private $email; 47 48 /** 49 * Returns true if this module can manage accounts of the current type, otherwise false. 50 * 51 * @return boolean true if module fits 52 */ 53 public function can_manage() { 54 return in_array($this->get_scope(), array('user')); 55 } 56 57 /** 58 * Returns meta data that is interpreted by parent class 59 * 60 * @return array array with meta data 61 * 62 * @see baseModule::get_metaData() 63 */ 64 function get_metaData() { 65 $return = array(); 66 // alias name 67 $return["alias"] = _("Mailbox"); 68 // module dependencies 69 $return['dependencies'] = array('depends' => array(array('inetOrgPerson', 'windowsUser')), 'conflicts' => array()); 70 // managed object classes 71 $return['objectClasses'] = array(); 72 // managed attributes 73 $return['attributes'] = array(); 74 // PHP extensions 75 $return['extensions'] = array('imap'); 76 // icon 77 $return['icon'] = 'mailBig.png'; 78 // help 79 $return['help'] = array( 80 'ImapServerAddress' => array( 81 "Headline" => _("Server address"), 82 "Text" => _("Address of IMAP server (e.g. mail.example.org).")), 83 'ImapServerEncryptionProtocol' => array( 84 "Headline" => _("Encryption protocol"), 85 "Text" => _("Encryption protocol for connecting to IMAP server. LAM requires an encrypted connection.")), 86 'ImapValidateServerCert' => array( 87 "Headline" => _("Validate server certificate"), 88 "Text" => _("This option allows you to disable the certificate check of your IMAP server certificate. Disabling the certificate check is not recommended.")), 89 'ImapAdmin' => array( 90 "Headline" => _("IMAP admin user"), 91 "Text" => _("The login name of your IMAP user who has rights to create/delete mailboxes.") . ' ' . _('Use wildcards like $uid$ for LDAP attributes of the current LAM admin user.')), 92 'ImapAdminPasswordSelect' => array( 93 "Headline" => _("IMAP password input"), 94 "Text" => _("Choose the way how to provide the IMAP admin password. You can use the same password as for the LAM login or LAM will ask you for a password when it is required.") 95 . ' ' . _('Storing the password in your server profile is also possible but not recommended.') 96 ), 97 'ImapAdminPassword_Sess' => array( 98 "Headline" => _("Password of IMAP admin user"), 99 "Text" => _("The password of your IMAP admin user. The login name for the IMAP admin user is stored in the LAM server profile.")), 100 'ImapUserPrefix' => array( 101 "Headline" => _("Prefix for mailboxes"), 102 "Text" => _("Some IMAP servers store mailboxes with a prefix (e.g. \"user\" for Cyrus which results in \"user.username\").")), 103 'ImapMailDomain' => array( 104 "Headline" => _("Mail domains"), 105 "Text" => _("Please enter a comma separated list of domain names (e.g. \"company.com,example.com\"). LAM will only manage mailboxes from these domains.")), 106 'ImapUserNameAttr' => array( 107 "Headline" => _("User name attribute"), 108 "Text" => _("Please choose the attribute to get the IMAP user name. The default is \"mail\" but you can also use \"uid\" or \"userPrincipalName\".")), 109 'MailAddress' => array( 110 "Headline" => _("Mailbox"), 111 "Text" => _("This mailbox will be created/deleted.")), 112 'ImapUserQuotaLimit' => array( 113 "Headline" => _("Quota"), 114 "Text" => _("Please enter the quota limit of this mailbox in kilobytes.")), 115 'pathSeparator' => array( 116 "Headline" => _("Path separator"), 117 "Text" => _("This is the separator for the mailbox path. Usually, this is \".\" but e.g. Cyrus with \"unixhierarchysep\" will require \"/\".")), 118 'initialFolders' => array( 119 "Headline" => _("Initial folders"), 120 "Text" => _("Use this to provide a list of folders (e.g. Trash) to add for new accounts.")), 121 'createMailbox' => array( 122 "Headline" => _("Create mailbox"), 123 "Text" => _('Set to "true" to create the mailbox.')), 124 ); 125 // configuration checks 126 $return['config_checks']['all']['ImapAccess_ImapServerAddress'] = array ( 127 'type' => 'ext_preg', 128 'regex' => 'DNSname', 129 'required' => true, 130 'required_message' => $this->messages['config'][0], 131 'error_message' => $this->messages['config'][0]); 132 $return['config_checks']['all']['ImapAccess_ImapDomain'] = array ( 133 'type' => 'regex_i', 134 'regex' => '[\\*a-z0-9\\._-]+(,[a-z0-9\\._-]+)*', 135 'required' => true, 136 'required_message' => $this->messages['config'][1], 137 'error_message' => $this->messages['config'][1]); 138 // profile options 139 $profileContainer = new htmlResponsiveRow(); 140 $profileContainer->add(new htmlResponsiveInputField(_('Quota'), 'ImapAccess_QuotaLimit', null, 'ImapUserQuotaLimit'), 12); 141 $return['profile_options'] = $profileContainer; 142 $return['profile_checks']['ImapAccess_QuotaLimit'] = array( 143 'type' => 'ext_preg', 144 'regex' => 'digit', 145 'error_message' => $this->messages['managemailbox'][8]); 146 return $return; 147 } 148 149 /** 150 * This function fills the error message array with messages 151 */ 152 function load_Messages() { 153 $this->messages['config'][0] = array('ERROR', _('Please enter a valid server name where the mailboxes reside.')); 154 $this->messages['config'][1] = array('ERROR', _('Please enter a correct list of valid mail domains.')); 155 $this->messages['config'][2] = array('ERROR', _('The IMAP admin password is empty.')); 156 $this->messages['managemailbox'][0] = array('ERROR', _('Unable to change ACL on IMAP server for mailbox deletion.')); 157 $this->messages['managemailbox'][1] = array('ERROR', _('Unable to delete mailbox from IMAP server.')); 158 $this->messages['managemailbox'][2] = array('ERROR', _('Unable to create mailbox on IMAP server.')); 159 $this->messages['managemailbox'][3] = array('ERROR', _('Unable to locate mailbox on IMAP.')); 160 $this->messages['managemailbox'][4] = array('ERROR', _('Your IMAP domains and email address domain do not match.')); 161 $this->messages['managemailbox'][5] = array('ERROR', _('Invalid password for IMAP admin or other problem occurred.')); 162 $this->messages['managemailbox'][6] = array('WARN', _('Your LAM login password was not accepted by the IMAP server.')); 163 $this->messages['managemailbox'][7] = array('ERROR', _('Cannot update quota.')); 164 $this->messages['managemailbox'][8] = array('ERROR', _('Wrong quota format. Quota must be numeric.')); 165 $this->messages['managemailbox'][9] = array('ERROR', _('Account %s:') . ' imapAccess_quota', _('Wrong quota format. Quota must be numeric.')); 166 $this->messages['managemailbox'][10] = array('ERROR', _('Cannot read quota.')); 167 $this->messages['createMailbox'][0] = array('ERROR', _('Account %s:') . ' imapAccess_createMailbox', _('This value can only be "true" or "false".')); 168 } 169 170 /** 171 * Extracts user name and email address from inetOrgPerson/posixAccount/windowsUser modules. 172 * 173 * @param array $attrs LDAP attributes (retrieved from other account modules if empty) 174 * @return htmlStatusMessage message if any 175 */ 176 private function extractUserAndEmail($attrs = null) { 177 $this->email = ''; 178 if ($attrs === null) { 179 if ($this->getAccountContainer()->getAccountModule('inetOrgPerson') != null) { 180 $attrs = $this->getAccountContainer()->getAccountModule('inetOrgPerson')->getAttributes(); 181 } 182 else { 183 $attrs = $this->getAccountContainer()->getAccountModule('windowsUser')->getAttributes(); 184 } 185 if ($this->getAccountContainer()->getAccountModule('posixAccount') != null) { 186 $attrs = array_merge($attrs, $this->getAccountContainer()->getAccountModule('posixAccount')->getAttributes()); 187 } 188 } 189 $this->email = !empty($attrs['mail'][0]) ? $attrs['mail'][0] : ''; 190 $this->user = ''; 191 // extract user name from email address 192 if (empty($this->moduleSettings['ImapAccess_UserNameAttribute'][0]) || $this->moduleSettings['ImapAccess_UserNameAttribute'][0] == 'mail') { 193 $email_parts = explode('@', $this->email, 2); 194 $this->user = array_shift($email_parts); 195 } 196 elseif ($this->moduleSettings['ImapAccess_UserNameAttribute'][0] == 'userPrincipalName') { 197 if (!empty($attrs['userPrincipalName'][0])) { 198 $parts = explode('@', $attrs['userPrincipalName'][0], 2); 199 $this->user = array_shift($parts); 200 } 201 } 202 // extract user name from Unix user name (might be in inetOrgPerson/windowUser or posixAccount module) 203 else { 204 $this->user = !empty($attrs['uid'][0]) ? $attrs['uid'][0] : ''; 205 } 206 207 if (empty($this->email)) { 208 $modName = ($this->getAccountContainer()->getAccountModule('inetOrgPerson') != null) ? 'inetOrgPerson' : 'windowsUser'; 209 return new htmlStatusMessage('INFO', _("Please enter an email address on this page: %s"), '', array($this->getAccountContainer()->getAccountModule($modName)->get_alias())); 210 } 211 } 212 213 /** 214 * @inheritDoc 215 */ 216 public function display_html_attributes() { 217 $return = new htmlResponsiveRow(); 218 if (!checkIfWriteAccessIsAllowed($this->get_scope())) { 219 return $return; 220 } 221 $msg = $this->extractUserAndEmail(); 222 if ($msg != null) { 223 $return->add($msg, 12); 224 return $return; 225 } 226 $prefix = $this->getMailboxPrefix(); 227 228 $email_domain = substr(strstr($this->email, '@'), 1); 229 $adminPassword = $this->getAdminPassword(); // check for password for fall back mechanism 230 if (!isset($_SESSION['imapAdmPass']) && !isset($adminPassword)) { 231 return $this->display_html_password(); 232 } 233 234 $return->addLabel(new htmlOutputText(_('Email address'))); 235 $return->addField(new htmlOutputText($this->email)); 236 237 $adminUser = $this->getAdminUser(); 238 $adminPassword = $this->getAdminPassword(); 239 try { 240 $client = $this->connect($adminUser, $adminPassword); 241 } 242 catch (LAMException $e) { 243 return $this->display_html_password(new htmlStatusMessage('ERROR', $e->getTitle(), $e->getMessage())); 244 } 245 246 $return->addLabel(new htmlOutputText(_('Mailbox'))); 247 $mailboxGroup = new htmlGroup(); 248 $mailboxGroup->addElement(new htmlOutputText($prefix . $this->getSep() . $this->user)); 249 $mailboxGroup->addElement(new htmlHelpLink('MailAddress')); 250 $return->addField($mailboxGroup); 251 $return->addVerticalSpacer('2rem'); 252 253 $list = $client->listMailboxes($prefix . $this->getSep() . $this->user, Horde_Imap_Client::MBOX_ALL); 254 if (is_array($list) && sizeof($list) == 1) { 255 $this->renderQuotasForMailbox($return, $client, $prefix . $this->getSep() . $this->user); 256 $return->addVerticalSpacer('2rem'); 257 $return->add(new htmlButton('deleteMailbox', _('Delete mailbox')), 12, 12, 12, 'text-center'); 258 } 259 else { 260 $mailboxMessage = new htmlOutputText(_("Mailbox does not exist on IMAP server.")); 261 $return->add($mailboxMessage, 12, 12, 12, 'text-center'); 262 $return->addVerticalSpacer('2rem'); 263 if ($this->isWrongDomain($email_domain)) { 264 $return->add(new htmlStatusMessage('INFO', $this->messages['managemailbox'][4][1]), 12); 265 $return->addVerticalSpacer('1rem'); 266 } 267 else { 268 $createButton = new htmlButton('createMailbox', _('Create mailbox')); 269 $return->add($createButton, 12, 12, 12, 'text-center'); 270 } 271 } 272 $client->logout(); 273 return $return; 274 } 275 276 /** 277 * Returns the HTML meta data for the password page. 278 * 279 * @param htmlStatusMessage|null $message status message 280 * @return htmlResponsiveRow HTML meta data 281 */ 282 function display_html_password($message = null) { 283 $return = new htmlResponsiveRow(); 284 if ($message !== null) { 285 $return->add($message, 12); 286 $return->addVerticalSpacer('1rem'); 287 } 288 $passwordInput = new htmlResponsiveInputField(_("Password of IMAP admin user"), 'ImapAdminPassword', '', 'ImapAdminPassword_Sess'); 289 $passwordInput->setIsPassword(true); 290 $passwordInput->setRequired(true); 291 $passwordInput->setOnKeyPress('SubmitForm(\'enterPasswordButton\', event);'); 292 $return->add($passwordInput, 12); 293 $return->addVerticalSpacer('2rem'); 294 $return->add(new htmlButton('enterPasswordButton', _('Ok')), 12, 12, 12, 'text-center'); 295 return $return; 296 } 297 298 /** 299 * Display the mailbox quota. 300 * 301 * @param htmlResponsiveRow $container structure that contained information to be displayed 302 * @param Horde_Imap_Client_Socket $client IMAP client 303 * @param String $username user name to connect to IMAP server 304 * @return htmlResponsiveRow table with added information about user quotas or controls to add quota 305 */ 306 function renderQuotasForMailbox($container, $client, $username) { 307 if (($this->profileQuotaLimit != null) && ($this->profileQuotaLimit != '')) { 308 $client->setQuota($username, array('storage' => $this->profileQuotaLimit)); 309 $this->profileQuotaLimit = null; 310 } 311 try { 312 $quotaRoot = $client->getQuotaRoot($username); 313 $quota_values = array(); 314 if (!empty($quotaRoot)) { 315 $quota_values = $client->getQuota($username); 316 } 317 if (!empty($quota_values)) { 318 if (isset($quota_values['storage']) && is_array($quota_values['storage'])) { 319 $quotaLimit = $quota_values['storage']['limit']; 320 $container->addLabel(new htmlOutputText(_("Current usage (kB)"))); 321 $container->addField(new htmlOutputText($quota_values['storage']['usage']), true); 322 $quotaLimitInput = new htmlResponsiveInputField(_("Quota limit (kB)"), 'ImapUserQuotaLimit', $quotaLimit, 'ImapUserQuotaLimit'); 323 $container->add($quotaLimitInput, 12); 324 $container->addVerticalSpacer('2rem'); 325 $container->add(new htmlButton('updateQuota', _('Update quota')), 12, 12, 12, 'text-center'); 326 $container->addVerticalSpacer('1rem'); 327 } 328 } 329 else { 330 $quotaLimit = ""; 331 $quotaLimitInput = new htmlResponsiveInputField(_("Quota limit (kB)"), 'ImapUserQuotaLimit', $quotaLimit, 'ImapUserQuotaLimit'); 332 $container->add($quotaLimitInput, 12); 333 $container->addVerticalSpacer('2rem'); 334 $container->add(new htmlButton('updateQuota', _('Update quota')), 12, 12, 12, 'text-center'); 335 $container->addVerticalSpacer('1rem'); 336 } 337 } 338 catch (Exception $e) { 339 $container->add(new htmlStatusMessage('ERROR', $this->messages['managemailbox'][10][1], $e->getMessage()), 12); 340 } 341 return $container; 342 } 343 344 /** 345 * Processes user input of the primary module page. 346 * It checks if all input values are correct and updates the associated LDAP attributes. 347 * 348 * @return array list of info/error messages 349 */ 350 function process_attributes() { 351 $errors = array(); 352 if (!checkIfWriteAccessIsAllowed($this->get_scope())) { 353 return $errors; 354 } 355 $prefix = $this->getMailboxPrefix(); 356 357 $adminUser = $this->getAdminUser(); 358 if (isset($_POST['ImapAdminPassword']) && isset($_POST['enterPasswordButton'])) { 359 $errors = $this->doLogin(); 360 } 361 $adminPassword = $this->getAdminPassword(); 362 363 try { 364 $client = $this->connect($adminUser, $adminPassword); 365 $this->extractUserAndEmail(); 366 $email_domain = substr(strstr($this->email, '@'), 1); 367 368 if (isset($_POST['deleteMailbox'])) { 369 if ($this->isWrongDomain($email_domain)) { 370 $errors[] = $this->messages['managemailbox'][4]; 371 } 372 else { 373 $root = $prefix . $this->getSep() . $this->user; 374 try { 375 $client->setACL($root, $adminUser, array('rights' => 'c', 'action' => 'add')); 376 $client->deleteMailbox($root); 377 } 378 catch (Exception $e) { 379 $message = $this->messages['managemailbox'][1]; 380 $message[] = $e->getMessage(); 381 $errors[] = $message; 382 } 383 } 384 } 385 386 if (isset($_POST['createMailbox'])) { 387 $createMessages = $this->createMailbox($client, $this->user, $email_domain); 388 $errors = array_merge($errors, $createMessages); 389 } 390 if (isset($_POST['updateQuota'])) { 391 $quota = $_POST['ImapUserQuotaLimit']; 392 $quotaMessages = $this->setQuota($client, $this->user, $email_domain, $quota); 393 $errors = array_merge($errors, $quotaMessages); 394 } 395 $client->logout(); 396 } 397 catch (LAMException $e) { 398 return array(array('ERROR', $e->getTitle(), $e->getMessage())); 399 } 400 // Return error-messages 401 return $errors; 402 } 403 404 /** 405 * Creates the mailbox for a user. 406 * 407 * @param Horde_Imap_Client_Socket $client IMAP client 408 * @param string $userName user name 409 * @param string $email_domain email domain 410 * @return array error messages 411 */ 412 private function createMailbox($client, $userName, $email_domain) { 413 $errors = array(); 414 $prefix = $this->getMailboxPrefix(); 415 if ($this->isWrongDomain($email_domain)) { 416 $errors[] = $this->messages['managemailbox'][4]; 417 } 418 else { 419 $root = $prefix . $this->getSep() . $userName; 420 logNewMessage(LOG_DEBUG, 'Creating mailbox: ' . $root); 421 try { 422 $client->createMailbox($root); 423 logNewMessage(LOG_DEBUG, 'Mailbox created'); 424 $list = $client->listMailboxes($root, Horde_Imap_Client::MBOX_ALL); 425 if (empty($list)) { 426 $errors[] = $this->messages['managemailbox'][3]; 427 return $errors; 428 } 429 // create initial folders 430 foreach ($this->getInitialFolders() as $folder) { 431 $fullFolderName = $root . $this->getSep() . $folder; 432 logNewMessage(LOG_DEBUG, 'Creating folder: ' . $fullFolderName); 433 $client->createMailbox($fullFolderName); 434 logNewMessage(LOG_DEBUG, 'Folder created: ' . $fullFolderName); 435 } 436 } 437 catch (Exception $e) { 438 $message = $this->messages['managemailbox'][2]; 439 $message[] = $e->getMessage(); 440 $errors[] = $message; 441 } 442 } 443 return $errors; 444 } 445 446 /** 447 * Sets the mailbox quota for a user. 448 * 449 * @param Horde_Imap_Client_Socket $client IMAP client 450 * @param string $userName user name 451 * @param string $email_domain email domain 452 * @param string $quota mailbox quota 453 * @return array error messages 454 */ 455 private function setQuota($client, $userName, $email_domain, $quota) { 456 $prefix = $this->getMailboxPrefix(); 457 $errors = array(); 458 $root = $prefix . $this->getSep() . $userName; 459 if ($this->isWrongDomain($email_domain)) { 460 $errors[] = $this->messages['managemailbox'][4]; 461 } 462 else { 463 if ($quota == '') { 464 try { 465 $client->setQuota($root, array('storage' => '-1')); 466 } 467 catch (Exception $e) { 468 $message = $this->messages['managemailbox'][7]; 469 $message[] = $e->getMessage(); 470 $errors[] = $message; 471 } 472 } 473 elseif (get_preg($quota, 'digit')) { 474 logNewMessage(LOG_DEBUG, 'Setting quota ' . $quota . ' for ' . $root); 475 try { 476 $client->setQuota($root, array('storage' => $quota)); 477 } 478 catch (Exception $e) { 479 $message = $this->messages['managemailbox'][7]; 480 $message[] = $e->getMessage(); 481 $errors[] = $message; 482 } 483 } 484 else { 485 $errors[] = $this->messages['managemailbox'][8]; 486 } 487 } 488 return $errors; 489 } 490 491 /** 492 * Loads the values of an account profile into internal variables. 493 * 494 * @param array $profile hash array with profile values (identifier => value) 495 */ 496 function load_profile($profile) { 497 // profile mappings in meta data 498 parent::load_profile($profile); 499 if (isset($profile['ImapAccess_QuotaLimit'][0]) && $profile['ImapAccess_QuotaLimit'][0] != '') { 500 $this->profileQuotaLimit = $profile['ImapAccess_QuotaLimit'][0]; 501 } 502 } 503 504 /** 505 * Returns a list of configuration options. 506 * 507 * @param array $scopes account types (user, group, host) 508 * @param array $allScopes list of all active account modules and their scopes (module => array(scopes)) 509 * @return mixed htmlElement or array of htmlElement 510 * 511 * @see htmlElement 512 */ 513 public function get_configOptions($scopes, $allScopes) { 514 // configuration settings 515 $configContainer = new htmlResponsiveRow(); 516 $configServer = new htmlResponsiveInputField(_('Server address'), 'ImapAccess_ImapServerAddress', '', 'ImapServerAddress'); 517 $configServer->setRequired(true); 518 $configContainer->add($configServer, 12); 519 $configContainer->add(new htmlResponsiveSelect('ImapAccess_ImapServerEncriptionProtocol', array('TLS', 'SSL'), array('TLS'), _("Encryption protocol"), 'ImapServerEncryptionProtocol'), 12); 520 $configCertValidate = new htmlResponsiveSelect('ImapAccess_ImapValidateServerCert', array(_('Yes') => 'validate-cert', _('No') => 'novalidate-cert'), array('validate-cert'), _("Validate server certificate"), 'ImapValidateServerCert'); 521 $configCertValidate->setHasDescriptiveElements(true); 522 $configContainer->add($configCertValidate, 12); 523 $configUser = new htmlResponsiveInputField(_('IMAP admin user'), 'ImapAccess_ImapAdmin', '', 'ImapAdmin'); 524 $configUser->setRequired(true); 525 $configContainer->add($configUser, 12); 526 $pwdSelectOptions = array( 527 _('LAM user password') => 'lam_user_pass', 528 _('Ask') => 'ask_pass', 529 _('Server profile') => 'config'); 530 $configPasswordType = new htmlResponsiveSelect('ImapAccess_ImapAdminPasswordSelect', $pwdSelectOptions, array('ask_pass'), _("IMAP password input"), 'ImapAdminPasswordSelect'); 531 $configPasswordType->setHasDescriptiveElements(true); 532 $configPasswordType->setTableRowsToShow(array('config' => array('ImapAccess_ImapAdminPassword'))); 533 $configPasswordType->setTableRowsToHide(array('lam_user_pass' => array('ImapAccess_ImapAdminPassword'), 'ask_pass' => array('ImapAccess_ImapAdminPassword'))); 534 $configContainer->add($configPasswordType, 12); 535 $adminPwdInput = new htmlResponsiveInputField(_('Admin password'), 'ImapAccess_ImapAdminPassword', null, 'ImapAdminPasswordSelect'); 536 $adminPwdInput->setIsPassword(true); 537 $adminPwdInput->setObfuscate(true); 538 $configContainer->add($adminPwdInput, 12); 539 $mailDomainsInput = new htmlResponsiveInputField(_('Mail domains'), 'ImapAccess_ImapDomain', '', 'ImapMailDomain'); 540 $mailDomainsInput->setRequired(true); 541 $configContainer->add($mailDomainsInput, 12); 542 $configContainer->add(new htmlResponsiveInputField(_('Prefix for mailboxes'), 'ImapAccess_ImapUserPrefix', '', 'ImapUserPrefix'), 12); 543 $configContainer->add(new htmlResponsiveInputTextarea('ImapAccess_initialFolders', '', 10, 3, _('Initial folders'), 'initialFolders'), 12); 544 $configUserName = new htmlResponsiveSelect('ImapAccess_UserNameAttribute', array('mail', 'uid', 'userPrincipalName'), array('mail'), _("User name attribute"), 'ImapUserNameAttr'); 545 $configContainer->add($configUserName, 12); 546 $configPathSeparator = new htmlResponsiveSelect('ImapAccess_pathSeparator', array('.', '/'), array('.'), _("Path separator"), 'pathSeparator'); 547 $configContainer->add($configPathSeparator, 12); 548 return $configContainer; 549 } 550 551 /** 552 * {@inheritDoc} 553 * @see baseModule::check_configOptions() 554 */ 555 public function check_configOptions($typeIds, &$options) { 556 $errors = parent::check_configOptions($typeIds, $options); 557 if (($options['ImapAccess_ImapAdminPasswordSelect'][0] == 'config') 558 && empty($options['ImapAccess_ImapAdminPassword'][0])) { 559 $errors[] = $this->messages['config'][2]; 560 } 561 return $errors; 562 } 563 564 /** 565 * Returns the user name of the IMAP admin. 566 * 567 * @return String admin user name 568 */ 569 private function getAdminUser() { 570 if (isset($_SESSION['imapAdmUser'])) { 571 return $_SESSION['imapAdmUser']; 572 } 573 $user = $this->moduleSettings['ImapAccess_ImapAdmin'][0]; 574 // check if user name contains any wildcards that need to be replaced with LDAP attribute values 575 $matches = array(); 576 preg_match_all('/\\$[a-z0-9_-]+\\$/i', $this->moduleSettings['ImapAccess_ImapAdmin'][0], $matches); 577 if (sizeof($matches) > 0) { 578 // find wildcards 579 $attrNames = array(); 580 foreach ($matches as $match) { 581 foreach ($match as $attr) { 582 $attrNames[] = substr($attr, 1, -1); 583 } 584 } 585 $attrNames = array_values(array_unique($attrNames)); 586 $attrNames = array_change_key_case($attrNames, CASE_LOWER); 587 // read LAM login user data 588 $dn = $_SESSION['ldap']->getUserName(); 589 $sr = @ldap_read($_SESSION['ldap']->server(), $dn, '(objectclass=*)', $attrNames, 0, 0, 0, LDAP_DEREF_NEVER); 590 if ($sr) { 591 $info = @ldap_get_entries($_SESSION['ldap']->server(), $sr); 592 if ($info) { 593 cleanLDAPResult($info); 594 $info = $info[0]; 595 } 596 } 597 // replace wildcards 598 foreach ($attrNames as $attr) { 599 if (empty($info[$attr])) { 600 continue; 601 } 602 $user = preg_replace('/\\$' . $attr . '\\$/i', $info[$attr][0], $user); 603 } 604 } 605 logNewMessage(LOG_DEBUG, 'IMAP admin user: ' . $user); 606 $_SESSION['imapAdmUser'] = $user; 607 return $user; 608 } 609 610 /** 611 * Returns the admin password. 612 * 613 * @return String password 614 */ 615 private function getAdminPassword() { 616 //perform admin password 617 $password = null; //default value is null, it can be changed during the work 618 if (isset($_SESSION['imapAdmPass'])) { 619 $password = lamDecrypt($_SESSION['imapAdmPass']); 620 } 621 elseif (isset($this->moduleSettings['ImapAccess_ImapAdminPasswordSelect'][0]) && ($this->moduleSettings['ImapAccess_ImapAdminPasswordSelect'][0] == "lam_user_pass")) { 622 $password = $_SESSION['ldap']->getPassword(); 623 } 624 elseif (!empty($this->moduleSettings['ImapAccess_ImapAdminPasswordSelect'][0]) && ($this->moduleSettings['ImapAccess_ImapAdminPasswordSelect'][0] == "config") 625 && !empty($this->moduleSettings['ImapAccess_ImapAdminPassword'][0])) { 626 $password = deobfuscateText($this->moduleSettings['ImapAccess_ImapAdminPassword'][0]); 627 } 628 return $password; 629 } 630 631 /** 632 * Checks the password given by user and save it as session parameter. 633 * 634 * @return array list of error messages 635 */ 636 function doLogin() { 637 $errors = array(); 638 $adminUser = $this->getAdminUser(); 639 if (isset($_POST['ImapAdminPassword']) && $_POST['ImapAdminPassword'] != "") { 640 $adminPassword = $_POST['ImapAdminPassword']; 641 try { 642 $client = $this->connect($adminUser, $adminPassword); 643 $_SESSION['imapAdmPass'] = lamEncrypt($_POST['ImapAdminPassword']); 644 $client->logout(); 645 } 646 catch (LAMException $e) { 647 $error = $this->messages['managemailbox'][5]; 648 $error[] = $e->getMessage(); 649 $errors[] = $error; 650 } 651 } 652 return $errors; 653 } 654 655 /** 656 * Connects to the IMAP server. 657 * 658 * @param string $user user name 659 * @param string $password password 660 * @return Horde_Imap_Client_Socket IMAP client 661 */ 662 private function connect($user, $password) { 663 include_once __DIR__ . '/../3rdParty/composer/autoload.php'; 664 $encryptionType = $this->moduleSettings['ImapAccess_ImapServerEncriptionProtocol'][0]; 665 if (strrpos($this->moduleSettings['ImapAccess_ImapServerAddress'][0], ":")) { 666 $port = substr(strstr($this->moduleSettings['ImapAccess_ImapServerAddress'][0], ':'), 1); 667 $serverName = array_shift(explode(':', $this->moduleSettings['ImapAccess_ImapServerAddress'][0], 2)); 668 } 669 else { 670 $serverName = $this->moduleSettings['ImapAccess_ImapServerAddress'][0]; 671 if ($encryptionType === "TLS") { 672 $port = 143; 673 } 674 else { 675 $port = 993; 676 } 677 } 678 $context = array( 679 'ssl' => array( 680 'cafile' => __DIR__ . '/../../serverCerts.pem' 681 ) 682 ); 683 if (isset($this->moduleSettings['ImapAccess_ImapValidateServerCert'][0]) && ($this->moduleSettings['ImapAccess_ImapValidateServerCert'][0] == 'novalidate-cert')) { 684 $context['ssl']['verify_peer'] = false; 685 $context['ssl']['verify_peer_name'] = false; 686 } 687 try { 688 $client = new Horde_Imap_Client_Socket(array( 689 'username' => $user, 690 'password' => $password, 691 'hostspec' => $serverName, 692 'port' => $port, 693 'secure' => strtolower($encryptionType), 694 'context' => $context 695 )); 696 $client->login(); 697 return $client; 698 } 699 catch (Exception $e) { 700 throw new LAMException(_('Unable to connect to IMAP server.'), $e->getMessage(), $e); 701 } 702 } 703 704 /** 705 * This function returns the prefix for mailboxes. 706 * If no prefix was given during configuration then "user" will be used (default for Cyrus). 707 * 708 * @return String prefix 709 */ 710 function getMailboxPrefix() { 711 if (!isset($this->moduleSettings['ImapAccess_ImapUserPrefix'][0]) || ($this->moduleSettings['ImapAccess_ImapUserPrefix'][0] == '')) { 712 return "user"; 713 } 714 else { 715 return $this->moduleSettings['ImapAccess_ImapUserPrefix'][0]; 716 } 717 } 718 719 /** 720 * This function checks if the domain of the mailbox is not in the list of domains listed in the configuration. 721 * If it is in the list then it returns false, otherwise returns true. If the list of domains is not set then it returns true. 722 * 723 * @param String $email_domain email domain 724 * @return boolean true if domains match 725 */ 726 function isWrongDomain($email_domain) { 727 if (isset($this->moduleSettings['ImapAccess_ImapDomain'][0])) { 728 $domain_list_string = $this->moduleSettings['ImapAccess_ImapDomain'][0]; 729 if ($domain_list_string == '*') { 730 return false; 731 } 732 $domains_array = explode(",", $domain_list_string); 733 if ((sizeof($domains_array) == 0) || in_array($email_domain, $domains_array)) { 734 return false; 735 } 736 } 737 else { 738 return false; 739 } 740 return true; 741 } 742 743 /** 744 * Returns the path separator. 745 * 746 * @return String separator char 747 */ 748 private function getSep() { 749 if (isset($this->moduleSettings['ImapAccess_pathSeparator'][0])) { 750 return $this->moduleSettings['ImapAccess_pathSeparator'][0]; 751 } 752 return '.'; // default 753 } 754 755 /** 756 * Returns the list of initial folders to create for a new mailbox. 757 * 758 * @return array list of folders 759 */ 760 private function getInitialFolders() { 761 $list = array(); 762 if (!empty($this->moduleSettings['ImapAccess_initialFolders'])) { 763 foreach ($this->moduleSettings['ImapAccess_initialFolders'] as $folder) { 764 $folder = trim($folder); 765 if (!empty($folder)) { 766 $list[] = $folder; 767 } 768 } 769 } 770 return $list; 771 } 772 773 /** 774 * {@inheritDoc} 775 * @see baseModule::get_uploadColumns() 776 */ 777 public function get_uploadColumns($selectedModules, &$type) { 778 $pwd = $this->getAdminPassword(); 779 if (empty($pwd)) { 780 return array(); 781 } 782 return array( 783 array( 784 'name' => 'imapAccess_createMailbox', 785 'description' => _('Create mailbox'), 786 'example' => 'false', 787 'default' => 'false', 788 'values' => 'true, false', 789 'help' => 'createMailbox' 790 ), 791 array( 792 'name' => 'imapAccess_quota', 793 'description' => _('Quota limit (kB)'), 794 'example' => '1000000', 795 'help' => 'ImapUserQuotaLimit' 796 ), 797 ); 798 } 799 800 /** 801 * {@inheritDoc} 802 * @see baseModule::build_uploadAccounts() 803 */ 804 public function build_uploadAccounts($rawAccounts, $ids, &$partialAccounts, $selectedModules, &$type) { 805 $errors = array(); 806 if (!isset($ids['imapAccess_createMailbox'])) { 807 return $errors; 808 } 809 for ($i = 0; $i < sizeof($rawAccounts); $i++) { 810 if (isset($rawAccounts[$i][$ids['imapAccess_createMailbox']]) 811 && !in_array($rawAccounts[$i][$ids['imapAccess_createMailbox']], array('true', 'false'))) { 812 $errMsg = $this->messages['createMailbox'][0]; 813 array_push($errMsg, array($i)); 814 $errors[] = $errMsg; 815 } 816 if (isset($rawAccounts[$i][$ids['imapAccess_createMailbox']]) 817 && ($rawAccounts[$i][$ids['imapAccess_createMailbox']] === 'true') 818 && !empty($ids['imapAccess_quota']) 819 && isset($rawAccounts[$i][$ids['imapAccess_quota']]) 820 && !get_preg($rawAccounts[$i][$ids['imapAccess_quota']], 'digit')) { 821 $errMsg = $this->messages['managemailbox'][9]; 822 array_push($errMsg, array($i)); 823 $errors[] = $errMsg; 824 } 825 } 826 return $errors; 827 } 828 829 /** 830 * {@inheritDoc} 831 * @see baseModule::doUploadPostActions() 832 */ 833 function doUploadPostActions(&$data, $ids, $failed, &$temp, &$accounts, $selectedModules, $type) { 834 if (!checkIfWriteAccessIsAllowed($this->get_scope())) { 835 die(); 836 } 837 // on first call generate list of IMAP operations 838 if (!isset($temp['counter'])) { 839 $temp['users'] = array(); 840 $temp['counter'] = 0; 841 $errors = array(); 842 if (isset($ids['imapAccess_createMailbox'])) { 843 foreach ($data as $i => $dataRow) { 844 if (in_array($i, $failed)) { 845 continue; // ignore failed accounts 846 } 847 if ($dataRow[$ids['imapAccess_createMailbox']] === 'true') { 848 $limit = ''; 849 if (isset($ids['imapAccess_quota']) 850 && isset($dataRow[$ids['imapAccess_quota']]) 851 && ($dataRow[$ids['imapAccess_quota']] !== '')) { 852 $limit = $dataRow[$ids['imapAccess_quota']]; 853 } 854 $attributes = $accounts[$i]; 855 foreach ($attributes as $name => $value) { 856 if (!is_array($value)) { 857 $attributes[$name] = array($value); 858 } 859 } 860 $extractErrors = $this->extractUserAndEmail($attributes); 861 if (!empty($extractErrors)) { 862 $errors = array_merge($errors, $extractErrors); 863 } 864 $temp['users'][] = array( 865 'uid' => $this->user, 866 'limit' => $limit, 867 'email' => substr(strstr($this->email, '@'), 1) 868 ); 869 } 870 } 871 } 872 return array( 873 'status' => 'inProgress', 874 'progress' => 0, 875 'errors' => $errors 876 ); 877 } 878 // add mailbox 879 elseif ($temp['counter'] < sizeof($temp['users'])) { 880 $errors = array(); 881 $data = $temp['users'][$temp['counter']]; 882 $uid = $data['uid']; 883 $limit = $data['limit']; 884 $email_domain = $data['email']; 885 ob_start(); 886 $imapUser = $this->getAdminUser(); 887 $imapPassword = $this->getAdminPassword(); 888 try { 889 $client = $this->connect($imapUser, $imapPassword); 890 $prefix = $this->getMailboxPrefix(); 891 $list = $client->listMailboxes($prefix . $this->getSep() . $uid); 892 if (empty($list)) { 893 $createErrors = $this->createMailbox($client, $uid, $email_domain); 894 $errors = array_merge($errors, $createErrors); 895 if (empty($createErrors)) { 896 $quotaErrors = $this->setQuota($client, $uid, $email_domain, $limit); 897 $errors = array_merge($errors, $quotaErrors); 898 } 899 } 900 $client->logout(); 901 } 902 catch (Exception $e) { 903 $message = $this->messages['managemailbox'][5]; 904 $message[] = $e->getMessage(); 905 $errors[] = $message; 906 } 907 ob_end_clean(); 908 $temp['counter']++; 909 return array ( 910 'status' => 'inProgress', 911 'progress' => ($temp['counter'] * 100) / sizeof($temp['users']), 912 'errors' => $errors 913 ); 914 } 915 // all modifications are done 916 else { 917 return array ( 918 'status' => 'finished', 919 'progress' => 100, 920 'errors' => array() 921 ); 922 } 923 } 924 925} 926