1<?php 2/********************************************************************* 3 class.email.php 4 5 Peter Rotich <peter@osticket.com> 6 Copyright (c) 2006-2013 osTicket 7 http://www.osticket.com 8 9 Released under the GNU General Public License WITHOUT ANY WARRANTY. 10 See LICENSE.TXT for details. 11 12 vim: expandtab sw=4 ts=4 sts=4: 13**********************************************************************/ 14include_once INCLUDE_DIR.'class.role.php'; 15include_once(INCLUDE_DIR.'class.dept.php'); 16include_once(INCLUDE_DIR.'class.mailfetch.php'); 17 18class Email extends VerySimpleModel { 19 static $meta = array( 20 'table' => EMAIL_TABLE, 21 'pk' => array('email_id'), 22 'joins' => array( 23 'priority' => array( 24 'constraint' => array('priority_id' => 'Priority.priority_id'), 25 'null' => true, 26 ), 27 'dept' => array( 28 'constraint' => array('dept_id' => 'Dept.id'), 29 'null' => true, 30 ), 31 'topic' => array( 32 'constraint' => array('topic_id' => 'Topic.topic_id'), 33 'null' => true, 34 ), 35 ) 36 ); 37 38 const PERM_BANLIST = 'emails.banlist'; 39 40 static protected $perms = array( 41 self::PERM_BANLIST => array( 42 'title' => 43 /* @trans */ 'Banlist', 44 'desc' => 45 /* @trans */ 'Ability to add/remove emails from banlist via ticket interface', 46 'primary' => true, 47 )); 48 49 50 var $address; 51 var $mail_proto; 52 53 function getId() { 54 return $this->email_id; 55 } 56 57 function __toString() { 58 if ($this->name) 59 return sprintf('%s <%s>', $this->name, $this->email); 60 61 return $this->email; 62 } 63 64 65 function __onload() { 66 $this->mail_proto = $this->get('mail_protocol'); 67 if ($this->mail_encryption == 'SSL') 68 $this->mail_proto .= "/".$this->mail_encryption; 69 70 $this->address=$this->name?($this->name.'<'.$this->email.'>'):$this->email; 71 } 72 73 function getEmail() { 74 return $this->email; 75 } 76 77 function getAddress() { 78 return $this->address; 79 } 80 81 function getName() { 82 return $this->name; 83 } 84 85 function getPriorityId() { 86 return $this->priority_id; 87 } 88 89 function getDeptId() { 90 return $this->dept_id; 91 } 92 93 function getDept() { 94 return $this->dept; 95 } 96 97 function getTopicId() { 98 return $this->topic_id; 99 } 100 101 function getTopic() { 102 return $this->topic; 103 } 104 105 function autoRespond() { 106 return !$this->noautoresp; 107 } 108 109 function getPasswd() { 110 if (!$this->userpass) 111 return ''; 112 return Crypto::decrypt($this->userpass, SECRET_SALT, $this->userid); 113 } 114 115 function getSMTPPasswd() { 116 if (!$this->smtp_userpass) 117 return ''; 118 return Crypto::decrypt($this->smtp_userpass, SECRET_SALT, $this->smtp_userid); 119 } 120 121 function getHashtable() { 122 return $this->ht; 123 } 124 125 function getInfo() { 126 $base = $this->getHashtable(); 127 $base['mail_proto'] = $this->mail_protocol; 128 if ($this->mail_encryption != 'NONE') 129 $base['mail_proto'] .= "/{$this->mail_encryption}"; 130 return $base; 131 } 132 133 function getMailAccountInfo() { 134 135 /*NOTE: Do not change any of the tags - otherwise mail fetching will fail */ 136 $info = array( 137 //Mail server info 138 'host' => $this->mail_host, 139 'port' => $this->mail_port, 140 'protocol' => $this->mail_protocol, 141 'encryption' => $this->mail_encryption, 142 'username' => $this->userid, 143 'password' => Crypto::decrypt($this->userpass, SECRET_SALT, $this->userid), 144 //osTicket specific 145 'email_id' => $this->getId(), //Required for email routing to work. 146 'max_fetch' => $this->mail_fetchmax, 147 'folder' => $this->mail_folder, 148 'delete_mail' => $this->mail_delete, 149 'archive_folder' => $this->mail_archivefolder 150 ); 151 152 return $info; 153 } 154 155 function isSMTPEnabled() { 156 157 return ( 158 $this->smtp_active 159 && ($info=$this->getSMTPInfo()) 160 && (!$info['auth'] || $info['password']) 161 ); 162 } 163 164 function allowSpoofing() { 165 return ($this->smtp_spoofing); 166 } 167 168 function getSMTPInfo() { 169 $smtpcreds = $this->smtp_auth_creds; 170 $username = $smtpcreds ? $this->smtp_userid : $this->userid; 171 $passwd = $smtpcreds ? $this->smtp_userpass : $this->userpass; 172 173 $info = array ( 174 'host' => $this->smtp_host, 175 'port' => $this->smtp_port, 176 'auth' => (bool) $this->smtp_auth, 177 'username' => $username, 178 'password' => Crypto::decrypt($passwd, SECRET_SALT, $username) 179 ); 180 181 return $info; 182 } 183 184 function send($to, $subject, $message, $attachments=null, $options=null, $cc=array()) { 185 186 $mailer = new Mailer($this); 187 if($attachments) 188 $mailer->addAttachments($attachments); 189 190 return $mailer->send($to, $subject, $message, $options, $cc); 191 } 192 193 function sendAutoReply($to, $subject, $message, $attachments=null, $options=array()) { 194 $options+= array('autoreply' => true); 195 return $this->send($to, $subject, $message, $attachments, $options); 196 } 197 198 function sendAlert($to, $subject, $message, $attachments=null, $options=array()) { 199 $options+= array('notice' => true); 200 return $this->send($to, $subject, $message, $attachments, $options); 201 } 202 203 function delete() { 204 global $cfg; 205 //Make sure we are not trying to delete default emails. 206 if(!$cfg || $this->getId()==$cfg->getDefaultEmailId() || $this->getId()==$cfg->getAlertEmailId()) //double...double check. 207 return 0; 208 209 if (!parent::delete()) 210 return false; 211 212 $type = array('type' => 'deleted'); 213 Signal::send('object.deleted', $this, $type); 214 215 Dept::objects() 216 ->filter(array('email_id' => $this->getId())) 217 ->update(array( 218 'email_id' => $cfg->getDefaultEmailId() 219 )); 220 221 Dept::objects() 222 ->filter(array('autoresp_email_id' => $this->getId())) 223 ->update(array( 224 'autoresp_email_id' => 0, 225 )); 226 227 return true; 228 } 229 230 231 /******* Static functions ************/ 232 233 static function getIdByEmail($email) { 234 $qs = static::objects()->filter(Q::any(array( 235 'email' => $email, 236 'userid' => $email 237 ))) 238 ->values_flat('email_id'); 239 240 $row = $qs->first(); 241 return $row ? $row[0] : false; 242 } 243 244 static function create($vars=false) { 245 $inst = new static($vars); 246 $inst->created = SqlFunction::NOW(); 247 return $inst; 248 } 249 250 function save($refetch=false) { 251 if ($this->dirty) 252 $this->updated = SqlFunction::NOW(); 253 return parent::save($refetch || $this->dirty); 254 } 255 256 function update($vars, &$errors=false) { 257 global $cfg; 258 259 // very basic checks 260 $vars['cpasswd']=$this->getPasswd(); //Current decrypted password. 261 $vars['smtp_cpasswd']=$this->getSMTPPasswd(); // Current decrypted SMTP password. 262 $vars['name']=Format::striptags(trim($vars['name'])); 263 $vars['email']=trim($vars['email']); 264 $vars['mail_folder']=Format::striptags(trim($vars['mail_folder'])); 265 266 $id = isset($this->email_id) ? $this->getId() : 0; 267 if($id && $id!=$vars['id']) 268 $errors['err']=__('Get technical help!') 269 .' '.__('Internal error occurred'); 270 271 if(!$vars['email'] || !Validator::is_email($vars['email'])) { 272 $errors['email']=__('Valid email required'); 273 }elseif(($eid=Email::getIdByEmail($vars['email'])) && $eid!=$id) { 274 $errors['email']=__('Email already exists'); 275 }elseif($cfg && !strcasecmp($cfg->getAdminEmail(), $vars['email'])) { 276 $errors['email']=__('Email already used as admin email!'); 277 }elseif(Staff::getIdByEmail($vars['email'])) { //make sure the email doesn't belong to any of the staff 278 $errors['email']=__('Email in use by an agent'); 279 } 280 281 if(!$vars['name']) 282 $errors['name']=__('Email name required'); 283 284 $dept = Dept::lookup($vars['dept_id']); 285 if($dept && !$dept->isActive()) 286 $errors['dept_id'] = ''; 287 288 $topic = Topic::lookup($vars['topic_id']); 289 if($topic && !$topic->isActive()) 290 $errors['topic_id'] = ''; 291 292 // Validate Credentials 293 if ($vars['mail_active'] || ($vars['smtp_active'] && $vars['smtp_auth'] 294 && !$vars['smtp_auth_creds'])) 295 $errors = self::validateCredentials($vars['userid'], $vars['passwd'], $id, $errors, false); 296 297 if ($vars['smtp_active'] && $vars['smtp_auth'] && $vars['smtp_auth_creds']) 298 $errors = self::validateCredentials($vars['smtp_userid'], $vars['smtp_passwd'], null, $errors, true); 299 300 list($vars['mail_protocol'], $encryption) = explode('/', $vars['mail_proto']); 301 $vars['mail_encryption'] = $encryption ?: 'NONE'; 302 303 if($vars['mail_active']) { 304 //Check pop/imapinfo only when enabled. 305 if(!function_exists('imap_open')) 306 $errors['mail_active']= __("IMAP doesn't exist. PHP must be compiled with IMAP enabled."); 307 if(!$vars['mail_host']) 308 $errors['mail_host']=__('Host name required'); 309 if(!$vars['mail_port']) 310 $errors['mail_port']=__('Port required'); 311 if(!$vars['mail_protocol']) 312 $errors['mail_protocol']=__('Select protocol'); 313 if(!$vars['mail_fetchfreq'] || !is_numeric($vars['mail_fetchfreq'])) 314 $errors['mail_fetchfreq']=__('Fetch interval required'); 315 if(!$vars['mail_fetchmax'] || !is_numeric($vars['mail_fetchmax'])) 316 $errors['mail_fetchmax']=__('Maximum emails required'); 317 318 if($vars['mail_protocol'] == 'POP' && !empty($vars['mail_folder'])) 319 $errors['mail_folder'] = __('POP mail servers do not support folders'); 320 321 if(!isset($vars['postfetch'])) 322 $errors['postfetch']=__('Indicate what to do with fetched emails'); 323 elseif(!strcasecmp($vars['postfetch'],'archive')) { 324 if ($vars['mail_protocol'] == 'POP') 325 $errors['postfetch'] = __('POP mail servers do not support folders'); 326 elseif (!$vars['mail_archivefolder']) 327 $errors['postfetch'] = __('Valid folder required'); 328 } 329 } 330 331 if($vars['smtp_active']) { 332 if(!$vars['smtp_host']) 333 $errors['smtp_host']=__('Host name required'); 334 if(!$vars['smtp_port']) 335 $errors['smtp_port']=__('Port required'); 336 } 337 338 //abort on errors 339 if ($errors) 340 return false; 341 342 if(!$errors && ($vars['mail_host'] && $vars['userid'])) { 343 $existing = static::objects() 344 ->filter(array( 345 'mail_host' => $vars['mail_host'], 346 'userid' => $vars['userid'] 347 )); 348 349 if ($id) 350 $existing->exclude(array('email_id' => $id)); 351 352 if ($existing->exists()) 353 $errors['userid']=$errors['host']=__('Host/userid combination already in use.'); 354 } 355 356 $passwd = $vars['passwd'] ?: $vars['cpasswd']; 357 if(!$errors && $vars['mail_active']) { 358 //note: password is unencrypted at this point...MailFetcher expect plain text. 359 $fetcher = new MailFetcher( 360 array( 361 'host' => $vars['mail_host'], 362 'port' => $vars['mail_port'], 363 'folder' => $vars['mail_folder'], 364 'username' => $vars['userid'], 365 'password' => $passwd, 366 'protocol' => $vars['mail_protocol'], 367 'encryption' => $vars['mail_encryption']) 368 ); 369 if(!$fetcher->connect()) { 370 //$errors['err']='Invalid login. Check '.Format::htmlchars($vars['mail_protocol']).' settings'; 371 $errors['err']=sprintf(__('Invalid login. Check %s settings'),Format::htmlchars($vars['mail_protocol'])); 372 $errors['mail']='<br>'.$fetcher->getLastError(); 373 } elseif ($vars['mail_folder'] && !$fetcher->checkMailbox($vars['mail_folder'],true)) { 374 $errors['mail_folder']=sprintf(__('Invalid or unknown mail folder! >> %s'),$fetcher->getLastError()); 375 if(!$errors['mail']) 376 $errors['mail']=__('Invalid or unknown mail folder!'); 377 }elseif($vars['mail_archivefolder'] && !$fetcher->checkMailbox($vars['mail_archivefolder'],true)) { 378 //$errors['postfetch']='Invalid or unknown mail folder! >> '.$fetcher->getLastError().''; 379 $errors['postfetch']=sprintf(__('Invalid or unknown mail folder! >> %s'),$fetcher->getLastError()); 380 if(!$errors['mail']) 381 $errors['mail']=__('Invalid or unknown archive folder!'); 382 } 383 } 384 385 $smtppasswd = $vars['smtp_passwd'] ?: $vars['smtp_cpasswd']; 386 if(!$errors && $vars['smtp_active']) { //Check SMTP login only. 387 $smtpcreds = $vars['smtp_auth_creds']; 388 require_once 'Mail.php'; // PEAR Mail package 389 $smtp = mail::factory('smtp', 390 array ('host' => $vars['smtp_host'], 391 'port' => $vars['smtp_port'], 392 'auth' => (bool) $vars['smtp_auth'], 393 'username' => $smtpcreds ? $vars['smtp_userid'] : $vars['userid'], 394 'password' => $smtpcreds ? $smtppasswd : $passwd, 395 'timeout' =>20, 396 'debug' => false, 397 )); 398 $mail = $smtp->connect(); 399 if(PEAR::isError($mail)) { 400 $errors['err']=__('Unable to log in. Check SMTP settings.'); 401 $errors['smtp']='<br>'.$mail->getMessage(); 402 }else{ 403 $smtp->disconnect(); //Thank you, sir! 404 } 405 } 406 407 if($errors) return false; 408 409 $this->mail_errors = 0; 410 $this->mail_lastfetch = null; 411 $this->email = $vars['email']; 412 $this->name = Format::striptags($vars['name']); 413 $this->dept_id = $vars['dept_id']; 414 $this->priority_id = $vars['priority_id']; 415 $this->topic_id = $vars['topic_id']; 416 $this->noautoresp = $vars['noautoresp']; 417 $this->userid = $vars['userid']; 418 $this->mail_active = $vars['mail_active']; 419 $this->mail_host = $vars['mail_host']; 420 $this->mail_folder = $vars['mail_folder'] ?: null; 421 $this->mail_protocol = $vars['mail_protocol'] ?: 'POP'; 422 $this->mail_encryption = $vars['mail_encryption']; 423 $this->mail_port = $vars['mail_port'] ?: 0; 424 $this->mail_fetchfreq = $vars['mail_fetchfreq'] ?: 0; 425 $this->mail_fetchmax = $vars['mail_fetchmax'] ?: 0; 426 $this->smtp_active = $vars['smtp_active']; 427 $this->smtp_host = $vars['smtp_host']; 428 $this->smtp_port = $vars['smtp_port'] ?: 0; 429 $this->smtp_auth = $vars['smtp_auth']; 430 $this->smtp_auth_creds = isset($vars['smtp_auth_creds']) ? 1 : 0; 431 $this->smtp_userid = $vars['smtp_userid']; 432 $this->smtp_spoofing = $vars['smtp_spoofing']; 433 $this->notes = Format::sanitize($vars['notes']); 434 435 //Post fetch email handling... 436 if ($vars['postfetch'] && !strcasecmp($vars['postfetch'],'delete')) { 437 $this->mail_delete = 1; 438 $this->mail_archivefolder = null; 439 } 440 elseif($vars['postfetch'] && !strcasecmp($vars['postfetch'],'archive') && $vars['mail_archivefolder']) { 441 $this->mail_delete = 0; 442 $this->mail_archivefolder = $vars['mail_archivefolder']; 443 } 444 else { 445 $this->mail_delete = 0; 446 $this->mail_archivefolder = null; 447 } 448 449 if ($vars['passwd']) //New password - encrypt. 450 $this->userpass = Crypto::encrypt($vars['passwd'],SECRET_SALT, $vars['userid']); 451 452 if ($vars['smtp_passwd']) // New SMTP password - encrypt. 453 $this->smtp_userpass = Crypto::encrypt($vars['smtp_passwd'], SECRET_SALT, $vars['smtp_userid']); 454 455 if ($this->save()) 456 return true; 457 458 if ($id) { //update 459 $errors['err']=sprintf(__('Unable to update %s.'), __('this email')) 460 .' '.__('Internal error occurred'); 461 } 462 else { 463 $errors['err']=sprintf(__('Unable to add %s.'), __('this email')) 464 .' '.__('Internal error occurred'); 465 } 466 467 return false; 468 } 469 470 static function validateCredentials($username=null, $password=null, $id=null, &$errors, $smtp=false) { 471 if (!$username) 472 $errors[$smtp ? 'smtp_userid' : 'userid'] = __('Username missing'); 473 474 if (!$id && !$password) 475 $errors[$smtp ? 'smtp_passwd' : 'passwd'] = __('Password Required'); 476 elseif ($password && $username 477 && !Crypto::encrypt($password, SECRET_SALT, $username)) 478 $errors[$smtp ? 'smtp_passwd' : 'passwd'] = sprintf('%s - %s', __('Unable to encrypt password'), __('Get technical help!')); 479 480 return $errors; 481 } 482 483 static function getPermissions() { 484 return self::$perms; 485 } 486 487 static function getAddresses($options=array(), $flat=true) { 488 $objects = static::objects(); 489 if ($options['smtp']) 490 $objects = $objects->filter(array('smtp_active'=>true)); 491 492 if ($options['depts']) 493 $objects = $objects->filter(array('dept_id__in'=>$options['depts'])); 494 495 if (!$flat) 496 return $objects; 497 498 $addresses = array(); 499 foreach ($objects->values_flat('email_id', 'email') as $row) { 500 list($id, $email) = $row; 501 $addresses[$id] = $email; 502 } 503 return $addresses; 504 } 505} 506RolePermission::register(/* @trans */ 'Miscellaneous', Email::getPermissions()); 507?> 508