2015-06-16 08:37:16 +02:00
< ? php
/**
* EGroupware - Mail - interface class for activesync implementation
*
* @ link http :// www . egroupware . org
* @ package mail
2016-10-08 14:32:58 +02:00
* @ author EGroupware GmbH [ info @ egroupware . org ]
* @ author Ralf Becker < rb @ egroupware . org >
2015-06-16 08:37:16 +02:00
* @ author Philip Herbert < philip @ knauber . de >
2016-10-08 14:32:58 +02:00
* @ copyright ( c ) 2014 - 16 by EGroupware GmbH < info - AT - egroupware . org >
2015-06-16 08:37:16 +02:00
* @ license http :// opensource . org / licenses / gpl - license . php GPL - GNU General Public License
* @ version $Id $
*/
2016-05-03 21:17:44 +02:00
use EGroupware\Api ;
2016-03-28 20:51:38 +02:00
use EGroupware\Api\Mail ;
2015-06-16 08:37:16 +02:00
/**
* mail eSync plugin
*
* Plugin creates a device specific file to map alphanumeric folder names to nummeric id ' s .
*/
2015-06-16 10:24:05 +02:00
class mail_zpush implements activesync_plugin_write , activesync_plugin_sendmail , activesync_plugin_meeting_response , activesync_plugin_search_mailbox
2015-06-16 08:37:16 +02:00
{
/**
2015-11-05 14:23:00 +01:00
* var activesync_backend
2015-06-16 08:37:16 +02:00
*/
private $backend ;
/**
2016-03-28 20:51:38 +02:00
* Instance of Mail
2015-06-16 08:37:16 +02:00
*
2016-03-28 20:51:38 +02:00
* @ var Mail
2015-06-16 08:37:16 +02:00
*/
private $mail ;
/**
* Provides the ability to change the line ending
* @ var string
*/
public static $LE = " \n " ;
/**
* Integer id of trash folder
*
* @ var mixed
*/
private $_wasteID = false ;
/**
* Integer id of sent folder
*
* @ var mixed
*/
private $_sentID = false ;
/**
* Integer id of current mail account / connection
*
* @ var int
*/
private $account ;
private $folders ;
private $messages ;
static $profileID ;
2016-07-28 15:47:54 +02:00
// to control how deep one may dive into the past
2024-05-15 16:07:54 +02:00
const PAST_LIMIT = 365 ;
2016-07-28 15:47:54 +02:00
2015-06-16 08:37:16 +02:00
/**
* debugLevel - enables more debug
*
* @ var int
*/
private $debugLevel = 0 ;
/**
* Constructor
*
2015-11-05 14:23:00 +01:00
* @ param activesync_backend $backend
2015-06-16 08:37:16 +02:00
*/
2015-11-05 14:23:00 +01:00
public function __construct ( activesync_backend $backend )
2015-06-16 08:37:16 +02:00
{
2023-06-09 18:27:28 +02:00
if ( isset ( $GLOBALS [ 'egw_setup' ])) return ;
2015-06-16 08:37:16 +02:00
//$this->debugLevel=2;
$this -> backend = $backend ;
if ( ! isset ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'activesync' ][ 'mail-ActiveSyncProfileID' ]))
{
if ( $this -> debugLevel > 1 ) error_log ( __METHOD__ . __LINE__ . ' Noprefs set: using 0 as default' );
// globals preferences add appname varname value
$GLOBALS [ 'egw' ] -> preferences -> add ( 'activesync' , 'mail-ActiveSyncProfileID' , 0 , 'user' );
// save prefs
$GLOBALS [ 'egw' ] -> preferences -> save_repository ( true );
}
if ( $this -> debugLevel > 1 ) error_log ( __METHOD__ . __LINE__ . ' ActiveProfileID:' . array2string ( self :: $profileID ));
if ( is_null ( self :: $profileID ))
{
if ( $this -> debugLevel > 1 ) error_log ( __METHOD__ . __LINE__ . ' self::ProfileID isNUll:' . array2string ( self :: $profileID ));
2016-05-03 21:17:44 +02:00
self :: $profileID =& Api\Cache :: getSession ( 'mail' , 'activeSyncProfileID' );
2015-06-16 08:37:16 +02:00
if ( $this -> debugLevel > 1 ) error_log ( __METHOD__ . __LINE__ . ' ActiveProfileID (after reading Cache):' . array2string ( self :: $profileID ));
}
if ( isset ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'activesync' ][ 'mail-ActiveSyncProfileID' ]))
{
if ( $this -> debugLevel > 1 ) error_log ( __METHOD__ . __LINE__ . ' Pref for ProfileID:' . array2string ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'activesync' ][ 'mail-ActiveSyncProfileID' ]));
if ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'activesync' ][ 'mail-ActiveSyncProfileID' ] == 'G' )
{
self :: $profileID = 'G' ; // this should trigger the fetch of the first negative profile (or if no negative profile is available the firstb there is)
}
else
{
self :: $profileID = ( int ) $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'activesync' ][ 'mail-ActiveSyncProfileID' ];
}
}
if ( $this -> debugLevel > 1 ) error_log ( __METHOD__ . __LINE__ . ' Profile Selected (after reading Prefs):' . array2string ( self :: $profileID ));
// verify we are on an existing profile, if not running in setup (settings can not be static according to interface!)
if ( ! isset ( $GLOBALS [ 'egw_setup' ]))
{
try {
2016-03-28 20:51:38 +02:00
Mail\Account :: read ( self :: $profileID );
2015-06-16 08:37:16 +02:00
}
catch ( Exception $e ) {
unset ( $e );
2016-03-28 20:51:38 +02:00
self :: $profileID = Mail\Account :: get_default_acc_id ();
2015-06-16 08:37:16 +02:00
}
}
if ( $this -> debugLevel > 0 ) error_log ( __METHOD__ . '::' . __LINE__ . ' ProfileSelected:' . self :: $profileID );
//$this->debugLevel=0;
}
/**
* Populates $settings for the preferences
*
* @ param array | string $hook_data
* @ return array
*/
function egw_settings ( $hook_data )
{
//error_log(__METHOD__.__LINE__.array2string($hook_data));
$identities = array ();
if ( ! isset ( $hook_data [ 'setup' ]) && in_array ( $hook_data [ 'type' ], array ( 'user' , 'group' )))
{
2016-03-28 20:51:38 +02:00
$identities = iterator_to_array ( Mail\Account :: search (( int ) $hook_data [ 'account_id' ]));
2015-06-16 08:37:16 +02:00
}
$identities += array (
'G' => lang ( 'Primary Profile' ),
);
$settings [ 'mail-ActiveSyncProfileID' ] = array (
'type' => 'select' ,
'label' => 'eMail Account to sync' ,
'name' => 'mail-ActiveSyncProfileID' ,
'help' => 'eMail Account to sync ' ,
'values' => $identities ,
'default' => 'G' ,
'xmlrpc' => True ,
'admin' => False ,
);
$settings [ 'mail-allowSendingInvitations' ] = array (
'type' => 'select' ,
'label' => 'allow sending of calendar invitations using this profile?' ,
'name' => 'mail-allowSendingInvitations' ,
'help' => 'control the sending of calendar invitations while using this profile' ,
'values' => array (
'sendifnocalnotif' => 'only send if there is no notification in calendar' ,
'send' => 'yes, always send' ,
'nosend' => 'no, do not send' ,
),
'xmlrpc' => True ,
'default' => 'sendifnocalnotif' ,
'admin' => False ,
);
2016-07-28 15:47:54 +02:00
$settings [ 'mail-maximumSyncRange' ] = array (
2016-07-29 15:50:34 +02:00
'type' => 'integer' ,
2016-07-28 15:47:54 +02:00
'label' => lang ( 'How many days to sync in the past when client does not specify a date-range (default %1)' , self :: PAST_LIMIT ),
'name' => 'mail-maximumSyncRange' ,
'help' => 'if the client sets no sync range, you may override the setting (preventing client crash that may be caused by too many mails/too much data). If you want to sync way-back into the past: set a large number' ,
'xmlrpc' => True ,
'admin' => False ,
);
2016-06-14 16:41:50 +02:00
/*
2016-07-07 12:04:47 +02:00
$sigOptions = array (
'send' => 'yes, always add EGroupware signatures to outgoing mails' ,
'nosend' => 'no, never add EGroupware signatures to outgoing mails' ,
);
if ( ! isset ( $hook_data [ 'setup' ]) && in_array ( $hook_data [ 'type' ], array ( 'user' , 'group' )) && $hook_data [ 'account_id' ])
{
$pID = self :: $profileID ;
if ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'activesync' ][ 'mail-ActiveSyncProfileID' ] == 'G' )
{
$pID = Mail\Account :: get_default_acc_id ();
}
$acc = Mail\Account :: read ( $pID );
error_log ( __METHOD__ . __LINE__ . ':' . $pID . '->' . array2string ( $acc ));
$Identities = Mail\Account :: identities ( $pID );
foreach ( $Identities as & $identity )
{
$Identity = self :: identity_name ( $identity );
}
error_log ( __METHOD__ . __LINE__ . array2string ( $Identities ));
}
2016-06-14 16:41:50 +02:00
$settings [ 'mail-useSignature' ] = array (
'type' => 'select' ,
'label' => 'control if and which available signature is added to outgoing mails' ,
'name' => 'mail-useSignature' ,
'help' => 'control the use of signatures' ,
2016-07-07 12:04:47 +02:00
'values' => $sigOptions ,
2016-06-14 16:41:50 +02:00
'xmlrpc' => True ,
'default' => 'nosend' ,
'admin' => False ,
);
*/
2015-06-16 08:37:16 +02:00
return $settings ;
}
/**
* Verify preferences
*
* @ param array | string $hook_data
* @ return array with error - messages from all plugins
*/
function verify_settings ( $hook_data )
{
$errors = array ();
// check if an eSync eMail profile is set (might not be set as default or forced!)
if ( isset ( $hook_data [ 'prefs' ][ 'mail-ActiveSyncProfileID' ]) || $hook_data [ 'type' ] == 'user' )
{
// eSync and eMail translations are not (yet) loaded
2016-05-03 21:17:44 +02:00
Api\Translation :: add_app ( 'activesync' );
Api\Translation :: add_app ( 'mail' );
2015-06-16 08:37:16 +02:00
// inject preference to verify and call constructor
$GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'activesync' ][ 'mail-ActiveSyncProfileID' ] =
$hook_data [ 'prefs' ][ 'mail-ActiveSyncProfileID' ];
$this -> __construct ( $this -> backend );
try {
$this -> _connect ( 0 , true );
$this -> _disconnect ();
if ( ! $this -> _wasteID ) $errors [] = lang ( 'No valid %1 folder configured!' , '<b>' . lang ( 'trash' ) . '</b>' );
if ( ! $this -> _sentID ) $errors [] = lang ( 'No valid %1 folder configured!' , '<b>' . lang ( 'send' ) . '</b>' );
}
catch ( Exception $e ) {
$errors [] = lang ( 'Can not open IMAP connection' ) . ': ' . $e -> getMessage ();
}
if ( $errors )
{
$errors [] = '<b>' . lang ( 'eSync will FAIL without a working eMail configuration!' ) . '</b>' ;
}
}
//error_log(__METHOD__.'('.array2string($hook_data).') returning '.array2string($errors));
return $errors ;
}
/**
* Open IMAP connection
*
* @ param int $account integer id of account to use
* @ todo support different accounts
*/
2015-10-20 18:26:55 +02:00
private function _connect ( $account = 0 )
2015-06-16 08:37:16 +02:00
{
2015-10-13 14:45:18 +02:00
if ( ! $account ) $account = self :: $profileID ? self :: $profileID : 0 ;
2015-06-16 08:37:16 +02:00
if ( $this -> mail && $this -> account != $account ) $this -> _disconnect ();
$this -> _wasteID = false ;
$this -> _sentID = false ;
2022-04-26 18:46:23 +02:00
if ( empty ( $this -> mail ))
2015-06-16 08:37:16 +02:00
{
2015-10-20 18:26:55 +02:00
$this -> account = $account ;
// todo: tell mail which account to use
//error_log(__METHOD__.__LINE__.' create object with ProfileID:'.array2string(self::$profileID));
2016-03-28 20:51:38 +02:00
$this -> mail = Mail :: getInstance ( false , self :: $profileID , true , false , true );
2015-10-20 18:26:55 +02:00
if ( self :: $profileID == 0 && isset ( $this -> mail -> icServer -> ImapServerId ) && ! empty ( $this -> mail -> icServer -> ImapServerId )) self :: $profileID = $this -> mail -> icServer -> ImapServerId ;
2015-06-16 08:37:16 +02:00
}
2015-10-13 16:47:18 +02:00
else
2015-06-16 08:37:16 +02:00
{
2015-10-20 18:26:55 +02:00
//error_log(__METHOD__.__LINE__." connect with profileID: ".self::$profileID);
if ( self :: $profileID == 0 && isset ( $this -> mail -> icServer -> ImapServerId ) && ! empty ( $this -> mail -> icServer -> ImapServerId )) self :: $profileID = $this -> mail -> icServer -> ImapServerId ;
2015-06-16 08:37:16 +02:00
}
2015-10-20 18:26:55 +02:00
$this -> mail -> openConnection ( self :: $profileID , false );
2015-10-13 16:47:18 +02:00
2015-06-16 08:37:16 +02:00
$this -> _wasteID = $this -> mail -> getTrashFolder ( false );
//error_log(__METHOD__.__LINE__.' TrashFolder:'.$this->_wasteID);
$this -> _sentID = $this -> mail -> getSentFolder ( false );
$this -> mail -> getOutboxFolder ( true );
//error_log(__METHOD__.__LINE__.' SentFolder:'.$this->_sentID);
//error_log(__METHOD__.__LINE__.' Connection Status for ProfileID:'.self::$profileID.'->'.$this->mail->icServer->_connected);
}
/**
* Close IMAP connection
*/
private function _disconnect ()
{
2016-02-26 14:04:35 +01:00
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ );
2015-06-16 08:37:16 +02:00
if ( $this -> mail ) $this -> mail -> closeConnection ();
unset ( $this -> mail );
unset ( $this -> account );
unset ( $this -> folders );
}
/**
* GetFolderList
*
* @ ToDo loop over available email accounts
*/
public function GetFolderList ()
{
$folderlist = array ();
2016-02-26 14:04:35 +01:00
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ );
2015-06-16 08:37:16 +02:00
/*foreach($available_accounts as $account)*/ $account = 0 ;
{
$this -> _connect ( $account );
if ( ! isset ( $this -> folders )) $this -> folders = $this -> mail -> getFolderObjects ( true , false , $_alwaysGetDefaultFolders = true );
2016-02-26 14:04:35 +01:00
if ( $this -> debugLevel > 1 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . array2string ( $this -> folders ));
2015-06-16 08:37:16 +02:00
foreach ( $this -> folders as $folder => $folderObj ) {
2016-02-26 14:04:35 +01:00
if ( $this -> debugLevel > 1 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' folder=' . $folder );
2015-06-16 08:37:16 +02:00
$folderlist [] = $f = array (
'id' => $this -> createID ( $account , $folder ),
'mod' => $folderObj -> shortDisplayName ,
'parent' => $this -> getParentID ( $account , $folder ),
);
2016-02-26 14:04:35 +01:00
if ( $this -> debugLevel > 1 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " () returning " . array2string ( $f ));
2015-06-16 08:37:16 +02:00
}
}
2016-02-26 14:04:35 +01:00
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " () returning " . array2string ( $folderlist ));
2015-06-16 08:37:16 +02:00
return $folderlist ;
}
/**
* Sends a message which is passed as rfc822 . You basically can do two things
* 1 ) Send the message to an SMTP server as - is
* 2 ) Parse the message yourself , and send it some other way
* It is up to you whether you want to put the message in the sent items folder . If you
* want it in 'sent items' , then the next sync on the 'sent items' folder should return
* the new message as any other new message in a folder .
*
2015-06-23 17:36:12 +02:00
* @ param array $smartdata = IMAP - SendMail : SyncSendMail (
* ( S ) clientid => SendMail - 30722448149304
* ( S ) saveinsent => empty
* ( S ) replacemime => null
* ( S ) accountid => null
* ( S ) source => SyncSendMailSource (
* ( S ) folderid => 101000000000
* ( S ) itemid => 33776
* ( S ) longid => null
* ( S ) instanceid => null
* unsetVars ( Array ) size : 0
* flags => false
* content => null
* )
* ( S ) mime => Date : Tue , 23 Jun 2015 14 : 13 : 23 + 0200
* Subject : AW : Blauer himmel
*....
* ( S ) replyflag => true
* ( S ) forwardflag => null
* unsetVars ( Array ) size : 0
* flags => false
* content => null
* )
*
2015-06-16 08:37:16 +02:00
* @ return boolean true on success , false on error
*
* @ see eg . BackendIMAP :: SendMail ()
* @ todo implement either here or in mail backend
* ( maybe sending here and storing to sent folder in plugin , as sending is supposed to always work in EGroupware )
*/
2015-06-23 17:36:12 +02:00
public function SendMail ( $smartdata )
2015-06-16 08:37:16 +02:00
{
2015-10-13 17:06:04 +02:00
//$this->debugLevel=2;
2015-06-16 08:37:16 +02:00
$ClientSideMeetingRequest = false ;
$allowSendingInvitations = 'sendifnocalnotif' ;
if ( isset ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'activesync' ][ 'mail-allowSendingInvitations' ]) &&
$GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'activesync' ][ 'mail-allowSendingInvitations' ] == 'nosend' )
{
$allowSendingInvitations = false ;
}
elseif ( isset ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'activesync' ][ 'mail-allowSendingInvitations' ]) &&
$GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'activesync' ][ 'mail-allowSendingInvitations' ] != 'nosend' )
{
$allowSendingInvitations = $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'activesync' ][ 'mail-allowSendingInvitations' ];
}
2015-06-23 17:36:12 +02:00
$smartdata_task = ( $smartdata -> replyflag ? 'reply' : ( $smartdata -> forwardflag ? 'forward' : 'new' ));
2015-06-16 08:37:16 +02:00
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ( isset ( $smartdata -> mime ) ? $smartdata -> mime : " " ) . " task: " . ( isset ( $smartdata_task ) ? $smartdata_task : " " ) . " itemid: " . ( isset ( $smartdata -> source -> itemid ) ? $smartdata -> source -> itemid : " " ) . " folder: " . ( isset ( $smartdata -> source -> folderid ) ? $smartdata -> source -> folderid : " " ));
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ): Smartdata = " . array2string ( $smartdata ));
2015-06-16 08:37:16 +02:00
//error_log("IMAP-Sendmail: Smartdata = ".array2string($smartdata));
2016-03-28 20:51:38 +02:00
// initialize our Mail
if ( ! isset ( $this -> mail )) $this -> mail = Mail :: getInstance ( false , self :: $profileID , true , false , true );
2015-06-16 08:37:16 +02:00
$activeMailProfiles = $this -> mail -> getAccountIdentities ( self :: $profileID );
// use the standardIdentity
2016-03-28 20:51:38 +02:00
$activeMailProfile = Mail :: getStandardIdentityForProfile ( $activeMailProfiles , self :: $profileID );
2015-06-16 08:37:16 +02:00
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ) " . ' ProfileID:' . self :: $profileID . ' ActiveMailProfile:' . array2string ( $activeMailProfile ));
2016-08-05 15:12:21 +02:00
// collect identity / signature for later usage, and to determine if we may have to manipulate TransferEncoding and Charset
try
{
$acc = Mail\Account :: read ( $this -> mail -> icServer -> ImapServerId );
//error_log(__METHOD__.__LINE__.array2string($acc));
$_signature = Mail\Account :: read_identity ( $acc [ 'ident_id' ], true );
}
catch ( Exception $e )
{
$_signature = array ();
}
$signature = $_signature [ 'ident_signature' ];
if (( isset ( $preferencesArray [ 'disableRulerForSignatureSeparation' ]) &&
$preferencesArray [ 'disableRulerForSignatureSeparation' ]) ||
empty ( $signature ) || trim ( Api\Mail\Html :: convertHTMLToText ( $signature )) == '' )
{
$disableRuler = true ;
}
2024-02-08 10:24:39 +01:00
$beforeHtml = ! empty ( $disableRuler ) ? ' <br>' : ' <br><hr style="border:dotted 1px silver; width:90%; border:dotted 1px silver;">' ;
$beforePlain = ! empty ( $disableRuler ) ? " \r \n \r \n " : " \r \n \r \n -- \r \n " ;
2016-08-05 15:12:21 +02:00
$sigText = Mail :: merge ( $signature , array ( $GLOBALS [ 'egw' ] -> accounts -> id2name ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ], 'person_id' )));
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' Signature to use:' . $sigText );
$sigTextHtml = $beforeHtml . $sigText ;
$sigTextPlain = $beforePlain . Api\Mail\Html :: convertHTMLToText ( $sigText );
2015-06-16 08:37:16 +02:00
2016-08-05 15:12:21 +02:00
$force8bit = false ;
if ( Api\Translation :: detect_encoding ( $sigTextPlain ) != 'ascii' ) $force8bit = true ;
2016-11-10 13:50:51 +01:00
// beware. the section below might cause trouble regarding bcc and attachments, so maybe this is to be handeled differently
if ( $force8bit )
{
$converterObj = new Api\Mailer ( 'initbasic' );
$smartdata -> mime = $converterObj -> convertMessageTextParts ( $smartdata -> mime , false , 'utf-8' );
}
2016-05-03 21:17:44 +02:00
// initialize the new Api\Mailer object for sending
$mailObject = new Api\Mailer ( self :: $profileID );
2016-11-10 13:50:51 +01:00
2016-10-19 12:01:38 +02:00
$this -> mail -> parseRawMessageIntoMailObject ( $mailObject , $smartdata -> mime , $force8bit );
2015-06-16 08:37:16 +02:00
// Horde SMTP Class uses utf-8 by default. as we set charset always to utf-8
$mailObject -> Sender = $activeMailProfile [ 'ident_email' ];
2016-07-21 13:36:43 +02:00
$mailObject -> setFrom ( $activeMailProfile [ 'ident_email' ], Mail :: generateIdentityString ( $activeMailProfile , false ));
2016-06-10 12:11:56 +02:00
$mailObject -> addHeader ( 'X-Mailer' , 'mail-Activesync' );
2015-06-16 08:37:16 +02:00
// prepare addressee list; moved the adding of addresses to the mailobject down
// to
2016-03-28 20:51:38 +02:00
foreach ( Mail :: parseAddressList ( $mailObject -> getHeader ( " To " )) as $addressObject ) {
2015-06-16 08:37:16 +02:00
if ( ! $addressObject -> valid ) continue ;
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ) Header Sentmail To: " . array2string ( $addressObject ) );
2015-06-16 08:37:16 +02:00
//$mailObject->AddAddress($addressObject->mailbox. ($addressObject->host ? '@'.$addressObject->host : ''),$addressObject->personal);
$toMailAddr [] = imap_rfc822_write_address ( $addressObject -> mailbox , $addressObject -> host , $addressObject -> personal );
}
// CC
2016-03-28 20:51:38 +02:00
foreach ( Mail :: parseAddressList ( $mailObject -> getHeader ( " Cc " )) as $addressObject ) {
2015-06-16 08:37:16 +02:00
if ( ! $addressObject -> valid ) continue ;
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ) Header Sentmail CC: " . array2string ( $addressObject ) );
2015-06-16 08:37:16 +02:00
//$mailObject->AddCC($addressObject->mailbox. ($addressObject->host ? '@'.$addressObject->host : ''),$addressObject->personal);
$ccMailAddr [] = imap_rfc822_write_address ( $addressObject -> mailbox , $addressObject -> host , $addressObject -> personal );
}
// BCC
2016-10-19 12:01:38 +02:00
foreach ( $mailObject -> getAddresses ( 'bcc' ) as $addressObject ) {
2015-06-16 08:37:16 +02:00
if ( ! $addressObject -> valid ) continue ;
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ) Header Sentmail BCC: " . array2string ( $addressObject ) );
2015-06-16 08:37:16 +02:00
//$mailObject->AddBCC($addressObject->mailbox. ($addressObject->host ? '@'.$addressObject->host : ''),$addressObject->personal);
$bccMailAddr [] = imap_rfc822_write_address ( $addressObject -> mailbox , $addressObject -> host , $addressObject -> personal );
}
$mailObject -> clearAllRecipients ();
$use_orgbody = false ;
$k = 'Content-Type' ;
$ContentType = $mailObject -> getHeader ( 'Content-Type' );
//error_log(__METHOD__.__LINE__." Header Sentmail original Header (filtered): " . $k. " = ".trim($ContentType));
// if the message is a multipart message, then we should use the sent body
if ( preg_match ( " /multipart/i " , $ContentType )) {
$use_orgbody = true ;
}
// save the original content-type header for the body part when forwarding
2015-06-23 17:36:12 +02:00
if ( $smartdata_task == 'forward' && $smartdata -> source -> itemid && ! $use_orgbody ) {
2015-06-16 08:37:16 +02:00
//continue; // ignore
}
// horde/egw_ mailer does everything as utf-8, the following should not be needed
//$org_charset = $ContentType;
//$ContentType = preg_replace("/charset=([A-Za-z0-9-\"']+)/", "charset=\"utf-8\"", $ContentType);
// if the message is a multipart message, then we should use the sent body
2015-06-23 17:36:12 +02:00
if (( $smartdata_task == 'new' || $smartdata_task == 'reply' || $smartdata_task == 'forward' ) &&
(( isset ( $smartdata -> replacemime ) && $smartdata -> replacemime == true ) ||
2015-06-16 08:37:16 +02:00
$k == " Content-Type " && preg_match ( " /multipart/i " , $ContentType ))) {
$use_orgbody = true ;
}
$Body = $AltBody = " " ;
// get body of the transmitted message
// if this is a simple message, no structure at all
if ( preg_match ( " /text/i " , $ContentType ))
{
$simpleBodyType = ( preg_match ( " /html/i " , $ContentType ) ? 'text/html' : 'text/plain' );
$bodyObj = $mailObject -> findBody ( preg_match ( " /html/i " , $ContentType ) ? 'html' : 'plain' );
$body = preg_replace ( " /(<|<)*(([ \ w \ .,-.,_.,0-9.]+)@([ \ w \ .,-.,_.,0-9.]+))(>|>)*/i " , " [ $ 2] " , $bodyObj ? $bodyObj -> getContents () : null );
if ( $simpleBodyType == " text/plain " )
{
$Body = $body ;
$AltBody = " <pre> " . nl2br ( $body ) . " </pre> " ;
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ) fetched Body as : " . $simpleBodyType . '=> Created AltBody' );
2015-06-16 08:37:16 +02:00
}
else
{
$AltBody = $body ;
2016-05-03 21:17:44 +02:00
$Body = trim ( Api\Mail\Html :: convertHTMLToText ( $body ));
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ) fetched Body as : " . $simpleBodyType . '=> Created Body' );
2015-06-16 08:37:16 +02:00
}
}
else
{
// if this is a structured message
// prefer plain over html
$Body = preg_replace ( " /(<|<)*(([ \ w \ .,-.,_.,0-9.]+)@([ \ w \ .,-.,_.,0-9.]+))(>|>)*/i " , " [ $ 2] " ,
( $text_body = $mailObject -> findBody ( 'plain' )) ? $text_body -> getContents () : null );
$AltBody = preg_replace ( " /(<|<)*(([ \ w \ .,-.,_.,0-9.]+)@([ \ w \ .,-.,_.,0-9.]+))(>|>)*/i " , " [ $ 2] " ,
( $html_body = $mailObject -> findBody ( 'html' )) ? $html_body -> getContents () : null );
}
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 1 && $Body ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ) fetched Body as with MessageContentType: " . $ContentType . '=>' . $Body );
if ( $this -> debugLevel > 1 && $AltBody ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ) fetched AltBody as with MessageContentType: " . $ContentType . '=>' . $AltBody );
2015-06-16 08:37:16 +02:00
//error_log(__METHOD__.__LINE__.array2string($mailObject));
// if this is a multipart message with a boundary, we must use the original body
2015-10-13 17:06:04 +02:00
//if ($this->debugLevel>2) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' mailObject after Inital Parse:'.array2string($mailObject));
2015-06-16 08:37:16 +02:00
if ( $use_orgbody ) {
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ) use_orgbody = true ContentType: " . $ContentType );
2015-06-16 08:37:16 +02:00
// if it is a ClientSideMeetingRequest, we report it as send at all times
2015-10-22 19:24:14 +02:00
if (( $cal_body = $mailObject -> findBody ( 'calendar' )) &&
( $cSMRMethod = $cal_body -> getContentTypeParameter ( 'method' )))
2015-06-16 08:37:16 +02:00
{
2015-10-22 19:24:14 +02:00
if ( $cSMRMethod == 'REPLY' && class_exists ( 'calendar_ical' ))
2015-06-16 08:37:16 +02:00
{
2015-10-22 19:24:14 +02:00
$organizer = calendar_ical :: getIcalOrganizer ( $cal_body -> getContents ());
2015-06-16 08:37:16 +02:00
}
2015-10-22 19:24:14 +02:00
if ( $this -> debugLevel ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ) we have a Client Side Meeting Request from organizer= $organizer " );
2015-06-16 08:37:16 +02:00
$ClientSideMeetingRequest = true ;
}
}
// now handle the addressee list
$toCount = 0 ;
//error_log(__METHOD__.__LINE__.array2string($toMailAddr));
foreach (( array ) $toMailAddr as $address ) {
2021-03-21 18:27:09 +01:00
foreach ( Mail :: parseAddressList (( function_exists ( 'get_magic_quotes_gpc' ) && get_magic_quotes_gpc () ? stripslashes ( $address ) : $address )) as $addressObject ) {
2015-06-16 08:37:16 +02:00
$emailAddress = $addressObject -> mailbox . ( $addressObject -> host ? '@' . $addressObject -> host : '' );
2015-10-22 19:24:14 +02:00
if ( $ClientSideMeetingRequest === true && $allowSendingInvitations == 'sendifnocalnotif' &&
calendar_boupdate :: email_update_requested ( $emailAddress , isset ( $cSMRMethod ) ? $cSMRMethod : 'REQUEST' ,
2024-02-08 10:24:39 +01:00
! empty ( $organizer ) && ! strcasecmp ( $emailAddress , $organizer ) ? 'CHAIR' : '' ))
2015-10-22 19:24:14 +02:00
{
2024-02-08 10:24:39 +01:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ) skiping mail to organizer " . json_encode ( $organizer ? ? null ) . " , as it will be send by calendar app " );
2015-10-22 19:24:14 +02:00
continue ;
}
2015-06-16 08:37:16 +02:00
$mailObject -> AddAddress ( $emailAddress , $addressObject -> personal );
$toCount ++ ;
}
}
$ccCount = 0 ;
2024-02-08 10:24:39 +01:00
foreach ( $ccMailAddr ? ? [] as $address ) {
2021-03-21 18:27:09 +01:00
foreach ( Mail :: parseAddressList (( function_exists ( 'get_magic_quotes_gpc' ) && get_magic_quotes_gpc () ? stripslashes ( $address ) : $address )) as $addressObject ) {
2015-06-16 08:37:16 +02:00
$emailAddress = $addressObject -> mailbox . ( $addressObject -> host ? '@' . $addressObject -> host : '' );
if ( $ClientSideMeetingRequest === true && $allowSendingInvitations == 'sendifnocalnotif' && calendar_boupdate :: email_update_requested ( $emailAddress )) continue ;
$mailObject -> AddCC ( $emailAddress , $addressObject -> personal );
$ccCount ++ ;
}
}
$bccCount = 0 ;
2024-02-08 10:24:39 +01:00
foreach ( $bccMailAddr ? ? [] as $address ) {
2021-03-21 18:27:09 +01:00
foreach ( Mail :: parseAddressList (( function_exists ( 'get_magic_quotes_gpc' ) && get_magic_quotes_gpc () ? stripslashes ( $address ) : $address )) as $addressObject ) {
2015-06-16 08:37:16 +02:00
$emailAddress = $addressObject -> mailbox . ( $addressObject -> host ? '@' . $addressObject -> host : '' );
if ( $ClientSideMeetingRequest === true && $allowSendingInvitations == 'sendifnocalnotif' && calendar_boupdate :: email_update_requested ( $emailAddress )) continue ;
$mailObject -> AddBCC ( $emailAddress , $addressObject -> personal );
$bccCount ++ ;
}
}
2015-10-22 19:24:14 +02:00
// typical organizer reply will end here with nothing send --> return true, because we suppressed the send above
if ( $toCount + $ccCount + $bccCount == 0 )
{
2024-02-08 10:24:39 +01:00
return $ClientSideMeetingRequest && $allowSendingInvitations === 'sendifnocalnotif' && ! empty ( $organizer ) ? true : 0 ; // noone to send mail to
2015-10-22 19:24:14 +02:00
}
2015-06-16 08:37:16 +02:00
if ( $ClientSideMeetingRequest === true && $allowSendingInvitations === false ) return true ;
// as we use our mailer (horde mailer) it is detecting / setting the mimetype by itself while creating the mail
/*
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 2 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' retrieved Body:' . $body );
2015-06-16 08:37:16 +02:00
$body = str_replace ( " \r " ,(( preg_match ( " ^text/html^i " , $ContentType )) ? '<br>' : " " ), $body ); // what is this for?
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 2 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' retrieved Body (modified):' . $body );
2015-06-16 08:37:16 +02:00
*/
2016-08-05 15:12:21 +02:00
// actually use prepared signature --------------------collected earlier--------------------------
2015-06-16 08:37:16 +02:00
$isreply = $isforward = false ;
// reply ---------------------------------------------------------------------------
2015-06-23 17:36:12 +02:00
if ( $smartdata_task == 'reply' && isset ( $smartdata -> source -> itemid ) &&
isset ( $smartdata -> source -> folderid ) && $smartdata -> source -> itemid && $smartdata -> source -> folderid &&
( ! isset ( $smartdata -> replacemime ) ||
( isset ( $smartdata -> replacemime ) && $smartdata -> replacemime == false )))
2015-06-16 08:37:16 +02:00
{
// now get on, and fetch the original mail
2015-06-23 17:36:12 +02:00
$uid = $smartdata -> source -> itemid ;
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ) IMAP Smartreply is called with FolderID: " . $smartdata -> source -> folderid . ' and ItemID:' . $smartdata -> source -> itemid );
2015-06-23 17:36:12 +02:00
$this -> splitID ( $smartdata -> source -> folderid , $account , $folder );
2015-06-16 08:37:16 +02:00
$this -> mail -> reopen ( $folder );
$bodyStruct = $this -> mail -> getMessageBody ( $uid , 'html_only' );
2015-10-23 12:02:18 +02:00
$bodyBUFFHtml = $this -> mail -> getdisplayableBody ( $this -> mail , $bodyStruct , true , false );
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 3 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' html_only:' . $bodyBUFFHtml );
2015-06-16 08:37:16 +02:00
if ( $bodyBUFFHtml != " " && ( is_array ( $bodyStruct ) && $bodyStruct [ 0 ][ 'mimeType' ] == 'text/html' )) {
// may be html
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ) MIME Body " . ' Type:html (fetched with html_only):' . $bodyBUFFHtml );
2015-06-16 08:37:16 +02:00
$AltBody = $AltBody . " </br> " . $bodyBUFFHtml . $sigTextHtml ;
$isreply = true ;
}
// plain text Message part
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ) MIME Body " . ' Type:plain, fetch text:' );
2015-06-16 08:37:16 +02:00
// if the new part of the message is html, we must preserve it, and handle that the original mail is text/plain
$bodyStruct = $this -> mail -> getMessageBody ( $uid , 'never_display' ); //'never_display');
2015-10-23 12:02:18 +02:00
$bodyBUFF = $this -> mail -> getdisplayableBody ( $this -> mail , $bodyStruct , false , false );
2015-06-16 08:37:16 +02:00
if ( $bodyBUFF != " " && ( is_array ( $bodyStruct ) && $bodyStruct [ 0 ][ 'mimeType' ] == 'text/plain' )) {
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ) MIME Body " . ' Type:plain (fetched with never_display):' . $bodyBUFF );
2015-06-16 08:37:16 +02:00
$Body = $Body . " \r \n " . $bodyBUFF . $sigTextPlain ;
$isreply = true ;
}
if ( ! empty ( $bodyBUFF ) && empty ( $bodyBUFFHtml ) && ! empty ( $AltBody ))
{
$isreply = true ;
$AltBody = $AltBody . " </br><pre> " . nl2br ( $bodyBUFF ) . '</pre>' . $sigTextHtml ;
2016-05-03 21:17:44 +02:00
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . " no Api \ Html Body found use modified plaintext body for txt/html: " . $AltBody );
2015-06-16 08:37:16 +02:00
}
}
// how to forward and other prefs
$preferencesArray =& $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'mail' ];
// forward -------------------------------------------------------------------------
2015-06-23 17:36:12 +02:00
if ( $smartdata_task == 'forward' && isset ( $smartdata -> source -> itemid ) &&
isset ( $smartdata -> source -> folderid ) && $smartdata -> source -> itemid && $smartdata -> source -> folderid &&
( ! isset ( $smartdata -> replacemime ) ||
( isset ( $smartdata -> replacemime ) && $smartdata -> replacemime == false )))
2015-06-16 08:37:16 +02:00
{
//force the default for the forwarding -> asmail
2023-08-23 14:56:22 +02:00
if ( empty ( $preferencesArray [ 'message_forwarding' ]))
{
2015-06-16 08:37:16 +02:00
$preferencesArray [ 'message_forwarding' ] = 'asmail' ;
}
// construct the uid of the message out of the itemid - seems to be the uid, no construction needed
2015-06-23 17:36:12 +02:00
$uid = $smartdata -> source -> itemid ;
2023-08-23 14:56:22 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " )IMAP Smartforward is called with FolderID: " . $smartdata -> source -> folderid . ' and ItemID:' . $smartdata -> source -> itemid );
2015-06-23 17:36:12 +02:00
$this -> splitID ( $smartdata -> source -> folderid , $account , $folder );
2015-06-16 08:37:16 +02:00
$this -> mail -> reopen ( $folder );
// receive entire mail (header + body)
// get message headers for specified message
2021-03-30 20:34:34 +02:00
$headers = $this -> mail -> getMessageEnvelope ( $uid , '' , true , $folder );
2015-06-16 08:37:16 +02:00
// build a new mime message, forward entire old mail as file
if ( $preferencesArray [ 'message_forwarding' ] == 'asmail' )
{
2021-03-30 20:34:34 +02:00
$rawHeader = $this -> mail -> getMessageRawHeader ( $smartdata -> source -> itemid , '' , $folder );
$rawBody = $this -> mail -> getMessageRawBody ( $smartdata -> source -> itemid , '' , $folder );
2015-06-16 08:37:16 +02:00
$mailObject -> AddStringAttachment ( $rawHeader . $rawBody , $headers [ 'SUBJECT' ] . '.eml' , 'message/rfc822' );
2023-08-23 14:56:22 +02:00
$mailObject -> addHeader ( 'Content-Type' , 'multipart/mixed' );
2015-06-16 08:37:16 +02:00
$AltBody = $AltBody . " </br> " . lang ( " See Attachments for Content of the Orignial Mail " ) . $sigTextHtml ;
$Body = $Body . " \r \n " . lang ( " See Attachments for Content of the Orignial Mail " ) . $sigTextPlain ;
$isforward = true ;
}
else
{
// now get on, and fetch the original mail
2015-06-23 17:36:12 +02:00
$uid = $smartdata -> source -> itemid ;
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ) IMAP Smartreply is called with FolderID: " . $smartdata -> source -> folderid . ' and ItemID:' . $smartdata -> source -> itemid );
2015-06-23 17:36:12 +02:00
$this -> splitID ( $smartdata -> source -> folderid , $account , $folder );
2015-06-16 08:37:16 +02:00
$this -> mail -> reopen ( $folder );
$bodyStruct = $this -> mail -> getMessageBody ( $uid , 'html_only' );
2015-10-23 12:02:18 +02:00
$bodyBUFFHtml = $this -> mail -> getdisplayableBody ( $this -> mail , $bodyStruct , true , false );
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' html_only:' . $bodyBUFFHtml );
2015-06-16 08:37:16 +02:00
if ( $bodyBUFFHtml != " " && ( is_array ( $bodyStruct ) && $bodyStruct [ 0 ][ 'mimeType' ] == 'text/html' )) {
// may be html
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ) MIME Body " . ' Type:html (fetched with html_only):' . $bodyBUFFHtml );
2015-06-16 08:37:16 +02:00
$AltBody = $AltBody . " </br> " . $bodyBUFFHtml . $sigTextHtml ;
$isforward = true ;
}
// plain text Message part
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ) MIME Body " . ' Type:plain, fetch text:' );
2015-06-16 08:37:16 +02:00
// if the new part of the message is html, we must preserve it, and handle that the original mail is text/plain
$bodyStruct = $this -> mail -> getMessageBody ( $uid , 'never_display' ); //'never_display');
2015-10-23 12:02:18 +02:00
$bodyBUFF = $this -> mail -> getdisplayableBody ( $this -> mail , $bodyStruct , false , false );
2015-06-16 08:37:16 +02:00
if ( $bodyBUFF != " " && ( is_array ( $bodyStruct ) && $bodyStruct [ 0 ][ 'mimeType' ] == 'text/plain' )) {
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ) MIME Body " . ' Type:plain (fetched with never_display):' . $bodyBUFF );
2015-06-16 08:37:16 +02:00
$Body = $Body . " \r \n " . $bodyBUFF . $sigTextPlain ;
$isforward = true ;
}
if ( ! empty ( $bodyBUFF ) && empty ( $bodyBUFFHtml ) && ! empty ( $AltBody ))
{
$AltBody = $AltBody . " </br><pre> " . nl2br ( $bodyBUFF ) . '</pre>' . $sigTextHtml ;
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . " no html Body found use modified plaintext body for txt/html: " . $AltBody );
2015-06-16 08:37:16 +02:00
$isforward = true ;
}
// get all the attachments and add them too.
// start handle Attachments
2017-04-26 17:23:48 +02:00
$attachments = $this -> mail -> getMessageAttachments ( $uid , null , null , true , false , true , $folder );
2015-06-16 08:37:16 +02:00
$attachmentNames = false ;
if ( is_array ( $attachments ) && count ( $attachments ) > 0 )
{
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' gather Attachments for BodyCreation of/for MessageID:' . $uid . ' found:' . count ( $attachments ));
2015-06-16 08:37:16 +02:00
foreach (( array ) $attachments as $key => $attachment )
{
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' Key:' . $key . '->' . array2string ( $attachment ));
2015-06-16 08:37:16 +02:00
$attachmentNames .= $attachment [ 'name' ] . " \n " ;
$attachmentData = $this -> mail -> getAttachment ( $uid , $attachment [ 'partID' ], 0 , false , false , $folder );
2021-03-30 20:34:34 +02:00
$x = $mailObject -> AddStringAttachment ( $attachmentData [ 'attachment' ], $attachment [ 'name' ], $attachment [ 'mimeType' ]);
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' added part with number:' . $x );
2015-06-16 08:37:16 +02:00
}
2024-03-04 16:27:02 +01:00
// if we have attachment(s), we need to use multipart/mixed
$mailObject -> addHeader ( 'Content-Type' , 'multipart/mixed' );
2015-06-16 08:37:16 +02:00
}
}
} // end forward
// add signature, in case its not already added in forward or reply
if ( ! $isreply && ! $isforward )
{
2016-08-05 15:12:21 +02:00
//error_log(__METHOD__.__LINE__.'adding Signature');
$Body = $Body . $sigTextPlain ;
$AltBody = $AltBody . $sigTextHtml ;
2015-06-16 08:37:16 +02:00
}
// now set the body
if ( $AltBody && ( $html_body = $mailObject -> findBody ( 'html' )))
{
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 1 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' -> ' . $AltBody );
2016-08-05 15:12:21 +02:00
//error_log(__METHOD__.__LINE__.' html:'.$AltBody);
2015-06-16 08:37:16 +02:00
$html_body -> setContents ( $AltBody , array ( 'encoding' => Horde_Mime_Part :: DEFAULT_ENCODING ));
}
if ( $Body && ( $text_body = $mailObject -> findBody ( 'plain' )))
{
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 1 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' -> ' . $Body );
2016-08-05 15:12:21 +02:00
//error_log(__METHOD__.__LINE__.' text:'.$Body);
2015-06-16 08:37:16 +02:00
$text_body -> setContents ( $Body , array ( 'encoding' => Horde_Mime_Part :: DEFAULT_ENCODING ));
}
//advanced debugging
// Horde SMTP Class uses utf-8 by default.
2016-06-07 13:00:47 +02:00
//ZLog::Write(LOGLEVEL_DEBUG, "IMAP-SendMail: parsed message: ". print_r($message,1));
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 2 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ): MailObject: " . array2string ( $mailObject ));
2015-06-16 08:37:16 +02:00
// set a higher timeout for big messages
@ set_time_limit ( 120 );
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' -> ' . ' about to send ....' );
2015-06-16 08:37:16 +02:00
// send
$send = true ;
try {
$mailObject -> Send ();
}
2015-10-22 19:24:14 +02:00
catch ( Exception $e ) {
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ) The email could not be sent. Last-SMTP-error: " . $e -> getMessage ());
2015-06-16 08:37:16 +02:00
$send = false ;
}
2015-06-23 17:36:12 +02:00
if (( $smartdata_task == 'reply' || $smartdata_task == 'forward' ) && $send == true )
2015-06-16 08:37:16 +02:00
{
2015-06-23 17:36:12 +02:00
$uid = $smartdata -> source -> itemid ;
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' tASK:' . $smartdata_task . " FolderID: " . $smartdata -> source -> folderid . ' and ItemID:' . $smartdata -> source -> itemid );
2015-06-23 17:36:12 +02:00
$this -> splitID ( $smartdata -> source -> folderid , $account , $folder );
2015-06-16 08:37:16 +02:00
//error_log(__METHOD__.__LINE__.' Folder:'.$folder.' Uid:'.$uid);
$this -> mail -> reopen ( $folder );
// if the draft folder is a starting part of the messages folder, the draft message will be deleted after the send
// unless your templatefolder is a subfolder of your draftfolder, and the message is in there
if ( $this -> mail -> isDraftFolder ( $folder ) && ! $this -> mail -> isTemplateFolder ( $folder ))
{
$this -> mail -> deleteMessages ( array ( $uid ), $folder );
} else {
$this -> mail -> flagMessages ( " answered " , array ( $uid ), $folder );
2015-06-23 17:36:12 +02:00
if ( $smartdata_task == " forward " )
2015-06-16 08:37:16 +02:00
{
$this -> mail -> flagMessages ( " forwarded " , array ( $uid ), $folder );
}
}
}
$asf = ( $send ? true : false ); // initalize accordingly
2015-07-02 14:09:19 +02:00
if ( /*($smartdata->saveinsent==1 || !isset($smartdata->saveinsent)) && */ $send == true && $this -> mail -> mailPreferences [ 'sendOptions' ] != 'send_only' )
2015-06-16 08:37:16 +02:00
{
$asf = false ;
$sentFolder = $this -> mail -> getSentFolder ();
if ( $this -> _sentID ) {
$folderArray [] = $this -> _sentID ;
}
else if ( isset ( $sentFolder ) && $sentFolder != 'none' )
{
$folderArray [] = $sentFolder ;
}
// No Sent folder set, try defaults
else
{
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ) IMAP-SendMail: No Sent mailbox set " );
2015-06-16 08:37:16 +02:00
// we dont try guessing
$asf = true ;
}
if ( count ( $folderArray ) > 0 ) {
2024-02-08 10:24:39 +01:00
foreach ( $bccMailAddr ? ? [] as $address ) {
2021-03-21 18:27:09 +01:00
foreach ( Mail :: parseAddressList (( function_exists ( 'get_magic_quotes_gpc' ) && get_magic_quotes_gpc () ? stripslashes ( $address ) : $address )) as $addressObject ) {
2015-06-16 08:37:16 +02:00
$emailAddress = $addressObject -> mailbox . ( $addressObject -> host ? '@' . $addressObject -> host : '' );
$mailAddr [] = array ( $emailAddress , $addressObject -> personal );
}
}
2016-10-18 15:59:32 +02:00
//$BCCmail='';
2021-11-10 12:24:08 +01:00
if ( ! empty ( $mailAddr )) $mailObject -> forceBccHeader ();
2016-10-18 15:59:32 +02:00
//$BCCmail = $mailObject->AddrAppend("Bcc",$mailAddr);
2015-06-16 08:37:16 +02:00
foreach ( $folderArray as $folderName ) {
if ( $this -> mail -> isSentFolder ( $folderName )) {
$flags = '\\Seen' ;
} elseif ( $this -> mail -> isDraftFolder ( $folderName )) {
$flags = '\\Draft' ;
} else {
$flags = '' ;
}
$asf = true ;
2015-10-13 17:06:04 +02:00
//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.'->'.array2string($this->mail->icServer));
2015-06-16 08:37:16 +02:00
$this -> mail -> openConnection ( self :: $profileID , false );
if ( $this -> mail -> folderExists ( $folderName )) {
try
{
$this -> mail -> appendMessage ( $folderName , $mailObject -> getRaw (), null ,
$flags );
}
2016-05-03 21:17:44 +02:00
catch ( Api\Exception\WrongUserinput $e )
2015-06-16 08:37:16 +02:00
{
//$asf = false;
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . '->' . lang ( " Import of message %1 failed. Could not save message to folder %2 due to: %3 " , $mailObject -> getHeader ( 'Subject' ), $folderName , $e -> getMessage ()));
2015-06-16 08:37:16 +02:00
}
}
else
{
//$asf = false;
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . '->' . lang ( " Import of message %1 failed. Destination Folder %2 does not exist. " , $mailObject -> getHeader ( 'Subject' ), $folderName ));
2015-06-16 08:37:16 +02:00
}
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( " . __LINE__ . " ): Outgoing mail saved in configured 'Sent' folder ' " . $folderName . " ': " . (( $asf ) ? " success " : " failed " ));
2015-06-16 08:37:16 +02:00
}
//$this->mail->closeConnection();
}
}
2015-06-23 17:36:12 +02:00
$this -> debugLevel = 0 ;
2015-06-16 08:37:16 +02:00
if ( $send && $asf )
{
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' -> send successfully' );
2015-06-16 08:37:16 +02:00
return true ;
}
else
{
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " returning " . ( $ClientSideMeetingRequest ? true : 120 ) . " (MailSubmissionFailed) " . ( $ClientSideMeetingRequest ? " is ClientSideMeetingRequest (we ignore the failure) " : " " ));
2015-06-16 08:37:16 +02:00
return ( $ClientSideMeetingRequest ? true : 120 ); //MAIL Submission failed, see MS-ASCMD
}
}
/**
* For meeting requests ( iCal attachments with method = 'request' ) we call calendar plugin with iCal to get SyncMeetingRequest object ,
* and do NOT return the attachment itself !
*
* @ param string $folderid
* @ param string $id
* @ param ContentParameters $contentparameters parameters of the requested message ( truncation , mimesupport etc )
* object with attributes foldertype , truncation , rtftruncation , conflict , filtertype , bodypref , deletesasmoves , filtertype , contentclass , mimesupport , conversationmode
* bodypref object with attributes : ] truncationsize , allornone , preview
* @ return $messageobject | boolean false on error
*/
public function GetMessage ( $folderid , $id , $contentparameters )
{
2015-07-02 14:09:19 +02:00
//$this->debugLevel=4;
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' FolderID:' . $folderid . ' ID:' . $id . ' ContentParams=' . array2string ( $contentparameters ));
2015-06-16 08:37:16 +02:00
$truncsize = Utils :: GetTruncSize ( $contentparameters -> GetTruncation ());
$mimesupport = $contentparameters -> GetMimeSupport ();
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " () truncsize= $truncsize , mimeSupport= " . array2string ( $mimesupport ));
2015-06-16 08:37:16 +02:00
$bodypreference = $contentparameters -> GetBodyPreference (); /* fmbiete's contribution r1528, ZP-320 */
2016-03-01 21:45:31 +01:00
// fix for z-push bug returning additional bodypreference type 4, even if only 1 is requested and mimessupport = 0
2021-11-14 10:27:12 +01:00
if ( ! $mimesupport && $bodypreference !== false && ( $key = array_search ( '4' , $bodypreference ))) unset ( $bodypreference [ $key ]);
2016-03-01 21:45:31 +01:00
2015-06-16 08:37:16 +02:00
//$this->debugLevel=4;
2016-03-28 20:51:38 +02:00
if ( ! isset ( $this -> mail )) $this -> mail = Mail :: getInstance ( false , self :: $profileID , true , false , true );
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' FolderID:' . $folderid . ' ID:' . $id . ' TruncSize:' . $truncsize . ' Bodypreference: ' . array2string ( $bodypreference ));
2015-06-16 08:37:16 +02:00
$account = $_folderName = $xid = null ;
$this -> splitID ( $folderid , $account , $_folderName , $xid );
$this -> mail -> reopen ( $_folderName );
2016-07-08 10:33:58 +02:00
$messages = $this -> fetchMessages ( $folderid , NULL , $id , true ); // true: return all headers
$headers = $messages [ $id ];
if ( $this -> debugLevel > 3 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . array2string ( $headers ));
2015-06-16 08:37:16 +02:00
// StatMessage should reopen the folder in question, so we dont need folderids in the following statements.
2016-07-08 10:33:58 +02:00
if ( $headers )
2015-06-16 08:37:16 +02:00
{
2016-07-08 10:33:58 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . " Message $id with stat " . array2string ( $headers ));
2015-06-16 08:37:16 +02:00
// initialize the object
$output = new SyncMail ();
//$rawHeaders = $this->mail->getMessageRawHeader($id);
// simple style
// start AS12 Stuff (bodypreference === false) case = old behaviour
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' for message with ID:' . $id . ' with headers:' . array2string ( $headers ));
2015-07-17 10:41:26 +02:00
2015-06-16 08:37:16 +02:00
if ( $bodypreference === false ) {
$bodyStruct = $this -> mail -> getMessageBody ( $id , 'only_if_no_text' , '' , null , true , $_folderName );
2015-10-23 12:02:18 +02:00
$raw_body = $this -> mail -> getdisplayableBody ( $this -> mail , $bodyStruct , false , false );
2015-06-16 08:37:16 +02:00
//$body = html_entity_decode($body,ENT_QUOTES,$this->mail->detect_encoding($body));
if ( stripos ( $raw_body , '<style' ) !== false ) $body = preg_replace ( " /<style.*?< \ /style>/is " , " " , $raw_body ); // in case there is only a html part
// remove all other html
$body = strip_tags ( $raw_body );
if ( strlen ( $body ) > $truncsize ) {
2015-10-13 18:15:32 +02:00
$body = Utils :: Utf8_truncate ( $body , $truncsize );
2015-06-16 08:37:16 +02:00
$output -> bodytruncated = 1 ;
}
else
{
$output -> bodytruncated = 0 ;
}
$output -> bodysize = strlen ( $body );
$output -> body = $body ;
}
else // style with bodypreferences
{
2015-07-17 10:41:26 +02:00
//Select body type preference
$bpReturnType = 1 ; //SYNC_BODYPREFERENCE_PLAIN;
if ( $bodypreference !== false ) {
2015-10-13 17:06:04 +02:00
// bodypreference can occur multiple times
// usually we would use Utils::GetBodyPreferenceBestMatch($bodypreference);
$bpReturnType = Utils :: GetBodyPreferenceBestMatch ( $bodypreference );
/*
foreach ( $bodypreference as $bpv )
{
// we use the last, or MIMEMESSAGE when present
$bpReturnType = $bpv ;
if ( $bpReturnType == SYNC_BODYPREFERENCE_MIME ) break ;
}
*/
2015-07-17 10:41:26 +02:00
}
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . " getBodyPreferenceBestMatch: " . array2string ( $bpReturnType ));
2015-06-16 08:37:16 +02:00
// set the protocoll class
2016-06-23 17:51:03 +02:00
$output -> asbody = new SyncBaseBody ();
2016-06-21 09:33:44 +02:00
// return full mime-message without any (charset) conversation directly as stream
if ( $bpReturnType == SYNC_BODYPREFERENCE_MIME )
2015-06-16 08:37:16 +02:00
{
2015-10-13 17:06:04 +02:00
//SYNC_BODYPREFERENCE_MIME
2016-06-21 09:33:44 +02:00
$output -> asbody -> type = SYNC_BODYPREFERENCE_MIME ;
$stream = $this -> mail -> getMessageRawBody ( $id , '' , $_folderName , true );
$fstat = fstat ( $stream );
fseek ( $stream , 0 , SEEK_SET );
$output -> asbody -> data = $stream ;
$output -> asbody -> estimatedDataSize = $fstat [ 'size' ];
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . " bodypreference 4=SYNC_BODYPREFERENCE_MIME=full mime message requested, size= $fstat[size] " );
2015-06-16 08:37:16 +02:00
}
2016-06-21 09:33:44 +02:00
else
2015-06-16 08:37:16 +02:00
{
2016-06-21 09:33:44 +02:00
// fetch the body (try to gather data only once)
$css = '' ;
2024-03-05 14:29:41 +01:00
try {
$bodyStruct = $this -> mail -> getMessageBody ( $id , 'html_only' , '' , null , true , $_folderName );
if ( $this -> debugLevel > 2 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' html_only Struct:' . array2string ( $bodyStruct ));
$body = $this -> mail -> getdisplayableBody ( $this -> mail , $bodyStruct , true , false );
if ( $this -> debugLevel > 3 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' html_only:' . $body );
if ( $body != " " && ( is_array ( $bodyStruct ) && $bodyStruct [ 0 ][ 'mimeType' ] == 'text/html' )) {
// may be html
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , " MIME Body " . ' Type:html (fetched with html_only)' );
$css = $this -> mail -> getStyles ( $bodyStruct );
$output -> nativebodytype = 2 ;
} else {
// plain text Message
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , " MIME Body " . ' Type:plain, fetch text (HTML, if no text available)' );
$output -> nativebodytype = 1 ;
$bodyStruct = $this -> mail -> getMessageBody ( $id , 'never_display' , '' , null , true , $_folderName ); //'only_if_no_text');
if ( $this -> debugLevel > 3 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' plain text Struct:' . array2string ( $bodyStruct ));
$body = $this -> mail -> getdisplayableBody ( $this -> mail , $bodyStruct , false , false );
if ( $this -> debugLevel > 3 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' never display html(plain text only):' . $body );
2016-06-21 09:33:44 +02:00
}
2024-03-05 14:29:41 +01:00
// whatever format decode (using the correct encoding)
if ( $this -> debugLevel > 3 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . " MIME Body " . ' Type:' . ( $output -> nativebodytype == 2 ? ' html ' : ' plain ' ) . $body );
//$body = html_entity_decode($body,ENT_QUOTES,$this->mail->detect_encoding($body));
// prepare plaintextbody
$plainBody = '' ;
if ( $output -> nativebodytype == 2 )
2016-06-21 09:33:44 +02:00
{
2024-03-05 14:29:41 +01:00
$bodyStructplain = $this -> mail -> getMessageBody ( $id , 'never_display' , '' , null , true , $_folderName ); //'only_if_no_text');
if ( isset ( $bodyStructplain [ 0 ]) && isset ( $bodyStructplain [ 0 ][ 'error' ]) && $bodyStructplain [ 0 ][ 'error' ] == 1 )
{
$plainBody = Api\Mail\Html :: convertHTMLToText ( $body ); // always display with preserved HTML
}
else
{
$plainBody = $this -> mail -> getdisplayableBody ( $this -> mail , $bodyStructplain , false , false );
}
2016-06-21 09:33:44 +02:00
}
2024-03-05 14:29:41 +01:00
//if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG, "MIME Body".$body);
$plainBody = preg_replace ( " /<style.*?< \ /style>/is " , " " , ( strlen ( $plainBody ) ? $plainBody : $body ));
// remove all other html
$plainBody = preg_replace ( " /<br.*>/is " , " \r \n " , $plainBody );
$plainBody = strip_tags ( $plainBody );
if ( $this -> debugLevel > 3 && $output -> nativebodytype == 1 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' Plain Text:' . $plainBody );
//$body = str_replace("\n","\r\n", str_replace("\r","",$body)); // do we need that?
2015-06-16 08:37:16 +02:00
2024-03-05 14:29:41 +01:00
if ( $bpReturnType == 2 ) //SYNC_BODYPREFERENCE_HTML
2016-06-21 09:33:44 +02:00
{
2024-03-05 14:29:41 +01:00
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , " HTML Body with requested pref 2 " );
// Send HTML if requested and native type was html
$output -> asbody -> type = 2 ;
$htmlbody = '<html>' .
'<head>' .
'<meta name="Generator" content="Z-Push">' .
'<meta http-equiv="Content-Type" content="text/html; charset=utf-8">' .
$css .
'</head>' .
'<body>' ;
if ( $output -> nativebodytype == 2 )
{
if ( $css ) Api\Mail\Html :: replaceTagsCompletley ( $body , 'style' );
// as we fetch html, and body is HTML, we may not need to handle this
$htmlbody .= $body ;
}
else
{
// html requested but got only plaintext, so fake html
$htmlbody .= str_replace ( " \n " , " <BR> " , str_replace ( " \r " , " <BR> " , str_replace ( " \r \n " , " <BR> " , $plainBody )));
}
$htmlbody .= '</body>' .
'</html>' ;
if ( isset ( $truncsize ) && strlen ( $htmlbody ) > $truncsize )
{
$htmlbody = Utils :: Utf8_truncate ( $htmlbody , $truncsize );
$output -> asbody -> truncated = 1 ;
}
// output->nativebodytype is used as marker that the original message was of type ... but is now converted to, as type 2 is requested.
$output -> nativebodytype = 2 ;
$output -> asbody -> data = StringStreamWrapper :: Open ( $htmlbody );
$output -> asbody -> estimatedDataSize = strlen ( $htmlbody );
2016-06-21 09:33:44 +02:00
}
else
{
2024-03-05 14:29:41 +01:00
// Send Plaintext as Fallback or if original body is plainttext
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , " Plaintext Body: " . $plainBody );
/* we use plainBody ( set above ) instead
$bodyStruct = $this -> mail -> getMessageBody ( $id , 'only_if_no_text' ); //'never_display');
$plain = $this -> mail -> getdisplayableBody ( $this -> mail , $bodyStruct );
$plain = html_entity_decode ( $plain , ENT_QUOTES , $this -> mail -> detect_encoding ( $plain ));
$plain = strip_tags ( $plain );
//$plain = str_replace("\n","\r\n",str_replace("\r","",$plain));
*/
$output -> asbody -> type = 1 ;
$output -> nativebodytype = 1 ;
if ( isset ( $truncsize ) &&
strlen ( $plainBody ) > $truncsize )
{
$plainBody = Utils :: Utf8_truncate ( $plainBody , $truncsize );
$output -> asbody -> truncated = 1 ;
}
$output -> asbody -> data = StringStreamWrapper :: Open (( string ) $plainBody !== '' ? $plainBody : ' ' );
$output -> asbody -> estimatedDataSize = strlen ( $plainBody );
2016-06-21 09:33:44 +02:00
}
2024-03-05 14:29:41 +01:00
// In case we have nothing for the body, send at least a blank...
// dw2412 but only in case the body is not rtf!
if ( $output -> asbody -> type != 3 && ! isset ( $output -> asbody -> data ))
2016-06-21 09:33:44 +02:00
{
2024-03-05 14:29:41 +01:00
$output -> asbody -> data = StringStreamWrapper :: Open ( " " );
$output -> asbody -> estimatedDataSize = 1 ;
2016-06-21 09:33:44 +02:00
}
2015-06-16 08:37:16 +02:00
}
2024-03-05 14:29:41 +01:00
catch ( Api\Exception\WrongParameter $e ) {
// we throw, if body is not found, e.g. whole message is a PDF or an image --> return an empty body (1 space)
2016-06-21 09:33:44 +02:00
$output -> asbody -> type = 1 ;
$output -> nativebodytype = 1 ;
2024-03-05 14:29:41 +01:00
$output -> asbody -> data = StringStreamWrapper :: Open ( $error = ' ' ); //$e->getMessage().' ('.$e->getCode().')');
$output -> asbody -> estimatedDataSize = strlen ( $error );
2015-06-16 08:37:16 +02:00
}
}
}
// end AS12 Stuff
2016-07-08 10:33:58 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' gather Header info:' . $headers [ 'subject' ] . ' from:' . $headers [ 'date' ]);
$output -> read = $headers [ " flags " ];
2015-10-13 17:06:04 +02:00
$output -> flag = new SyncMailFlags ();
2023-06-09 18:27:28 +02:00
if ( isset ( $headers [ 'flagged' ]) && $headers [ 'flagged' ] == 1 )
2015-10-13 17:06:04 +02:00
{
$output -> flag -> flagstatus = 2 ;
//$output->flag->flagtype = "Flag for Follow up";
} else {
$output -> flag -> flagstatus = 0 ;
}
2024-03-05 13:54:29 +01:00
if ( ! empty ( $headers [ 'forwarded' ]))
2015-10-13 17:06:04 +02:00
{
2024-03-05 13:54:29 +01:00
$output -> lastverbexecuted = AS_FORWARD ;
2015-10-13 17:06:04 +02:00
}
2024-03-05 13:54:29 +01:00
elseif ( ! empty ( $headers [ 'answered' ]))
2015-10-13 17:06:04 +02:00
{
2024-03-05 13:54:29 +01:00
$output -> lastverbexecuted = AS_REPLYTOSENDER ;
2015-10-13 17:06:04 +02:00
}
2016-07-08 10:33:58 +02:00
$output -> subject = $headers [ 'subject' ];
$output -> importance = $headers [ 'priority' ] > 3 ? 0 :
( $headers [ 'priority' ] < 3 ? 2 : 1 ) ;
2023-05-09 17:03:11 +02:00
$output -> datereceived = $this -> mail -> _strtotime ( $headers [ 'date' ], 'ts' , false ); // false = servertime
2024-02-08 10:24:39 +01:00
$output -> to = $headers [ 'to_address' ] ? ? null ;
2024-03-04 18:37:28 +01:00
if ( ! empty ( $headers [ 'additional_to_addresses' ]))
{
$output -> to = array_merge (( array ) $output -> to , $headers [ 'additional_to_addresses' ]);
}
2022-04-26 18:46:23 +02:00
if ( ! empty ( $headers [ 'to' ])) $output -> displayto = $headers [ 'to_address' ]; //$headers['FETCHED_HEADER']['to_name']
2016-07-08 10:33:58 +02:00
$output -> from = $headers [ 'sender_address' ];
2022-04-26 18:46:23 +02:00
if ( ! empty ( $headers [ 'cc_addresses' ])) $output -> cc = $headers [ 'cc_addresses' ];
if ( ! empty ( $headers [ 'reply_to_address' ])) $output -> reply_to = $headers [ 'reply_to_address' ];
2015-10-13 17:06:04 +02:00
2015-06-16 08:37:16 +02:00
$output -> messageclass = " IPM.Note " ;
2016-07-08 10:33:58 +02:00
if ( stripos ( $headers [ 'mimetype' ], 'multipart' ) !== false &&
stripos ( $headers [ 'mimetype' ], 'signed' ) !== false )
2015-06-16 08:37:16 +02:00
{
$output -> messageclass = " IPM.Note.SMIME.MultipartSigned " ;
}
2015-10-13 17:06:04 +02:00
if ( Request :: GetProtocolVersion () >= 12.0 ) {
$output -> contentclass = " urn:content-classes:message " ;
2015-06-16 08:37:16 +02:00
}
// start handle Attachments (include text/calendar multipart alternative)
$attachments = $this -> mail -> getMessageAttachments ( $id , $_partID = '' , $_structure = null , $fetchEmbeddedImages = true , $fetchTextCalendar = true , true , $_folderName );
2015-10-13 17:06:04 +02:00
// Attachments should not needed for MIME messages, so skip this part if bpReturnType==4
if ( /*$bpReturnType != SYNC_BODYPREFERENCE_MIME &&*/ is_array ( $attachments ) && count ( $attachments ) > 0 )
2015-06-16 08:37:16 +02:00
{
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' gather Attachments for MessageID:' . $id . ' found:' . count ( $attachments ));
2015-06-16 08:37:16 +02:00
//error_log(__METHOD__.__LINE__.array2string($attachments));
foreach ( $attachments as $key => $attach )
{
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' Key:' . $key . '->' . array2string ( $attach ));
2015-06-16 08:37:16 +02:00
// pass meeting requests to calendar plugin
if ( strtolower ( $attach [ 'mimeType' ]) == 'text/calendar' && strtolower ( $attach [ 'method' ]) == 'request' &&
isset ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'apps' ][ 'calendar' ]) &&
( $attachment = $this -> mail -> getAttachment ( $id , $attach [ 'partID' ], 0 , false , false , $_folderName )) &&
2015-09-04 17:27:41 +02:00
( $output -> meetingrequest = calendar_zpush :: meetingRequest ( $attachment [ 'attachment' ])))
2015-06-16 08:37:16 +02:00
{
2015-12-15 14:39:10 +01:00
//overwrite the globalobjId from calendar object, as: if you delete the mail, that is
//the meeting-request its using the globalobjid as reference and deletes both:
//mail AND meeting. we dont want this. accepting meeting requests with the mobile does nothing
$output -> meetingrequest -> globalobjid = activesync_backend :: uid2globalObjId ( $id );
2015-06-16 08:37:16 +02:00
$output -> messageclass = " IPM.Schedule.Meeting.Request " ;
2016-02-26 14:04:35 +01:00
//$output->messageclass = "IPM.Schedule.Meeting";
2015-10-13 17:06:04 +02:00
unset ( $attachment );
2015-06-16 08:37:16 +02:00
continue ; // do NOT add attachment as attachment
}
2015-06-16 13:57:25 +02:00
if ( Request :: GetProtocolVersion () >= 12.0 ) {
2015-06-16 08:37:16 +02:00
$attachment = new SyncBaseAttachment ();
2015-06-16 13:57:25 +02:00
if ( ! isset ( $output -> asattachments ) || ! is_array ( $output -> asattachments ))
$output -> asattachments = array ();
$attachment -> estimatedDataSize = $attach [ 'size' ];
$attachment -> method = 1 ;
$attachment -> filereference = $folderid . " : " . $id . " : " . $attach [ 'partID' ];
} else {
$attachment = new SyncAttachment ();
if ( ! isset ( $output -> attachments ) || ! is_array ( $output -> attachments ))
$output -> attachments = array ();
$attachment -> attsize = $attach [ 'size' ];
$attachment -> attmethod = 1 ;
$attachment -> attname = $folderid . " : " . $id . " : " . $attach [ 'partID' ]; //$key;
2015-06-16 08:37:16 +02:00
}
2015-06-23 17:36:12 +02:00
2015-06-16 08:37:16 +02:00
$attachment -> displayname = $attach [ 'name' ];
//error_log(__METHOD__.__LINE__.'->'.$folderid . ":" . $id . ":" . $attach['partID']);
2015-06-23 17:36:12 +02:00
2015-06-16 08:37:16 +02:00
$attachment -> attoid = " " ; //isset($part->headers['content-id']) ? trim($part->headers['content-id']) : "";
2015-10-13 17:06:04 +02:00
//$attachment->isinline=0; // if not inline, do not use isinline
2015-06-16 08:37:16 +02:00
if ( ! empty ( $attach [ 'cid' ]) && $attach [ 'cid' ] <> 'NIL' )
{
2015-09-07 14:59:19 +02:00
if ( $bpReturnType != 4 && $attach [ 'disposition' ] == 'inline' )
{
$attachment -> isinline = true ;
}
2015-06-16 13:57:25 +02:00
if ( Request :: GetProtocolVersion () >= 12.0 ) {
2015-06-23 17:36:12 +02:00
$attachment -> method = 1 ;
2015-06-16 13:57:25 +02:00
$attachment -> contentid = str_replace ( array ( " < " , " > " ), " " , $attach [ 'cid' ]);
} else {
$attachment -> attmethod = 6 ;
$attachment -> attoid = str_replace ( array ( " < " , " > " ), " " , $attach [ 'cid' ]);
}
2016-06-07 13:00:47 +02:00
// ZLog::Write(LOGLEVEL_DEBUG, "'".$part->headers['content-id']."' ".$attachment->contentid);
2015-06-16 08:37:16 +02:00
$attachment -> contenttype = trim ( $attach [ 'mimeType' ]);
2016-06-07 13:00:47 +02:00
// ZLog::Write(LOGLEVEL_DEBUG, "'".$part->headers['content-type']."' ".$attachment->contentid);
2015-06-16 08:37:16 +02:00
}
2015-06-16 13:57:25 +02:00
if ( Request :: GetProtocolVersion () >= 12.0 ) {
2015-06-16 08:37:16 +02:00
array_push ( $output -> asattachments , $attachment );
2015-06-16 13:57:25 +02:00
} else {
array_push ( $output -> attachments , $attachment );
2015-06-16 08:37:16 +02:00
}
2015-10-13 17:06:04 +02:00
unset ( $attachment );
2015-06-16 08:37:16 +02:00
}
}
//$this->debugLevel=0;
// end handle Attachments
2015-10-13 17:06:04 +02:00
unset ( $attachments );
// Language Code Page ID: http://msdn.microsoft.com/en-us/library/windows/desktop/dd317756%28v=vs.85%29.aspx
$output -> internetcpid = INTERNET_CPID_UTF8 ;
if ( $this -> debugLevel > 3 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . array2string ( $output ));
2015-07-02 14:09:19 +02:00
//$this->debugLevel=0;
2015-06-16 08:37:16 +02:00
return $output ;
}
2015-07-02 14:09:19 +02:00
//$this->debugLevel=0;
2015-06-16 08:37:16 +02:00
return false ;
}
/**
* Process response to meeting request
*
* mail plugin only extracts the iCal attachment and let ' s calendar plugin deal with adding it
*
* @ see BackendDiff :: MeetingResponse ()
* @ param string $folderid folder of meeting request mail
* @ param int | string $requestid uid of mail with meeting request
* @ param int $response 1 = accepted , 2 = tentative , 3 = decline
* @ return int | boolean id of calendar item , false on error
*/
function MeetingResponse ( $folderid , $requestid , $response )
{
2015-09-04 17:27:41 +02:00
if ( ! class_exists ( 'calendar_zpush' ))
2015-06-16 08:37:16 +02:00
{
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " (...) no EGroupware calendar installed! " );
2015-06-16 08:37:16 +02:00
return null ;
}
if ( ! ( $stat = $this -> StatMessage ( $folderid , $requestid )))
{
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( $requestid , ' $folderid ', $response ) returning FALSE (can NOT stat message) " );
2015-06-16 08:37:16 +02:00
return false ;
}
$ret = false ;
foreach ( $this -> mail -> getMessageAttachments ( $requestid , $_partID = '' , $_structure = null , $fetchEmbeddedImages = true , $fetchTextCalendar = true ) as $key => $attach )
{
if ( strtolower ( $attach [ 'mimeType' ]) == 'text/calendar' && strtolower ( $attach [ 'method' ]) == 'request' &&
( $attachment = $this -> mail -> getAttachment ( $requestid , $attach [ 'partID' ], 0 , false )))
{
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( $requestid , ' $folderid ', $response ) iCal found, calling now backend->MeetingResponse(' $attachment[attachment] ') " );
2015-06-16 08:37:16 +02:00
// calling backend again with iCal attachment, to let calendar add the event
2015-10-14 18:42:04 +02:00
$ret = $this -> backend -> MeetingResponse ( $attachment [ 'attachment' ],
2015-06-16 08:37:16 +02:00
$this -> backend -> createID ( 'calendar' , $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ]),
2015-10-14 18:42:04 +02:00
$response );
2019-06-15 08:45:25 +02:00
// delete message after meeting-response is processed successful by calendar
if ( $ret ) $this -> DeleteMessage ( $folderid , $requestid , null );
2015-06-16 08:37:16 +02:00
break ;
}
}
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( $requestid , ' $folderid ', $response ) returning " . array2string ( $ret ));
2015-06-16 08:37:16 +02:00
return $ret ;
}
/**
* GetAttachmentData
* Should return attachment data for the specified attachment . The passed attachment identifier is
* the exact string that is returned in the 'AttName' property of an SyncAttachment . So , you should
* encode any information you need to find the attachment in that 'attname' property .
*
* @ param string $fid - id
* @ param string $attname - should contain ( folder ) id
2016-06-14 16:41:50 +02:00
* @ return SyncItemOperationsAttachment - object
2015-06-16 08:37:16 +02:00
*/
function GetAttachmentData ( $fid , $attname ) {
2016-06-14 16:41:50 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " : $fid (attname: ' $attname ') " );
return $this -> _GetAttachmentData ( $fid , $attname );
2015-06-16 08:37:16 +02:00
}
/**
* ItemOperationsGetAttachmentData
* Should return attachment data for the specified attachment . The passed attachment identifier is
* the exact string that is returned in the 'AttName' property of an SyncAttachment . So , you should
* encode any information you need to find the attachment in that 'attname' property .
*
* @ param string $fid - id
* @ param string $attname - should contain ( folder ) id
2015-06-16 13:57:25 +02:00
* @ return SyncItemOperationsAttachment - object
2015-06-16 08:37:16 +02:00
*/
function ItemOperationsGetAttachmentData ( $fid , $attname ) {
2016-06-14 16:41:50 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " : $fid (attname: ' $attname ') " );
return $this -> _GetAttachmentData ( $fid , $attname );
}
/**
* _GetAttachmentData implements
* - ItemOperationsGetAttachmentData
* - GetAttachmentData
*
* @ param string $fid - id
* @ param string $attname - should contain ( folder ) id
* @ return SyncItemOperationsAttachment - object
*/
private function _GetAttachmentData ( $fid , $attname )
{
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " : $fid (attname: ' $attname ') " . function_backtrace ());
2015-06-16 13:57:25 +02:00
//error_log(__METHOD__.__LINE__." Fid: $fid (attname: '$attname')");
2015-06-16 08:37:16 +02:00
list ( $folderid , $id , $part ) = explode ( " : " , $attname );
$this -> splitID ( $folderid , $account , $folder );
2016-03-28 20:51:38 +02:00
if ( ! isset ( $this -> mail )) $this -> mail = Mail :: getInstance ( false , self :: $profileID , true , false , true );
2015-06-16 08:37:16 +02:00
$this -> mail -> reopen ( $folder );
2015-09-07 14:59:19 +02:00
$attachment = $this -> mail -> getAttachment ( $id , $part , 0 , false , true , $folder );
2016-06-23 17:51:03 +02:00
$SIOattachment = new SyncItemOperationsAttachment ();
2015-10-13 23:12:02 +02:00
fseek ( $attachment [ 'attachment' ], 0 , SEEK_SET ); // z-push requires stream seeked to start
2016-06-14 16:41:50 +02:00
$SIOattachment -> data = $attachment [ 'attachment' ];
//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": $fid (attname: '$attname') Data:".$attachment['attachment']);
if ( isset ( $attachment [ 'type' ]) )
$SIOattachment -> contenttype = $attachment [ 'type' ];
2015-06-16 13:57:25 +02:00
unset ( $attachment );
2016-06-14 16:41:50 +02:00
return $SIOattachment ;
2015-06-16 08:37:16 +02:00
}
/**
* StatMessage should return message stats , analogous to the folder stats ( StatFolder ) . Entries are :
2016-06-23 23:15:19 +02:00
*
2015-06-16 08:37:16 +02:00
* 'id' => Server unique identifier for the message . Again , try to keep this short ( under 20 chars )
* 'flags' => simply '0' for unread , '1' for read
* 'mod' => modification signature . As soon as this signature changes , the item is assumed to be completely
* changed , and will be sent to the PDA as a whole . Normally you can use something like the modification
* time for this field , which will change as soon as the contents have changed .
*
* @ param string $folderid
2016-06-23 23:15:19 +02:00
* @ param int $id id ( uid ) of message
2015-06-16 08:37:16 +02:00
* @ return array
*/
public function StatMessage ( $folderid , $id )
{
2016-06-23 23:15:19 +02:00
$messages = $this -> fetchMessages ( $folderid , NULL , $id );
//ZLog::Write(LOGLEVEL_DEBUG, __METHOD__."('$folderid','$id') returning ".array2string($messages[$id]));
return $messages [ $id ];
2015-06-16 08:37:16 +02:00
}
/**
2015-06-23 17:36:12 +02:00
* Called when a message has been changed on the mobile .
* Added support for FollowUp flag
2015-06-16 08:37:16 +02:00
*
2015-06-23 17:36:12 +02:00
* @ param string $folderid id of the folder
* @ param string $id id of the message
* @ param SyncXXX $message the SyncObject containing a message
2015-06-16 08:37:16 +02:00
* @ param ContentParameters $contentParameters
2015-06-23 17:36:12 +02:00
*
* @ access public
* @ return array same return value as StatMessage ()
* @ throws StatusException could throw specific SYNC_STATUS_ * exceptions
2015-06-16 08:37:16 +02:00
*/
function ChangeMessage ( $folderid , $id , $message , $contentParameters )
{
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . " $folderid , $id , " . array2string ( $message ) . " , " . array2string ( $contentParameters ));
2015-06-23 17:36:12 +02:00
//unset($folderid, $id, $message, $contentParameters);
$account = $folder = null ;
$this -> splitID ( $folderid , $account , $folder );
if ( isset ( $message -> flag )) {
if ( isset ( $message -> flag -> flagstatus ) && $message -> flag -> flagstatus == 2 ) {
$rv = $this -> mail -> flagMessages ((( $message -> flag -> flagstatus == 2 ) ? " flagged " : " unflagged " ), $id , $folder );
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " -> set " . array2string ( $id ) . ' in Folder ' . $folder . " as " . (( $message -> flag -> flagstatus == 2 ) ? " flagged " : " unflagged " ) . " --> " . $rv );
2015-06-23 17:36:12 +02:00
} else {
$rv = $this -> mail -> flagMessages ( " unflagged " , $id , $folder );
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " -> set " . array2string ( $id ) . ' in Folder ' . $folder . " as " . " unflagged " . " --> " . $rv );
2015-06-23 17:36:12 +02:00
}
2015-07-02 17:42:22 +02:00
}
2015-06-23 17:36:12 +02:00
return $this -> StatMessage ( $folderid , $id );
2015-06-16 08:37:16 +02:00
}
/**
* This function is called when the user moves an item on the PDA . You should do whatever is needed
* to move the message on disk . After this call , StatMessage () and GetMessageList () should show the items
* to have a new parent . This means that it will disappear from GetMessageList () will not return the item
* at all on the source folder , and the destination folder will show the new message
*
* @ param string $folderid id of the source folder
* @ param string $id id of the message
* @ param string $newfolderid id of the destination folder
* @ param ContentParameters $contentParameters
*
* @ return boolean status of the operation
* @ throws StatusException could throw specific SYNC_MOVEITEMSSTATUS_ * exceptions
*/
public function MoveMessage ( $folderid , $id , $newfolderid , $contentParameters )
{
unset ( $contentParameters ); // not used, but required by function signature
2016-06-07 13:00:47 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , " IMAP-MoveMessage: (sfid: ' $folderid ' id: ' $id ' dfid: ' $newfolderid ' ) " );
2015-06-16 08:37:16 +02:00
$account = $srcFolder = $destFolder = null ;
$this -> splitID ( $folderid , $account , $srcFolder );
$this -> splitID ( $newfolderid , $account , $destFolder );
2016-06-07 13:00:47 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , " IMAP-MoveMessage: (SourceFolder: ' $srcFolder ' id: ' $id ' DestFolder: ' $destFolder ' ) " );
2016-03-28 20:51:38 +02:00
if ( ! isset ( $this -> mail )) $this -> mail = Mail :: getInstance ( false , self :: $profileID , true , false , true );
2015-06-16 08:37:16 +02:00
$this -> mail -> reopen ( $destFolder );
$status = $this -> mail -> getFolderStatus ( $destFolder );
$uidNext = $status [ 'uidnext' ];
$this -> mail -> reopen ( $srcFolder );
// move message
$rv = $this -> mail -> moveMessages ( $destFolder ,( array ) $id , true , $srcFolder , true );
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . " : New Status of $destFolder : " . array2string ( $status ) . " , ReturnValOf moveMessage " . array2string ( $rv )); // this may be true, so try using the nextUID value by examine
2015-07-02 15:29:11 +02:00
// return the new id "as string"
2015-06-16 08:37:16 +02:00
return ( $rv === true ? $uidNext : $rv [ $id ]) . " " ;
}
/**
2016-06-23 23:15:19 +02:00
* Get all messages of a folder with optional cutoffdate
2015-06-16 08:37:16 +02:00
*
2016-06-23 23:15:19 +02:00
* @ param int $cutoffdate = null timestamp with cutoffdate , default 12 weeks
2015-06-16 08:37:16 +02:00
*/
public function GetMessageList ( $folderid , $cutoffdate = NULL )
{
2016-06-23 23:15:19 +02:00
if ( $cutoffdate > 0 )
{
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . ' for Folder:' . $folderid . ' SINCE:' . $cutoffdate . '/' . date ( " d-M-Y " , $cutoffdate ));
}
else
2015-06-16 08:37:16 +02:00
{
2016-07-28 15:47:54 +02:00
$maximumSyncRangeInDays = self :: PAST_LIMIT ; // corresponds to our default value
if ( isset ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'activesync' ][ 'mail-maximumSyncRange' ]))
{
$maximumSyncRangeInDays = $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'activesync' ][ 'mail-maximumSyncRange' ];
}
$cutoffdate = ( is_numeric ( $maximumSyncRangeInDays ) ? Api\DateTime :: to ( 'now' , 'ts' ) - ( 3600 * 24 * $maximumSyncRangeInDays ) : null );
if ( is_numeric ( $maximumSyncRangeInDays )) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . ' Client set no truncationdate. Using ' . $maximumSyncRangeInDays . ' days.' . date ( " d-M-Y " , $cutoffdate ));
2015-06-16 08:37:16 +02:00
}
2016-10-24 18:46:45 +02:00
try {
return $this -> fetchMessages ( $folderid , $cutoffdate );
} catch ( Exception $e )
{
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' failed for ' . $e -> getMessage () . ( $e -> details ? $e -> details : '' ));
return array ();
}
2015-06-16 08:37:16 +02:00
}
2016-06-23 23:15:19 +02:00
/**
* Fetch headers for one or all mail of a folder using optional cutoffdate
*
* Headers of last fetchMessage call of complate folder are cached in static $headers ,
* to allow to use them without fetching them again .
* Next call clears cache
*
* @ param int $folderid
* @ param int $cutoffdate timestamp with cutoffdate
* @ param string $_id = null uid of single message to fetch
2016-07-08 10:33:58 +02:00
* @ param boolean $return_all_headers = false true : additinal contain all headers eg . " subject "
2016-06-23 23:15:19 +02:00
* @ return array uid => array StatMessage ( $folderid , $_id )
*/
2016-07-08 10:33:58 +02:00
private function fetchMessages ( $folderid , $cutoffdate = NULL , $_id = NULL , $return_all_headers = false )
2015-06-16 08:37:16 +02:00
{
2016-06-23 23:15:19 +02:00
static $headers = array ();
2015-06-16 08:37:16 +02:00
if ( $this -> debugLevel > 1 ) $gstarttime = microtime ( true );
2015-10-13 17:06:04 +02:00
//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__);
2015-06-16 08:37:16 +02:00
$rv_messages = array ();
// if the message is still available within the class, we use it instead of fetching it again
2016-06-23 23:15:19 +02:00
if ( $_id && isset ( $headers [ $_id ]) && is_array ( $headers [ $_id ]))
2015-06-16 08:37:16 +02:00
{
2015-10-13 17:06:04 +02:00
//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__." the message ".$_id[0]." is still available within the class, we use it instead of fetching it again");
2016-06-23 23:15:19 +02:00
$rv_messages = array ( 'header' => array ( $headers [ $_id ]));
2015-06-16 08:37:16 +02:00
}
2016-06-23 23:15:19 +02:00
else
2015-06-16 08:37:16 +02:00
{
2016-06-23 23:15:19 +02:00
$headers = array (); // clear cache to not use too much memory
2015-06-16 08:37:16 +02:00
if ( $this -> debugLevel > 1 ) $starttime = microtime ( true );
$this -> _connect ( $this -> account );
if ( $this -> debugLevel > 1 )
{
$endtime = microtime ( true ) - $starttime ;
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " connect took : " . $endtime . ' for account:' . $this -> account );
2015-06-16 08:37:16 +02:00
}
2015-10-13 17:06:04 +02:00
$messagelist = $_filter = array ();
2015-06-16 08:37:16 +02:00
// if not connected, any further action must fail
2016-02-26 14:04:35 +01:00
if ( ! empty ( $cutoffdate )) $_filter = array ( 'status' => array ( 'UNDELETED' ), 'range' => " SINCE " , 'date' => date ( " d-M-Y " , $cutoffdate ));
2015-06-16 08:37:16 +02:00
if ( $this -> debugLevel > 1 ) $starttime = microtime ( true );
$account = $_folderName = $id = null ;
$this -> splitID ( $folderid , $account , $_folderName , $id );
if ( $this -> debugLevel > 1 )
{
$endtime = microtime ( true ) - $starttime ;
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " splitID took : " . $endtime . ' for FolderID:' . $folderid );
2015-06-16 08:37:16 +02:00
}
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 1 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . ' for Folder:' . $_folderName . ' Filter:' . array2string ( $_filter ) . ' Ids:' . array2string ( $_id ) . '/' . $id );
2015-06-16 08:37:16 +02:00
if ( $this -> debugLevel > 1 ) $starttime = microtime ( true );
$_numberOfMessages = ( empty ( $cutoffdate ) ? 250 : 99999 );
$rv_messages = $this -> mail -> getHeaders ( $_folderName , $_startMessage = 1 , $_numberOfMessages , $_sort = 0 , $_reverse = false , $_filter , $_id );
if ( $this -> debugLevel > 1 )
{
$endtime = microtime ( true ) - $starttime ;
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " getHeaders call took : " . $endtime . ' for FolderID:' . $_folderName );
2015-06-16 08:37:16 +02:00
}
}
2015-10-13 17:06:04 +02:00
if ( $_id == NULL && $this -> debugLevel > 1 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " found : " . count ( $rv_messages [ 'header' ]));
//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' Result:'.array2string($rv_messages));
$messagelist = array ();
if ( ! isset ( $rv_messages [ 'header' ]) || empty ( $rv_messages [ 'header' ])) return $messagelist ;
//if ($_returnModHash) $messageFolderHash = array();
2015-06-16 08:37:16 +02:00
foreach (( array ) $rv_messages [ 'header' ] as $k => $vars )
{
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 3 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' ID to process:' . $vars [ 'uid' ] . ' Subject:' . $vars [ 'subject' ]);
2016-06-23 23:15:19 +02:00
$headers [ $vars [ 'uid' ]] = $vars ;
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 3 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' MailID:' . $k . '->' . array2string ( $vars ));
2015-06-16 08:37:16 +02:00
if ( ! empty ( $vars [ 'deleted' ])) continue ; // cut of deleted messages
if ( $cutoffdate && $vars [ 'date' ] < $cutoffdate ) continue ; // message is out of range for cutoffdate, ignore it
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' ID to report:' . $vars [ 'uid' ] . ' Subject:' . $vars [ 'subject' ]);
2016-07-08 10:33:58 +02:00
$mess = $return_all_headers ? $vars : array ();
2015-10-13 17:06:04 +02:00
$mess [ " mod " ] = self :: doFlagsMod ( $vars ) . $vars [ 'date' ];
2015-06-16 08:37:16 +02:00
$mess [ " id " ] = $vars [ 'uid' ];
// 'seen' aka 'read' is the only flag we want to know about
$mess [ " flags " ] = 0 ;
// outlook supports additional flags, set them to 0
2023-06-09 18:27:28 +02:00
if ( ! empty ( $vars [ " seen " ])) $mess [ " flags " ] = 1 ;
2015-10-13 23:12:02 +02:00
if ( $this -> debugLevel > 3 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . array2string ( $mess ));
2015-06-16 08:37:16 +02:00
$messagelist [ $vars [ 'uid' ]] = $mess ;
unset ( $mess );
}
if ( $this -> debugLevel > 1 )
{
$endtime = microtime ( true ) - $gstarttime ;
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " total time used : " . $endtime . ' for Folder:' . $_folderName . ' Filter:' . array2string ( $_filter ) . ' Ids:' . array2string ( $_id ) . '/' . $id );
2015-06-16 08:37:16 +02:00
}
return $messagelist ;
}
2015-10-13 17:06:04 +02:00
/**
2015-10-13 23:12:02 +02:00
* Prepare headeinfo on a message to return some standardized string to tell which flags are set for a message
*
* AS currently only supports flagged , answered / replied and forwarded flags .
* Seen / read is in under flags key of stat !
*
2015-10-13 17:06:04 +02:00
* @ param array $headerFlags - array to process , a full return array from getHeaders
2015-10-13 23:12:02 +02:00
* @ link https :// sourceforge . net / p / zimbrabackend / code / HEAD / tree / zimbra - backend / branches / z - push - 2 / zimbra . php #l11652
* @ return string string of a representation of supported flags
2015-10-13 17:06:04 +02:00
*/
static function doFlagsMod ( $headerFlags )
{
2015-10-13 23:12:02 +02:00
$flags = 'nnn' ;
2023-06-09 18:27:28 +02:00
if ( ! empty ( $headerFlags [ 'flagged' ])) $flags [ 0 ] = 'f' ;
if ( ! empty ( $headerFlags [ 'answered' ])) $flags [ 1 ] = 'a' ;
if ( ! empty ( $headerFlags [ 'forwarded' ])) $flags [ 2 ] = 'f' ;
2015-10-13 23:12:02 +02:00
//ZLog::Write(LOGLEVEL_DEBUG, __METHOD__.'('.array2string($headerFlags).') returning '.array2string($flags));
return $flags ;
2015-10-13 17:06:04 +02:00
}
2015-06-16 08:37:16 +02:00
/**
* Search mailbox for a given pattern
*
2015-07-17 10:41:26 +02:00
* @ param object $_searchquery holds information specifying the query with GetDataArray it holds
* [ searchname ] => MAILBOX
* [ searchfolderid ] => 101000000000
* [ searchfreetext ] => somesearchtexgt
* [ searchdatereceivedgreater ] => 1
* [ searchvaluegreater ] => 2015 - 07 - 06 T22 : 00 : 00.000 Z
* [ searchdatereceivedless ] => 1
* [ searchvalueless ] => 2015 - 07 - 14 T15 : 11 : 00.000 Z
* [ searchrebuildresults ] => 1
* [ searchrange ] => 0 - 99
* [ bodypref ] => Array ([ 1 ] => BodyPreference Object ([ unsetdata : protected ] => Array ([ truncationsize ] => [ allornone ] => [ preview ] => )[ SO_internalid : StateObject : private ] => [ data : protected ] =>
* Array ([ truncationsize ] => 2147483647 )[ changed : protected ] => 1 ))
2015-09-04 17:27:41 +02:00
* [ mimesupport ] => 2 )
2015-07-17 10:41:26 +02:00
* @ return array ([ " range " ] = $_searchquery -> GetSearchRange (), [ 'searchtotal' ] = count of results ,
* array ( " class " => " Email " ,
* " longid " => folderid . ':' . uid ' ,
* " folderid " => folderid ,
* ), ....
* )
2015-06-16 08:37:16 +02:00
*/
2015-07-17 10:41:26 +02:00
public function getSearchResultsMailbox ( $_searchquery )
2015-06-16 08:37:16 +02:00
{
2015-07-17 10:41:26 +02:00
//$this->debugLevel=1;
$searchquery = $_searchquery -> GetDataArray ();
2015-06-16 08:37:16 +02:00
if ( ! is_array ( $searchquery )) return array ();
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . array2string ( $searchquery ));
2015-07-17 10:41:26 +02:00
if ( isset ( $searchquery [ 'searchrebuildresults' ])) {
$rebuildresults = $searchquery [ 'searchrebuildresults' ];
2015-06-16 08:37:16 +02:00
} else {
$rebuildresults = false ;
}
2016-06-07 13:00:47 +02:00
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , 'RebuildResults [' . $rebuildresults . ']' );
2015-06-16 08:37:16 +02:00
if ( isset ( $searchquery [ 'deeptraversal' ])) {
$deeptraversal = $searchquery [ 'deeptraversal' ];
} else {
$deeptraversal = false ;
}
2016-06-07 13:00:47 +02:00
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , 'DeepTraversal [' . $deeptraversal . ']' );
2015-06-16 08:37:16 +02:00
2015-07-17 10:41:26 +02:00
if ( isset ( $searchquery [ 'searchrange' ])) {
$range = explode ( " - " , $_searchquery -> GetSearchRange ());
$start = $range [ 0 ] + 1 ;
2015-06-16 08:37:16 +02:00
$limit = $range [ 1 ] - $range [ 0 ] + 1 ;
} else {
$range = false ;
}
2016-06-07 13:00:47 +02:00
if ( $this -> debugLevel > 0 ) ZLog :: Write ( LOGLEVEL_DEBUG , 'Range [' . print_r ( $range , true ) . ']' );
2015-06-16 08:37:16 +02:00
//foreach($searchquery['query'] as $k => $value) {
// $query = $value;
//}
2015-07-17 10:41:26 +02:00
if ( isset ( $searchquery [ 'searchfolderid' ]))
2015-06-16 08:37:16 +02:00
{
2015-07-17 10:41:26 +02:00
$folderid = $searchquery [ 'searchfolderid' ];
2015-06-16 08:37:16 +02:00
}
2015-08-11 10:46:19 +02:00
/*
2015-06-16 08:37:16 +02:00
// other types may be possible - we support quicksearch first (freeText in subject and from (or TO in Sent Folder))
2016-03-28 20:51:38 +02:00
if ( is_null ( Mail :: $supportsORinQuery ) || ! isset ( Mail :: $supportsORinQuery [ self :: $profileID ]))
2015-06-16 08:37:16 +02:00
{
2016-05-03 21:17:44 +02:00
Mail :: $supportsORinQuery = Api\Cache :: getCache ( Api\Cache :: INSTANCE , 'email' , 'supportsORinQuery' . trim ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ]), $callback = null , $callback_params = array (), $expiration = 60 * 60 * 10 );
2016-03-28 20:51:38 +02:00
if ( ! isset ( Mail :: $supportsORinQuery [ self :: $profileID ])) Mail :: $supportsORinQuery [ self :: $profileID ] = true ;
2015-06-16 08:37:16 +02:00
}
2015-08-11 10:46:19 +02:00
*/
2015-07-17 10:41:26 +02:00
if ( isset ( $searchquery [ 'searchfreetext' ]))
2015-06-16 08:37:16 +02:00
{
2015-07-17 10:41:26 +02:00
$searchText = $searchquery [ 'searchfreetext' ];
2015-06-16 08:37:16 +02:00
}
if ( ! $folderid )
{
$_folderName = ( $this -> mail -> sessionData [ 'mailbox' ] ? $this -> mail -> sessionData [ 'mailbox' ] : 'INBOX' );
$folderid = $this -> createID ( $account = 0 , $_folderName );
}
$rv = $this -> splitID ( $folderid , $account , $_folderName , $id );
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' ProfileID:' . self :: $profileID . ' FolderID:' . $folderid . ' Foldername:' . $_folderName );
2015-06-16 08:37:16 +02:00
$this -> _connect ( $account );
2015-08-11 10:46:19 +02:00
// this should not be needed ???
2016-03-28 20:51:38 +02:00
Mail :: $supportsORinQuery [ self :: $profileID ] = true ; // trigger quicksearch (if possible)
$_filter = array ( 'type' => ( Mail :: $supportsORinQuery [ self :: $profileID ] ? 'quick' : 'subject' ),
2015-06-16 08:37:16 +02:00
'string' => $searchText ,
2016-02-26 15:19:11 +01:00
'status' => 'any'
2015-06-16 08:37:16 +02:00
);
2016-02-26 15:19:11 +01:00
if ( isset ( $searchquery [ 'searchdatereceivedgreater' ]) || isset ( $searchquery [ 'searchdatereceivedless' ]))
{
/*
* We respect only the DATEPART of the RANGE specified
* [ searchdatereceivedgreater ] => 1
* [ searchvaluegreater ] => 2015 - 07 - 06 T22 : 00 : 00.000 Z , SINCE
* [ searchdatereceivedless ] => 1
* [ searchvalueless ] => 2015 - 07 - 14 T15 : 11 : 00.000 Z , BEFORE
*/
$_filter [ 'range' ] = " BETWEEN " ;
list ( $sincedate , $crap ) = explode ( 'T' , $searchquery [ 'searchvaluegreater' ]);
list ( $beforedate , $crap ) = explode ( 'T' , $searchquery [ 'searchvalueless' ]);
2016-05-03 21:17:44 +02:00
$_filter [ 'before' ] = date ( " d-M-Y " , Api\DateTime :: to ( $beforedate , 'ts' ));
$_filter [ 'since' ] = date ( " d-M-Y " , Api\DateTime :: to ( $sincedate , 'ts' ));
2016-02-26 15:19:11 +01:00
}
2015-06-16 08:37:16 +02:00
//$_filter[] = array('type'=>"SINCE",'string'=> date("d-M-Y", $cutoffdate));
2016-06-07 13:00:47 +02:00
if ( $this -> debugLevel > 1 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . ' for Folder:' . $_folderName . ' Filter:' . array2string ( $_filter ));
2015-07-17 10:41:26 +02:00
$rv_messages = $this -> mail -> getHeaders ( $_folderName , $_startMessage = ( $range ? $start : 1 ), $_numberOfMessages = ( $limit ? $limit : 9999999 ), $_sort = 0 , $_reverse = false , $_filter , $_id = NULL );
2015-10-13 17:06:04 +02:00
//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.array2string($rv_messages));
2015-06-16 08:37:16 +02:00
$list = array ();
2015-07-17 10:41:26 +02:00
2021-11-18 09:01:36 +01:00
$cnt = count ( $rv_messages [ 'header' ] ? ? []);
2015-08-11 10:46:19 +02:00
//$list['status'] = 1;
$list [ 'searchtotal' ] = $cnt ;
$list [ " range " ] = $_searchquery -> GetSearchRange ();
2015-06-16 08:37:16 +02:00
foreach (( array ) $rv_messages [ 'header' ] as $i => $vars )
{
$list [] = array (
2015-07-17 10:41:26 +02:00
" class " => " Email " ,
" longid " => $folderid . ':' . $vars [ 'uid' ],
" folderid " => $folderid ,
2015-06-16 08:37:16 +02:00
);
}
//error_log(__METHOD__.__LINE__.array2string($list));
2015-10-13 17:06:04 +02:00
//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.array2string($list));
2015-07-17 10:41:26 +02:00
return $list ;
2015-06-16 08:37:16 +02:00
}
/**
* Get ID of parent Folder or '0' for folders in root
*
* @ param int $account
* @ param string $folder
* @ return string
*/
private function getParentID ( $account , $folder )
{
$this -> _connect ( $account );
if ( ! isset ( $this -> folders )) $this -> folders = $this -> mail -> getFolderObjects ( true , false );
$mailFolder = $this -> folders [ $folder ];
if ( ! isset ( $mailFolder )) return false ;
$delimiter = ( isset ( $mailFolder -> delimiter ) ? $mailFolder -> delimiter : $this -> mail -> getHierarchyDelimiter ());
$parent = explode ( $delimiter , $folder );
array_pop ( $parent );
$parent = implode ( $delimiter , $parent );
2022-04-26 18:46:23 +02:00
$id = $parent && ! empty ( $this -> folders [ $parent ]) ? $this -> createID ( $account , $parent ) : '0' ;
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 1 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " (' $folder ') --> parent= $parent --> $id " );
2015-06-16 08:37:16 +02:00
return $id ;
}
/**
* Get Information about a folder
*
* @ param string $id
* @ return SyncFolder | boolean false on error
*/
public function GetFolder ( $id )
{
static $last_id = null ;
static $folderObj = null ;
if ( isset ( $last_id ) && $last_id === $id ) return $folderObj ;
try {
$account = $folder = null ;
$this -> splitID ( $id , $account , $folder );
}
catch ( Exception $e ) {
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' failed for ' . $e -> getMessage ());
2015-06-16 08:37:16 +02:00
return $folderObj = false ;
}
$this -> _connect ( $account );
if ( ! isset ( $this -> folders )) $this -> folders = $this -> mail -> getFolderObjects ( true , false );
$mailFolder = $this -> folders [ $folder ];
if ( ! isset ( $mailFolder )) return $folderObj = false ;
$folderObj = new SyncFolder ();
$folderObj -> serverid = $id ;
$folderObj -> parentid = $this -> getParentID ( $account , $folder );
$folderObj -> displayname = $mailFolder -> shortDisplayName ;
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 1 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . " ID: $id , Account: $account , Folder: $folder " );
2015-06-16 08:37:16 +02:00
// get folder-type
foreach ( $this -> folders as $inbox => $mailFolder ) break ;
if ( $folder == $inbox )
{
$folderObj -> type = SYNC_FOLDER_TYPE_INBOX ;
}
2016-06-17 11:49:09 +02:00
elseif ( $this -> mail -> isDraftFolder ( $folder , false , true ))
2015-06-16 08:37:16 +02:00
{
2015-10-13 17:06:04 +02:00
//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.' isDraft');
2015-06-16 08:37:16 +02:00
$folderObj -> type = SYNC_FOLDER_TYPE_DRAFTS ;
$folderObj -> parentid = 0 ; // required by devices
}
2016-06-17 11:49:09 +02:00
elseif ( $this -> mail -> isTrashFolder ( $folder , false , true ))
2015-06-16 08:37:16 +02:00
{
$folderObj -> type = SYNC_FOLDER_TYPE_WASTEBASKET ;
$this -> _wasteID = $folder ;
//error_log(__METHOD__.__LINE__.' TrashFolder:'.$this->_wasteID);
$folderObj -> parentid = 0 ; // required by devices
}
2016-06-17 11:49:09 +02:00
elseif ( $this -> mail -> isSentFolder ( $folder , false , true ))
2015-06-16 08:37:16 +02:00
{
$folderObj -> type = SYNC_FOLDER_TYPE_SENTMAIL ;
$folderObj -> parentid = 0 ; // required by devices
$this -> _sentID = $folder ;
//error_log(__METHOD__.__LINE__.' SentFolder:'.$this->_sentID);
}
2016-06-17 11:49:09 +02:00
elseif ( $this -> mail -> isOutbox ( $folder , false , true ))
2015-06-16 08:37:16 +02:00
{
2015-10-13 17:06:04 +02:00
//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.' isOutbox');
2015-06-16 08:37:16 +02:00
$folderObj -> type = SYNC_FOLDER_TYPE_OUTBOX ;
$folderObj -> parentid = 0 ; // required by devices
}
else
{
2015-10-13 17:06:04 +02:00
//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.' isOther Folder'.$folder);
2015-06-16 08:37:16 +02:00
$folderObj -> type = SYNC_FOLDER_TYPE_USER_MAIL ;
}
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 1 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( $id ) --> $folder --> type= $folderObj->type , parentID= $folderObj->parentid , displayname= $folderObj->displayname " );
2015-06-16 08:37:16 +02:00
return $folderObj ;
}
/**
* Return folder stats . This means you must return an associative array with the
* following properties :
*
* " id " => The server ID that will be used to identify the folder . It must be unique , and not too long
* How long exactly is not known , but try keeping it under 20 chars or so . It must be a string .
* " parent " => The server ID of the parent of the folder . Same restrictions as 'id' apply .
* " mod " => This is the modification signature . It is any arbitrary string which is constant as long as
* the folder has not changed . In practice this means that 'mod' can be equal to the folder name
* as this is the only thing that ever changes in folders . ( the type is normally constant )
*
* @ return array with values for keys 'id' , 'mod' and 'parent'
*/
public function StatFolder ( $id )
{
$folder = $this -> GetFolder ( $id );
$stat = array (
'id' => $id ,
'mod' => $folder -> displayname ,
'parent' => $folder -> parentid ,
);
return $stat ;
}
/**
* Return a changes array
*
* if changes occurr default diff engine computes the actual changes
*
* @ param string $folderid
* @ param string & $syncstate on call old syncstate , on return new syncstate
* @ return array | boolean false if $folderid not found , array () if no changes or array ( array ( " type " => " fakeChange " ))
*/
function AlterPingChanges ( $folderid , & $syncstate )
{
$account = $folder = null ;
$this -> splitID ( $folderid , $account , $folder );
if ( is_numeric ( $account )) $type = 'mail' ;
2016-06-23 23:15:19 +02:00
2015-06-16 08:37:16 +02:00
if ( $type != 'mail' ) return false ;
2016-03-28 20:51:38 +02:00
if ( ! isset ( $this -> mail )) $this -> mail = Mail :: getInstance ( false , self :: $profileID , true , false , true );
2016-10-25 13:39:23 +02:00
if ( ! $this -> mail -> folderIsSelectable ( $folder ))
{
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " : could not select folder $folder returning fake state " );
2024-03-05 13:54:29 +01:00
$syncstate = " M: " . '0' . " -R: " . '0' . " -U: " . '0' . " -NUID: " . '0' . " -UIDV: " . '0' . " -MODSEQ: " . '0' ;
2016-10-25 13:39:23 +02:00
return array ();
}
2015-06-16 08:37:16 +02:00
2016-10-25 13:39:23 +02:00
$this -> mail -> reopen ( $folder );
2016-06-23 23:15:19 +02:00
2024-03-05 13:54:29 +01:00
if ( ! ( $status = $this -> mail -> getFolderStatus ( $folder , true , false , true , true )))
2016-06-23 23:15:19 +02:00
{
2016-10-25 13:39:23 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " : could not stat folder $folder " );
return false ;
}
2024-03-05 13:54:29 +01:00
$syncstate = " M: " . $status [ 'messages' ] . " -R: " . $status [ 'recent' ] . " -U: " . $status [ 'unseen' ] .
" -NUID: " . $status [ 'uidnext' ] . " -UIDV: " . $status [ 'uidvalidity' ] . " -MODSEQ: " . ( $status [ 'highestmodseq' ] ? ? '0' );
2016-06-23 23:15:19 +02:00
2019-06-15 08:45:25 +02:00
if ( $this -> debugLevel ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( $folderid , ...) $folder ( $account ) returning " . array2string ( $syncstate ));
2016-06-23 23:15:19 +02:00
return array ();
2015-06-16 08:37:16 +02:00
}
/**
* Should return a wastebasket folder if there is one . This is used when deleting
* items ; if this function returns a valid folder ID , then all deletes are handled
* as moves and are sent to your backend as a move . If it returns FALSE , then deletes
* are always handled as real deletes and will be sent to your importer as a DELETE
*/
function GetWasteBasket ()
{
$this -> _connect ( $this -> account );
2015-07-02 17:42:22 +02:00
$id = $this -> createID ( $account = 0 , $this -> _wasteID );
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . " () account= $this->account returned $id for folder $this->_wasteID " );
2015-07-02 17:42:22 +02:00
return $id ;
2015-06-16 08:37:16 +02:00
}
/**
* Called when the user has requested to delete ( really delete ) a message . Usually
* this means just unlinking the file its in or somesuch . After this call has succeeded , a call to
* GetMessageList () should no longer list the message . If it does , the message will be re - sent to the mobile
* as it will be seen as a 'new' item . This means that if this method is not implemented , it ' s possible to
* delete messages on the PDA , but as soon as a sync is done , the item will be resynched to the mobile
*
* @ param string $folderid id of the folder
* @ param string $id id of the message
* @ param ContentParameters $contentParameters
*
* @ access public
* @ return boolean status of the operation
* @ throws StatusException could throw specific SYNC_STATUS_ * exceptions
*/
public function DeleteMessage ( $folderid , $id , $contentParameters )
{
unset ( $contentParameters ); // not used, but required by function signature
2016-06-07 13:00:47 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , " IMAP-DeleteMessage: (fid: ' $folderid ' id: ' $id ' ) " );
2015-06-16 08:37:16 +02:00
/*
$this -> imap_reopenFolder ( $folderid );
$s1 = @ imap_delete ( $this -> _mbox , $id , FT_UID );
$s11 = @ imap_setflag_full ( $this -> _mbox , $id , " \\ Deleted " , FT_UID );
$s2 = @ imap_expunge ( $this -> _mbox );
*/
// we may have to split folderid
$account = $folder = null ;
$this -> splitID ( $folderid , $account , $folder );
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . ' ' . $folderid . '->' . $folder );
2015-06-16 08:37:16 +02:00
$_messageUID = ( array ) $id ;
$this -> _connect ( $this -> account );
$this -> mail -> reopen ( $folder );
try
{
$rv = $this -> mail -> deleteMessages ( $_messageUID , $folder );
}
2016-05-03 21:17:44 +02:00
catch ( Api\Exception $e )
2015-06-16 08:37:16 +02:00
{
$error = $e -> getMessage ();
2015-10-13 17:06:04 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . __LINE__ . " $_messageUID , $folder -> " . $error );
2015-06-16 08:37:16 +02:00
// if the server thinks the message does not exist report deletion as success
if ( stripos ( $error , '[NONEXISTENT]' ) !== false ) return true ;
return false ;
}
// this may be a bit rude, it may be sufficient that GetMessageList does not list messages flagged as deleted
if ( $this -> mail -> mailPreferences [ 'deleteOptions' ] == 'mark_as_deleted' )
{
// ignore mark as deleted -> Expunge!
//$this->mail->icServer->expunge(); // do not expunge as GetMessageList does not List messages flagged as deleted
}
2016-06-07 13:00:47 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , " IMAP-DeleteMessage: $rv " );
2015-06-16 08:37:16 +02:00
return $rv ;
}
/**
* Changes the 'read' flag of a message on disk . The $flags
* parameter can only be '1' ( read ) or '0' ( unread ) . After a call to
* SetReadFlag (), GetMessageList () should return the message with the
* new 'flags' but should not modify the 'mod' parameter . If you do
* change 'mod' , simply setting the message to 'read' on the mobile will trigger
* a full resync of the item from the server .
*
* @ param string $folderid id of the folder
* @ param string $id id of the message
* @ param int $flags read flag of the message
* @ param ContentParameters $contentParameters
*
* @ access public
* @ return boolean status of the operation
* @ throws StatusException could throw specific SYNC_STATUS_ * exceptions
*/
public function SetReadFlag ( $folderid , $id , $flags , $contentParameters )
{
unset ( $contentParameters ); // not used, but required by function signature
2016-06-07 13:00:47 +02:00
// ZLog::Write(LOGLEVEL_DEBUG, "IMAP-SetReadFlag: (fid: '$folderid' id: '$id' flags: '$flags' )");
2015-06-16 08:37:16 +02:00
$account = $folder = null ;
$this -> splitID ( $folderid , $account , $folder );
$_messageUID = ( array ) $id ;
$this -> _connect ( $this -> account );
$rv = $this -> mail -> flagMessages ((( $flags ) ? " read " : " unread " ), $_messageUID , $folder );
2016-06-07 13:00:47 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , " IMAP-SetReadFlag -> set " . array2string ( $_messageUID ) . ' in Folder ' . $folder . " as " . (( $flags ) ? " read " : " unread " ) . " --> " . $rv );
2015-06-16 08:37:16 +02:00
return $rv ;
}
/**
* Creates or modifies a folder
*
* @ param string $id of the parent folder
* @ param string $oldid => if empty -> new folder created , else folder is to be renamed
* @ param string $displayname => new folder name ( to be created , or to be renamed to )
* @ param string $type folder type , ignored in IMAP
*
2016-08-11 15:33:02 +02:00
* @ throws StatusException could throw specific SYNC_FSSTATUS_ * exceptions
2015-06-16 08:37:16 +02:00
* @ return array | boolean stat array or false on error
*/
public function ChangeFolder ( $id , $oldid , $displayname , $type )
{
2016-08-11 15:33:02 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " (' $id ', ' $oldid ', ' $displayname ', $type ) " );
2016-08-11 16:20:30 +02:00
$account = $parent_id = null ;
2016-08-12 13:02:29 +02:00
$this -> splitID ( $id , $account , $parentFolder , $app );
2016-08-11 16:20:30 +02:00
2016-08-12 13:02:29 +02:00
$parent_id = $this -> folder2hash ( $account , $parentFolder );
2016-08-11 16:20:30 +02:00
$old_hash = $oldFolder = null ;
2016-08-11 15:33:02 +02:00
if ( empty ( $oldid ))
{
$action = 'create' ;
2016-08-11 16:20:30 +02:00
}
else
{
2016-08-11 15:33:02 +02:00
$action = 'rename' ;
2016-08-12 13:02:29 +02:00
$this -> splitID ( $oldid , $account , $oldFolder , $app );
$old_hash = $this -> folder2hash ( $account , $oldFolder );
2016-08-11 15:33:02 +02:00
}
2016-08-12 13:02:29 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " : { $action } Folder(' $id '=>( $parentFolder ( $parent_id )), ' $oldid ' " . ( $oldid ? " =>( $oldFolder ( $old_hash )) " : '' ) . " , ' $displayname ', $type ) " );
2016-08-11 15:33:02 +02:00
$this -> _connect ( $this -> account );
try
{
if ( $action == 'rename' )
{
$newFolderName = $this -> mail -> renameFolder ( $oldFolder , $parentFolder , $displayname );
2016-08-11 16:20:30 +02:00
}
elseif ( $action == 'create' )
2016-08-11 15:33:02 +02:00
{
$error = null ;
$newFolderName = $this -> mail -> createFolder ( $parentFolder , $displayname , $error );
}
}
catch ( \Exception $e )
{
2016-08-11 16:20:30 +02:00
//throw new Exception(__METHOD__." $action failed for $oldFolder ($action: $displayname) with error:".$e->getMessage());
return false ;
2016-08-11 15:33:02 +02:00
}
2016-08-11 16:20:30 +02:00
$newHash = $this -> rename_folder_hash ( $account , $old_hash , $newFolderName );
$newID = $this -> createID ( $account , $newHash );
2016-11-30 16:11:42 +01:00
$this -> folders = $this -> mail -> getFolderObjects ( true , false , $_alwaysGetDefaultFolders = true , false );
2016-08-11 15:33:02 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , " : { $action } Folder(' $id '=>( $parentFolder ), ' $oldid ' " . ( $oldid ? " =>( $oldFolder ) " : '' ) . " , ' $displayname ' => $newFolderName (ID: $newID )) " );
2016-08-11 16:20:30 +02:00
return $this -> StatFolder ( $newID );
2015-06-16 08:37:16 +02:00
}
/**
* Deletes ( really delete ) a Folder
*
* @ param string $id of the folder to delete
2016-08-12 15:04:52 +02:00
* @ param string $parentid ( = false ) of the folder to delete , may be false / not set
2015-06-16 08:37:16 +02:00
*
2016-08-11 15:33:02 +02:00
* @ throws StatusException could throw specific SYNC_FSSTATUS_ * exceptions
2016-08-12 15:04:52 +02:00
* @ return boolean true or false on error
2015-06-16 08:37:16 +02:00
*/
2016-08-12 15:04:52 +02:00
public function DeleteFolder ( $id , $parentid = false )
2015-06-16 08:37:16 +02:00
{
2016-08-12 15:04:52 +02:00
$account = $parent_id = $app = null ;
$this -> splitID ( $id , $account , $folder , $app );
$old_hash = $this -> folder2hash ( $account , $folder );
if ( $parentid ) $this -> splitID ( $parentid , $account , $parentfolder , $app );
ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( ' $id (-> $folder )',' $parentid " . ( $parentid ? '(->' . $parentfolder . ')' : '' ) . " ') called! " );
$ret = $this -> mail -> deleteFolder ( $folder );
if ( $ret ) $newHash = $this -> rename_folder_hash ( $account , $old_hash , " ##Dele#edFolder# $folder ## " );
2016-11-30 16:11:42 +01:00
$this -> folders = $this -> mail -> getFolderObjects ( true , false , $_alwaysGetDefaultFolders = true , false );
2016-08-12 15:04:52 +02:00
return $ret ;
2015-06-16 08:37:16 +02:00
}
/**
* modify olflags ( outlook style ) flag of a message
*
* @ param $folderid
* @ param $id
* @ param $flags
*
*
* @ DESC The $flags parameter must contains the poommailflag Object
*/
function ChangeMessageFlag ( $folderid , $id , $flags )
{
$_messageUID = ( array ) $id ;
$this -> _connect ( $this -> account );
$account = $folder = null ;
$this -> splitID ( $folderid , $account , $folder );
$rv = $this -> mail -> flagMessages ((( $flags -> flagstatus == 2 ) ? " flagged " : " unflagged " ), $_messageUID , $folder );
2016-06-07 13:00:47 +02:00
ZLog :: Write ( LOGLEVEL_DEBUG , " IMAP-SetFlaggedFlag -> set " . array2string ( $_messageUID ) . ' in Folder ' . $folder . " as " . (( $flags -> flagstatus == 2 ) ? " flagged " : " unflagged " ) . " --> " . $rv );
2015-06-16 08:37:16 +02:00
return $rv ;
}
/**
* Create a max . 32 hex letter ID , current 20 chars are used
*
* @ param int $account mail account id
* @ param string $folder
* @ param int $id = 0
* @ return string
2016-05-03 21:17:44 +02:00
* @ throws Api\Exception\WrongParameter
2015-06-16 08:37:16 +02:00
*/
private function createID ( $account , $folder , $id = 0 )
{
if ( ! is_numeric ( $folder ))
{
// convert string $folder in numeric id
$folder = $this -> folder2hash ( $account , $f = $folder );
}
$str = $this -> backend -> createID ( $account , $folder , $id );
2015-10-13 17:06:04 +02:00
if ( $this -> debugLevel > 1 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " ( $account ,' $f ', $id ) type= $account , folder= $folder --> ' $str ' " );
2015-06-16 08:37:16 +02:00
return $str ;
}
/**
2016-08-12 13:02:29 +02:00
* Split an ID string into $app , $account $folder and $appid
2015-06-16 08:37:16 +02:00
*
* @ param string $str
* @ param int & $account mail account id
* @ param string & $folder
2016-08-12 13:02:29 +02:00
* @ param int & $appid = null ( for mail = mail is to be expected )
2016-05-03 21:17:44 +02:00
* @ throws Api\Exception\WrongParameter
2015-06-16 08:37:16 +02:00
*/
2016-08-12 13:02:29 +02:00
private function splitID ( $str , & $account , & $folder , & $appid = null )
2015-06-16 08:37:16 +02:00
{
2016-08-12 13:02:29 +02:00
$this -> backend -> splitID ( $str , $account , $folder , $appid );
2015-06-16 08:37:16 +02:00
// convert numeric folder-id back to folder name
$folder = $this -> hash2folder ( $account , $f = $folder );
2016-08-12 13:02:29 +02:00
if ( $this -> debugLevel > 1 ) ZLog :: Write ( LOGLEVEL_DEBUG , __METHOD__ . " (' $str ',' $account ',' $folder ', $appid ) " );
2015-06-16 08:37:16 +02:00
}
/**
* Methods to convert ( hierarchical ) folder names to nummerical id ' s
*
* This is currently done by storing a serialized array in the device specific
* state directory .
*/
/**
* Convert folder string to nummeric hash
*
* @ param int $account
* @ param string $folder
* @ return int
*/
private function folder2hash ( $account , $folder )
{
if ( ! isset ( $this -> folderHashes )) $this -> readFolderHashes ();
if (( $index = array_search ( $folder , ( array ) $this -> folderHashes [ $account ])) === false )
{
// new hash
$this -> folderHashes [ $account ][] = $folder ;
$index = array_search ( $folder , ( array ) $this -> folderHashes [ $account ]);
// maybe later storing in on class destruction only
$this -> storeFolderHashes ();
}
return $index ;
}
/**
* Convert numeric hash to folder string
*
* @ param int $account
* @ param int $index
* @ return string NULL if not used so far
*/
private function hash2folder ( $account , $index )
{
if ( ! isset ( $this -> folderHashes )) $this -> readFolderHashes ();
2015-09-15 09:45:58 +02:00
return isset ( $this -> folderHashes [ $account ]) ? $this -> folderHashes [ $account ][ $index ] : null ;
2015-06-16 08:37:16 +02:00
}
2016-08-11 16:20:30 +02:00
/**
* Rename or create a folder in hash table
*
* @ param int $account
* @ param int $index or null to create
* @ param string $new_name
* @ return int $index or new hash if $index is not found
*/
private function rename_folder_hash ( $account , $index , $new_name )
{
if (( string ) $index === '' || ! $this -> hash2folder ( $account , $index ))
{
return $this -> folder2hash ( $account , $new_name );
}
$this -> folderHashes [ $account ][ $index ] = $new_name ;
2016-08-12 13:02:29 +02:00
$this -> storeFolderHashes ();
2016-08-11 16:20:30 +02:00
return $index ;
}
2015-06-16 08:37:16 +02:00
private $folderHashes ;
2016-06-10 08:31:09 +02:00
/**
* Statemaschine instance used to store folders
*
* @ var activesync_statemaschine
*/
private $fh_state_maschine ;
/**
* state_type ( and _key ) used to store folder hashes
*/
const FOLDER_STATE_TYPE = 'folder_hashes' ;
2015-06-16 08:37:16 +02:00
/**
* Read hashfile from state dir
*/
private function readFolderHashes ()
{
2016-06-10 08:31:09 +02:00
if ( ! isset ( $this -> fh_state_maschine ))
2015-06-16 08:37:16 +02:00
{
2016-06-10 08:31:09 +02:00
$this -> fh_state_maschine = new activesync_statemachine ( $this -> backend );
}
try {
$this -> folderHashes = $this -> fh_state_maschine -> getState ( Request :: GetDeviceID (),
self :: FOLDER_STATE_TYPE , self :: FOLDER_STATE_TYPE , 0 );
}
catch ( Exception $e ) {
2016-06-17 09:11:26 +02:00
unset ( $e );
2016-06-10 08:31:09 +02:00
if (( file_exists ( $file = $this -> hashFile ()) || file_exists ( $file = $this -> hashFile ( true ))) &&
( $hashes = file_get_contents ( $file )))
2015-06-16 08:37:16 +02:00
{
2016-06-10 08:31:09 +02:00
$this -> folderHashes = json_decode ( $hashes , true );
// fallback in case hashes have been serialized instead of being json-encoded
if ( json_last_error () != JSON_ERROR_NONE )
{
//error_log(__METHOD__.__LINE__." error decoding with json");
$this -> folderHashes = unserialize ( $hashes );
}
// store folder-hashes to state
2016-06-17 09:11:26 +02:00
$this -> storeFolderHashes ();
2016-06-10 08:31:09 +02:00
}
else
{
$this -> folderHashes = array ();
2015-06-16 08:37:16 +02:00
}
}
}
/**
2016-06-10 08:31:09 +02:00
* Store hashfile via state - maschine
2015-06-16 08:37:16 +02:00
*
* return int | boolean false on error
*/
private function storeFolderHashes ()
{
2016-06-10 08:31:09 +02:00
if ( ! isset ( $this -> fh_state_maschine ))
{
$this -> fh_state_maschine = new activesync_statemachine ( $this -> backend );
}
try {
$this -> fh_state_maschine -> setState ( $this -> folderHashes , Request :: GetDeviceID (),
self :: FOLDER_STATE_TYPE , self :: FOLDER_STATE_TYPE , 0 );
}
catch ( Exception $e ) {
_egw_log_exception ( $e );
return false ;
}
return true ;
2015-06-16 08:37:16 +02:00
}
/**
* Get name of hashfile in state dir
*
2015-09-15 09:45:58 +02:00
* New z - push 2 directory is in activesync_statemachine :: getDeviceDirectory .
* On request this function also returns , but never creates ( ! ), old z - push 1 directory .
*
* @ param boolean $old = false true : return old / pre - 15 hash - file
2016-05-03 21:17:44 +02:00
* @ throws Api\Exception\AssertionFailed
2015-06-16 08:37:16 +02:00
*/
2015-09-15 09:45:58 +02:00
private function hashFile ( $old = false )
2015-06-16 08:37:16 +02:00
{
if ( ! ( $dev_id = Request :: GetDeviceID ()))
{
2016-05-03 21:17:44 +02:00
throw new Api\Exception\AssertionFailed ( __METHOD__ . " () no DeviceID set! " );
2015-09-15 09:45:58 +02:00
}
if ( $old )
{
return STATE_DIR . $dev_id . '/' . $dev_id . '.hashes' ;
2015-06-16 08:37:16 +02:00
}
2016-06-23 23:15:19 +02:00
$dir = activesync_statemachine :: getDeviceDirectory ( $dev_id );
2015-09-15 09:45:58 +02:00
return $dir . '/' . $dev_id . '.hashes' ;
2015-06-16 08:37:16 +02:00
}
2022-04-26 18:46:23 +02:00
}