mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-30 03:43:40 +01:00
459 lines
20 KiB
PHP
459 lines
20 KiB
PHP
|
<?php
|
||
|
/***************************************************************************\
|
||
|
* EGroupWare - EMailAdmin IMAP via Plesk *
|
||
|
* http://www.egroupware.org *
|
||
|
* Written and (c) 2006 by RalfBecker-AT-outdoor-training.de *
|
||
|
* ------------------------------------------------------------------------- *
|
||
|
* emailadmin plugin for plesk: *
|
||
|
* - tested with Plesk7.5 under Linux, but should work with other plesk *
|
||
|
* versions and Windows too as it uses plesks cli (command line interface) *
|
||
|
* - this plugin ONLY works if you have root access to the webserver !!! *
|
||
|
* - you need to have mail activated for the domain in plesk first *
|
||
|
* - you need to configure the path to plesk's mail.sh or mail.exe cli by *
|
||
|
* editing this file (search for psa_mail_script) for now *
|
||
|
* - to allow the webserver to use the mail cli under Linux you need to *
|
||
|
* install the sudo package and add the following line to your sudoers *
|
||
|
* file using the visudo command as root: *
|
||
|
* wwwrun ALL = NOPASSWD: /usr/local/psa/bin/mail.sh *
|
||
|
* Replace wwwrun with the user the webserver is running as and, if *
|
||
|
* necessary adapt the path to mail.sh. *
|
||
|
* PLEASE NOTE: This allows all webserver users to run the mail.sh script *
|
||
|
* and to change the mail configuration of ALL domains !!! *
|
||
|
* => as with the "LDAP, Postfix & Cyrus" plugin the plesk one creates mail *
|
||
|
* users and manages passwords, aliases, forwards and quota from within *
|
||
|
* eGroupWare - no need to additionally visit the plesk interface anymore *
|
||
|
* ------------------------------------------------------------------------- *
|
||
|
* This program is free software; you can redistribute it and/or modify it *
|
||
|
* under the terms of the GNU General Public License as published by the *
|
||
|
* Free Software Foundation; version 2 of the License. *
|
||
|
\***************************************************************************/
|
||
|
/* $Id$ */
|
||
|
|
||
|
include_once(EGW_SERVER_ROOT."/emailadmin/inc/class.defaultimap.inc.php");
|
||
|
|
||
|
class pleskimap extends defaultimap
|
||
|
{
|
||
|
/**
|
||
|
* @var string $psa_mail_script full path to Plesk's mail.sh (Linux including sudo!) or mail.exe (Windows) interface
|
||
|
*/
|
||
|
var $psa_mail_script = '/usr/bin/sudo /usr/local/psa/bin/mail.sh'; // 'C:/psa/bin/mail.exe'
|
||
|
/**
|
||
|
* @var boolean $allways_create_mailbox true = allways create a mailbox on user creation,
|
||
|
* false = only if a local email (no forward) is given. To use felamimail you need a mailbox!
|
||
|
*/
|
||
|
var $allways_create_mailbox = true;
|
||
|
/**
|
||
|
* @var array $create_folders=array('Send','Trash') folders to automatic create and subscribe on account creation
|
||
|
*/
|
||
|
var $create_folders = array('Sent','Trash');
|
||
|
/**
|
||
|
* @var string/boolean $error string with last error-message or false
|
||
|
*/
|
||
|
var $error = false;
|
||
|
|
||
|
/**
|
||
|
* Create a full mailbox or just forward, depending on the given email address
|
||
|
* If email matches the default domain, we create a full mailbox, otherwise we create a forward
|
||
|
*
|
||
|
* @param array $hookValues
|
||
|
* @param string $action='create'
|
||
|
* @return boolean true on success, false otherwise
|
||
|
*/
|
||
|
function addAccount($hookValues,$action='create')
|
||
|
{
|
||
|
//echo "<p>pleskimap::addAccount(".print_r($hookValues,true).")</p>\n";
|
||
|
|
||
|
$defaultDomain = $this->profileData['defaultDomain'] ? $this->profileData['defaultDomain'] :
|
||
|
$GLOBALS['egw_info']['server']['mail_suffix'];
|
||
|
|
||
|
$localEmail = $hookValues['account_lid'].'@'.$defaultDomain;
|
||
|
$aliases = $forwards = array();
|
||
|
|
||
|
// is the given email a local address from our default domain?
|
||
|
if (substr($hookValues['account_email'],-1-strlen($defaultDomain)) != '@'.$defaultDomain)
|
||
|
{
|
||
|
$forwards[] = $hookValues['account_email'];
|
||
|
}
|
||
|
elseif ($hookValues['account_email'] != $localEmail)
|
||
|
{
|
||
|
$aliases[] = $hookValues['account_email'];
|
||
|
}
|
||
|
// add a default alias with Firstname.Lastname
|
||
|
if (!in_array($alias=$hookValues['account_firstname'].'.'.$hookValues['account_lastname'],$aliases) &&
|
||
|
$this->is_email($alias))
|
||
|
{
|
||
|
$aliases[] = $alias;
|
||
|
}
|
||
|
$info = $this->plesk_mail($action,$hookValues['account_lid'],$hookValues['account_passwd'],
|
||
|
$action != 'create' && !$aliases ? null : $aliases,$forwards,$this->allways_create_mailbox);
|
||
|
|
||
|
if (!$info['SUCCESS']) return false;
|
||
|
|
||
|
if ($forwards && !$this->allways_create_mailbox) return true; // no mailbox created, only a forward
|
||
|
|
||
|
// create Sent & Trash mailboxes and subscribe them
|
||
|
if(($mbox = @imap_open ($this->getMailboxString(),$localEmail,$hookValues['account_passwd'])))
|
||
|
{
|
||
|
$list = imap_getmailboxes($mbox, $this->getMailboxString(),'INBOX');
|
||
|
$delimiter = isset($list[0]->delimiter) ? $list[0]->delimiter : '.';
|
||
|
imap_subscribe($mbox,$this->getMailboxString('INBOX'));
|
||
|
|
||
|
foreach($this->create_folders as $folder)
|
||
|
{
|
||
|
$mailBoxName = 'INBOX'.$delimiter.$folder;
|
||
|
if(imap_createmailbox($mbox,imap_utf7_encode('{'.$this->profileData['imapServer'].'}'.$mailBoxName)))
|
||
|
{
|
||
|
imap_subscribe($mbox,$this->getMailboxString($mailBoxName));
|
||
|
}
|
||
|
}
|
||
|
imap_close($mbox);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
function deleteAccount($hookValues)
|
||
|
{
|
||
|
//echo "<p>pleskimap::deleteAccount(".print_r($hookValues,true).")</p>\n";
|
||
|
|
||
|
return $this->plesk_mail('remove',$hookValues['account_lid']);
|
||
|
}
|
||
|
|
||
|
function updateAccount($hookValues)
|
||
|
{
|
||
|
//echo "<p>pleskimap::updateAccount(".print_r($hookValues,true).")</p>\n";
|
||
|
|
||
|
if($hookValues['account_lid'] != $hookValues['old_loginid'])
|
||
|
{
|
||
|
$this->error = lang("Plesk can't rename users --> request ignored");
|
||
|
return false;
|
||
|
}
|
||
|
return $this->addAccount($hookValues,'update');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Read data from the mail account
|
||
|
*
|
||
|
* @param string/int $accountID
|
||
|
* @return array/boolean with keys mailLocalAddress, mailAlternateAddress, accountStatus, mailRoutingAddress, ... or false if not found
|
||
|
*/
|
||
|
function getUserData($accountID)
|
||
|
{
|
||
|
//echo "<p>pleskimap::getUserData('$accountID')</p>\n";
|
||
|
|
||
|
if (!($info = $this->plesk_mail('info',$accountID))) return false;
|
||
|
//_debug_array($info);
|
||
|
|
||
|
$data = array(
|
||
|
'mailLocalAddress' => $info['Mailname'].'@'.$info['Domain'],
|
||
|
'mailAlternateAddress' => $info['Alias(es)'] ? explode(' ',$info['Alias(es)']) : array(),
|
||
|
'accountStatus' => $info['Mailbox'] == 'true' || $info['Redirect'] == 'true' ? 'active' : 'disabled',
|
||
|
'mailRoutingAddress' => $info['Redirect address'] ? explode(' ',$info['Redirect address']) : false,
|
||
|
'deliveryMode' => $info['Redirect'] == 'true' && $info['Mailbox'] == 'false' ? 'forwardOnly' : '',
|
||
|
// 'qmailDotMode' => false,
|
||
|
// 'deliveryProgramPath' => false,
|
||
|
'quotaLimit' => $info['Mbox quota'] == 'Unlimited' ? '' : $info['Mbox quota']/1024.0,
|
||
|
);
|
||
|
//_debug_array($data);
|
||
|
return $data;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Save mail account data
|
||
|
*
|
||
|
* @param string/int $accountID
|
||
|
* @param array $accountData with keys mailLocalAddress, mailAlternateAddress, accountStatus, mailRoutingAddress, ...
|
||
|
* @return boolean true on success, false otherwise
|
||
|
*/
|
||
|
function saveUserData($accountID, $accountData)
|
||
|
{
|
||
|
//echo "<p>pleskimap::saveUserData('$accountID',".print_r($accountData,true).")</p>\n";
|
||
|
|
||
|
// not used: $accountData['accountStatus']=='active', $accountData['qmailDotMode'], $accountData['deliveryProgramPath']
|
||
|
$info = $this->plesk_mail('update',$accountID,null,
|
||
|
$accountData['mailAlternateAddress'] ? $accountData['mailAlternateAddress'] : array(),
|
||
|
$accountData['mailRoutingAddress'] ? $accountData['mailRoutingAddress'] : array(),
|
||
|
empty($accountData['deliveryMode']),
|
||
|
1024*(float)$accountData['quotaLimit'],$accountData['accountStatus']=='active');
|
||
|
|
||
|
if (!$info['SUCCSESS'])
|
||
|
{
|
||
|
if ($info) $this->error = implode(', ',$info);
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* call plesk's mail command line interface
|
||
|
*
|
||
|
* Usage: mail.sh command <mail_name> [options]
|
||
|
*
|
||
|
* Available commands:
|
||
|
* --create or -c <mail>@<domain> creates mail account
|
||
|
* --update or -u <mail>@<domain> updates mail account parameters
|
||
|
* --remove or -r <mail>@<domain> removes mail account
|
||
|
* --info or -i <mail>@<domain> retrieves mail account information
|
||
|
* --on <domain> enables mail service for domain
|
||
|
* --off <domain> disables mail service for domain
|
||
|
* --help or -h displays this help page
|
||
|
*
|
||
|
* Available options:
|
||
|
* -cp_access <true|false> enables control panel access (default:
|
||
|
* true)
|
||
|
* -mailbox <true|false> creates/removes mailbox
|
||
|
* -passwd <passwd> sets mailbox password [see the note
|
||
|
* below for details]
|
||
|
* -boxpass <passwd> obsolete alias for option "passwd"
|
||
|
* (this option may be removed from
|
||
|
* future releases)
|
||
|
* -passwd_type <plain|crypt> specifies the type of mailbox
|
||
|
* password, ignored if no password
|
||
|
* specified [see the note below for
|
||
|
* details]
|
||
|
* -mbox_quota <KB> limits the mailbox quota to the
|
||
|
* desired amount
|
||
|
* -boxquota <KB> obsolete alias for option "mbox_quota"
|
||
|
* (this option may be removed from
|
||
|
* future releases)
|
||
|
* -aliases <add|del>:<name1[,name2]> adds or deletes mail
|
||
|
* alias(es) to/from mailname
|
||
|
* -mgroups <add|del>:<list1[,list2]> adds or removes mail name
|
||
|
* to/from mail group
|
||
|
* -redirect <true|false> switches mail redirect on/off
|
||
|
* -rediraddr <addr> sets redirect to address (required if
|
||
|
* redirect is enabled)
|
||
|
* -group <true|false> switches mail group on/off
|
||
|
* -groupmem <add|del>:<addr1[,addr2]> adds/removes address(-es)
|
||
|
* to/from mail group
|
||
|
* -repo <add|del>:<file1[,file2]> adds/removes file to/from
|
||
|
* attachments repository
|
||
|
* [deprecated, use
|
||
|
* autoresponder.sh]
|
||
|
* -autorsp <true|false> switches all autoresponders on/off
|
||
|
* [deprecated, use autoresponder.sh]
|
||
|
* -autoname <name> autoresponder name (required for all
|
||
|
* autoresponder options) [deprecated,
|
||
|
* use autoresponder.sh]
|
||
|
* -autostatus <true|false> switches on/off autoresponder with
|
||
|
* specified name (true) [deprecated,
|
||
|
* use autoresponder.sh]
|
||
|
* -autoreq <subj|body>:<string> or <always> defines the condition
|
||
|
* for the autoresponder
|
||
|
* to be activated
|
||
|
* whether the
|
||
|
* specified pattern is
|
||
|
* encountered in the
|
||
|
* subject or body, or
|
||
|
* to respond always
|
||
|
* [deprecated, use
|
||
|
* autoresponder.sh]
|
||
|
* -autosubj <original|string> the subject line to be set up into
|
||
|
* autoresponder ("Re: <incoming
|
||
|
* subject>") or a custom string
|
||
|
* [deprecated, use autoresponder.sh]
|
||
|
* -auto_replyto <string> return address that will be set up
|
||
|
* into the autoresponder's messages
|
||
|
* [deprecated, use autoresponder.sh]
|
||
|
* -autotext <string> autoresponder message text
|
||
|
* [deprecated, use autoresponder.sh]
|
||
|
* -autoatch <add|del>:<file1[,file2]> adds/removes autoresponder
|
||
|
* attachment files
|
||
|
* [deprecated, use
|
||
|
* autoresponder.sh]
|
||
|
* -autofrq <number> defines the maximum number of
|
||
|
* responses to a unique e-mail address
|
||
|
* per day [deprecated, use
|
||
|
* autoresponder.sh]
|
||
|
* -autostor <number> defines the number of unique addresses
|
||
|
* to be stored for autoresponder
|
||
|
* [deprecated, use autoresponder.sh]
|
||
|
* -autored <addr> defines the e-mail address to forward
|
||
|
* all incoming mail to [deprecated, use
|
||
|
* autoresponder.sh]
|
||
|
* -multiple-sessions <true|false> allow multiple sessions
|
||
|
*
|
||
|
* Note:
|
||
|
* For security reasons, you can transfer not encrypted passwords via environment
|
||
|
* variable PSA_PASSWORD, by specifying the empty value in the command line for
|
||
|
* the passwd arguments (like " -passwd ''") and setting the password value in
|
||
|
* the PSA_PASSWORD variable.
|
||
|
* Similarly, you can transfer the crypted password via the environment variable
|
||
|
* PSA_CRYPTED_PASSWORD, by specifying the empty value in the command line for
|
||
|
* the passwd arguments (like " -passwd ''") and by setting the password value in
|
||
|
* the PSA_CRYPTED_PASSWORD variable.
|
||
|
*
|
||
|
* Version: psa v7.5.0_build75041208.07 os_SuSE 9.1
|
||
|
*
|
||
|
* mail.sh --info account@domain.com
|
||
|
* Mailname: account
|
||
|
* Domain: domain.com
|
||
|
* Alias(es): Firstname.Lastname
|
||
|
* CP Access: true
|
||
|
* Mailbox: true
|
||
|
* Password: geheim
|
||
|
* Password type: plain
|
||
|
* Mbox quota: Unlimited
|
||
|
* Redirect: false
|
||
|
* Mailgroup: false
|
||
|
* File repository: Empty
|
||
|
* Autoresponder: false
|
||
|
* Antivirus mail
|
||
|
* checking: Disabled
|
||
|
*
|
||
|
* SUCCESS: Gathering information for 'account@domain.com' complete
|
||
|
*
|
||
|
* mail.sh --info bogus@domain.com
|
||
|
* An error occured during getting mailname information: Mailname 'bogus@domain.com' doesn't exists
|
||
|
*
|
||
|
* @param string $action 'info', 'create', 'update' or 'remove'
|
||
|
* @param string/int $account account_lid or numerical account_id
|
||
|
* @param string $password=null string with password or null to not change
|
||
|
* @param array $aliases=null array with aliases or null to not change the aliases
|
||
|
* @param array $forwards=null array of email address to forward or null to not change
|
||
|
* @param boolean $keepLocalCopy=null if forwarding keep a local copy or not, null = dont change
|
||
|
* @param int $quota_kb=null mailbox quota in kb
|
||
|
* @return boolean/array array with returned values or false otherwise, error-message in $this->error
|
||
|
*/
|
||
|
function plesk_mail($action,$account,$password=null,$aliases=null,$forwards=null,$keepLocalCopy=null,$quota_kb=null)
|
||
|
{
|
||
|
//echo "<p>smtpplesk::plesk_mail('$action','$account','$password',".print_r($aliases,true).",".print_r($forwards,true).",".(is_null($keepLocalCopy)?'':(int)$keepLocalCopy).",$quota_kb)</p>\n";
|
||
|
|
||
|
$this->error = false;
|
||
|
|
||
|
if (is_numeric($account))
|
||
|
{
|
||
|
$account_lid = $GLOBALS['egw']->accounts->id2name($account);
|
||
|
}
|
||
|
elseif ($GLOBALS['egw']->accounts->name2id($account))
|
||
|
{
|
||
|
$account_lid = $account;
|
||
|
}
|
||
|
if (!$account_lid)
|
||
|
{
|
||
|
$this->error = lang("Account '%1' not found !!!",$account);
|
||
|
return false;
|
||
|
}
|
||
|
if (!in_array($action,array('info','create','update','remove')))
|
||
|
{
|
||
|
$this->error = lang("Unsupported action '%1' !!!",$action);
|
||
|
return false;
|
||
|
}
|
||
|
$defaultDomain = $this->profileData['defaultDomain'] ? $this->profileData['defaultDomain'] :
|
||
|
$GLOBALS['egw_info']['server']['mail_suffix'];
|
||
|
|
||
|
if ($action == 'update' && !($info = $this->plesk_mail('info',$account)))
|
||
|
{
|
||
|
$action = 'create'; // mail-account does not yet exist --> create it
|
||
|
}
|
||
|
$localEmail = $account_lid.'@'.$defaultDomain;
|
||
|
$script = $this->psa_mail_script . ' --'.$action . ' ' . $localEmail;
|
||
|
|
||
|
if ($action != 'info')
|
||
|
{
|
||
|
// invalidate our cache
|
||
|
$GLOBALS['egw']->session->appsession('plesk-email-'.$account_lid,'emailadmin',false);
|
||
|
|
||
|
// we dont set passwords shorten then 5 chars, as it only give an error in plesk
|
||
|
if (!is_null($password) && $password)
|
||
|
{
|
||
|
if (strlen($password) < 5 || strpos($password,$account_lid) !== false)
|
||
|
{
|
||
|
$this->error = lang('Plesk requires passwords to have at least 5 characters and not contain the account-name --> password NOT set!!!');
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$script .= ' -passwd \''.str_replace('\'','\\\'',$password).'\' -passwd_type plain';
|
||
|
}
|
||
|
}
|
||
|
if ($action == 'create' || !is_null($forwards) || !is_null($keepLocalCopy))
|
||
|
{
|
||
|
$script .= ' -mailbox '.(!$forwards || $keepLocalCopy ? 'true' : 'false');
|
||
|
}
|
||
|
// plesk allows only one forwarding address, we ignore everything but the first
|
||
|
if (!is_null($forwards) && (!$forwards || $this->is_email($forwards[0])))
|
||
|
{
|
||
|
$script .= ' -redirect '.(!$forwards ? 'false' : 'true -rediraddr '.$forwards[0]);
|
||
|
}
|
||
|
if ($action == 'update')
|
||
|
{
|
||
|
if (!is_null($aliases))
|
||
|
{
|
||
|
$existing_aliases = explode(' ',$info['Alias(es)']); // without domain!
|
||
|
$delete_aliases = array();
|
||
|
foreach($existing_aliases as $alias)
|
||
|
{
|
||
|
if ($alias && !in_array($alias,$aliases) && !in_array($alias.'@'.$defaultDomain,$aliases))
|
||
|
{
|
||
|
$delete_aliases[] = $alias;
|
||
|
}
|
||
|
}
|
||
|
if ($delete_aliases)
|
||
|
{
|
||
|
$script .= ' -aliases del:'.implode(',',$delete_aliases);
|
||
|
}
|
||
|
foreach($aliases as $n => $alias)
|
||
|
{
|
||
|
if (in_array($alias,$existing_aliases) || in_array(str_replace('@'.$defaultDomain,'',$alias),$existing_aliases))
|
||
|
{
|
||
|
unset($aliases[$n]); // no change
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (!is_null($aliases) && count($aliases))
|
||
|
{
|
||
|
foreach($aliases as $alias)
|
||
|
{
|
||
|
if (!$this->is_email($alias)) return false; // security precausion
|
||
|
}
|
||
|
$script .= ' -aliases add:'.str_replace('@'.$defaultDomain,'',implode(',',$aliases));
|
||
|
}
|
||
|
if (!is_null($quota_kb) && (int)$quota_kb)
|
||
|
{
|
||
|
$script .= ' -mbox_quota '.(int)$quota_kb;
|
||
|
}
|
||
|
}
|
||
|
//echo "<p>$script</p>\n";
|
||
|
if (!($fp = popen($script.' 2>&1','r')))
|
||
|
{
|
||
|
$this->error = lang("Plesk mail script '%1' not found !!!",$this->psa_mail_script);
|
||
|
return false;
|
||
|
}
|
||
|
$values = array();
|
||
|
while(!feof($fp))
|
||
|
{
|
||
|
$line = trim(fgets($fp));
|
||
|
list($name,$value) = split(': *',$line,2);
|
||
|
if (!is_null($value) && strpos($name,'An error occured') === false && $name)
|
||
|
{
|
||
|
$values[$name] = $value;
|
||
|
}
|
||
|
elseif ($line)
|
||
|
{
|
||
|
$values[] = $line;
|
||
|
}
|
||
|
}
|
||
|
pclose($fp);
|
||
|
|
||
|
if (!$values['SUCCESS'])
|
||
|
{
|
||
|
$this->error = implode(', ',$values);
|
||
|
return false;
|
||
|
}
|
||
|
return $values;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* checks for valid email addresse (local mail address dont need a domain!)
|
||
|
*
|
||
|
* Important as we run shell scripts with the address and one could try to run arbitrary commands this way!!!
|
||
|
* We only allow letters a-z, numbers and the following other chars: _ . - @
|
||
|
*
|
||
|
* @return boolean
|
||
|
*/
|
||
|
function is_email($email)
|
||
|
{
|
||
|
return preg_match('/^[@a-z0-9_.-]+$/i',$email);
|
||
|
}
|
||
|
}
|