/backend/kolab/kolab.php
PHP | 3372 lines | 3271 code | 31 blank | 70 comment | 124 complexity | 8375fbd2f23bda297a441f04a2031d0d MD5 | raw file
Possible License(s): AGPL-3.0
Large files files are truncated, but you can click here to view the full file
- <?php
- /*
- Kolab Z-Push Backend
- Copyright (C) 2009-2010 Free Software Foundation Europe e.V.
- The main author of the Kolab Z-Push Backend is Alain Abbas, with
- contributions by .......
- This program is Free Software; you can redistribute it and/or
- modify it under the terms of version two of the GNU General Public
- License as published by the Free Software Foundation.
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
- The licensor of the Kolab Z-Push Backend is the
- Free Software Foundation Europe (FSFE), Fiduciary Program,
- Linienstr. 141, 10115 Berlin, Germany, email:ftf@fsfeurope.org.
- */
- //define('KOLABBACKEND_VERSION', 'SVN developpment 20100307');
- define('KOLABBACKEND_VERSION', '0.6');
- include_once('diffbackend.php');
- // The is an improved version of mimeDecode from PEAR that correctly
- // handles charsets and charset conversion
- include_once('mimeDecode.php');
- include_once('z_RTF.php');
- include_once('z_RFC822.php');
- include_once('Horde/Kolab/Kolab_Zpush/lib/kolabActivesyncData.php');
- require_once 'Horde/Kolab/Format.php';
- class BackendKolab extends BackendDiff {
- private $_server = "";
- private $_username ="";
- private $_domain = "";
- private $_password = "";
- private $_cache;
- private $_deviceType;
- private $_deviceAgent;
- private $hasDefaultEventFolder =false;
- private $hasDefaultContactFolder= false;
- private $hasDefaultTaskFolder = false;
- private $foMode = false;
- private $_mbox;
- private $_KolabHomeServer;
- private $_cn;
- private $_email;
- private $sentFolder="";
- /* Called to logon a user. These are the three authentication strings that you must
- * specify in ActiveSync on the PDA. Normally you would do some kind of password
- * check here. Alternatively, you could ignore the password here and have Apache
- * do authentication via mod_auth_*
- */
- function Logon($username, $domain, $password) {
- $this->_wasteID = false;
- $this->_sentID = false;
- $this->_username = $username;
- $this->_domain = $domain;
- $this->_password = $password;
- if (!$this->getLdapAccount())
- {
- return false;
- }
- $this->_server = "{" . $this->_KolabHomeServer . ":" . KOLAB_IMAP_PORT . "/imap" . KOLAB_IMAP_OPTIONS . "}";
- $this->Log("Connecting to ". $this->_server);
- if (!function_exists("imap_open"))
- {
- debugLog("ERROR BackendIMAP : PHP-IMAP module not installed!!!!!");
- $this->Log("module PHP imap not installed ") ;
- }
- // open the IMAP-mailbox
- $this->_mbox = @imap_open($this->_server , $username, $password, OP_HALFOPEN);
- $this->_mboxFolder = "";
- if ($this->_mbox) {
- debugLog("KolabBackend Version : " . KOLABBACKEND_VERSION);
- debugLog("KolabActiveSyndData Version : " .KOLABACTIVESYNCDATA_VERSION);
- $this->Log("KolabBackend Version : " . KOLABBACKEND_VERSION);
- $this->Log("KolabActiveSyndData Version : " .KOLABACTIVESYNCDATA_VERSION);
- $this->Log("IMAP connection opened sucessfully user : " . $username );
- // set serverdelimiter
- $this->_serverdelimiter = $this->getServerDelimiter();
- return true;
- }
- else {
- $this->Log("IMAP can't connect: " . imap_last_error() . " user : " . $this->_user . " Mobile ID:" . $this->_devid);
- return false;
- }
- }
- /* Called before shutting down the request to close the IMAP connection
- */
- function Logoff() {
- if ($this->_mbox) {
- // list all errors
- $errors = imap_errors();
- if (is_array($errors)) {
- foreach ($errors as $e) debugLog("IMAP-errors: $e");
- }
- @imap_close($this->_mbox);
- debugLog("IMAP connection closed");
- $this->Log("IMAP connection closed");
- unset($this->_cache);
- }
- }
- /* Called directly after the logon. This specifies the client's protocol version
- * and device id. The device ID can be used for various things, including saving
- * per-device state information.
- * The $user parameter here is normally equal to the $username parameter from the
- * Logon() call. In theory though, you could log on a 'foo', and then sync the emails
- * of user 'bar'. The $user here is the username specified in the request URL, while the
- * $username in the Logon() call is the username which was sent as a part of the HTTP
- * authentication.
- */
- function Setup($user, $devid, $protocolversion) {
- $this->_user = $user;
- $this->_devid = $devid;
- $this->_protocolversion = $protocolversion;
- if ($devid == "")
- {
- //occurs in the OPTION Command
- return true;
- }
- $this->_deviceType=$_REQUEST["DeviceType"];
- $this->_deviceAgent=$_SERVER["HTTP_USER_AGENT"];
- $this->Log("Setup : " . $user. " Mobile ID :" . $devid. " Proto Version : " . $protocolversion ." DeviceType : ". $this->_deviceType . " DeviceAgent : ". $this->_deviceAgent);
- $this->_cache=new userCache();
- $this->CacheCheckVersion();
- //read globalparam .
- $gp=$this->kolabReadGlobalParam();
- $mode=KOLAB_MODE;
- if ($gp != false )
- {
- //search if serial already in it;
- if ( $gp->getDeviceType($devid))
- {
- if ( $gp->getDeviceMode($devid) != -1)
- {
- $mode=$gp->getDeviceMode($devid);
- }
- }
- else
- {
- //no present we must write it;
- $gp->setDevice($devid,$this->_deviceType) ;
- if ( ! $this->kolabWriteGlobalParam($gp))
- {
- $this->Log("ERR cant write Globalparam");
- }
- }
- }
- switch($mode)
- {
- case 0:$this->foMode = false;
- $this->Log("NOTICE : Forced to flatmode") ;
- break;
- case 1:$this->foMode = true;
- $this->Log("NOTICE : Forced to foldermode") ;
- break;
- case 2:$this->foMode = $this->findMode();
- break;
- }
- return true;
- }
- /* 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.
- */
- private function findMode()
- {
- $type=explode(":",KOLAB_MOBILES_FOLDERMODE);
- if (in_array(strtolower($this->_deviceType),$type))
- {
- $this->Log("NOTICE : findMode Foldermode") ;
- return 1;
- }
- $this->Log("NOTICE : findMode Flatmode") ;
- return 0;
- }
- function SendMail($rfc822, $forward = false, $reply = false, $parent = false) {
- debugLog("IMAP-SendMail: " . $rfc822 . "for: $forward reply: $reply parent: $parent" );
- //
- $mobj = new Mail_mimeDecode($rfc822);
- $message = $mobj->decode(array('decode_headers' => false, 'decode_bodies' => true, 'include_bodies' => true, 'input' => $rfc822, 'crlf' => "\n", 'charset' => 'utf-8'));
- $toaddr = $ccaddr = $bccaddr = "";
- if(isset($message->headers["to"]))
- $toaddr = $this->parseAddr(Mail_RFC822::parseAddressList($message->headers["to"]));
- if(isset($message->headers["cc"]))
- $ccaddr = $this->parseAddr(Mail_RFC822::parseAddressList($message->headers["cc"]));
- if(isset($message->headers["bcc"]))
- $bccaddr = $this->parseAddr(Mail_RFC822::parseAddressList($message->headers["bcc"]));
- // save some headers when forwarding mails (content type & transfer-encoding)
- $headers = "";
- $forward_h_ct = "";
- $forward_h_cte = "";
- $use_orgbody = false;
- // clean up the transmitted headers
- // remove default headers because we are using imap_mail
- $changedfrom = false;
- $returnPathSet = false;
- $body_base64 = false;
- $org_charset = "";
- foreach($message->headers as $k => $v) {
- if ($k == "subject" || $k == "to" || $k == "cc" || $k == "bcc")
- continue;
- if ($k == "content-type") {
- // save the original content-type header for the body part when forwarding
- if ($forward) {
- $forward_h_ct = $v;
- continue;
- }
- // set charset always to utf-8
- $org_charset = $v;
- $v = preg_replace("/charset=([A-Za-z0-9-\"']+)/", "charset=\"utf-8\"", $v);
- }
- if ($k == "content-transfer-encoding") {
- // if the content was base64 encoded, encode the body again when sending
- if (trim($v) == "base64") $body_base64 = true;
- // save the original encoding header for the body part when forwarding
- if ($forward) {
- $forward_h_cte = $v;
- continue;
- }
- }
- // if the message is a multipart message, then we should use the sent body
- if (!$forward && $k == "content-type" && preg_match("/multipart/i", $v)) {
- $use_orgbody = true;
- }
- // check if "from"-header is set
- if ($k == "from" ) {
- $changedfrom = true;
- if (! trim($v) )
- {
- $v = $this->_email;
- }
- }
- // check if "Return-Path"-header is set
- if ($k == "return-path") {
- $returnPathSet = true;
- if (! trim($v) ) {
- $v = $this->_email;
- }
- }
- // all other headers stay
- if ($headers) $headers .= "\n";
- $headers .= ucfirst($k) . ": ". $v;
- }
-
- // set "From" header if not set on the device
- if( !$changedfrom){
- $v = $this->_email;
- if ($headers) $headers .= "\n";
- $headers .= 'From: '.$v;
- }
- // set "Return-Path" header if not set on the device
- if(!$returnPathSet){
- $v = $this->_email;
- if ($headers) $headers .= "\n";
- $headers .= 'Return-Path: '.$v;
- }
-
- // if this is a multipart message with a boundary, we must use the original body
- if ($use_orgbody) {
- list(,$body) = $mobj->_splitBodyHeader($rfc822);
- }
- else
- $body = $this->getBody($message);
- // reply
- if (isset($reply) && isset($parent) && $reply && $parent) {
- $this->imap_reopenFolder($parent);
- // receive entire mail (header + body) to decode body correctly
- $origmail = @imap_fetchheader($this->_mbox, $reply, FT_PREFETCHTEXT | FT_UID) . @imap_body($this->_mbox, $reply, FT_PEEK | FT_UID);
- $mobj2 = new Mail_mimeDecode($origmail);
- // receive only body
- $body .= $this->getBody($mobj2->decode(array('decode_headers' => false, 'decode_bodies' => true, 'include_bodies' => true, 'input' => $origmail, 'crlf' => "\n", 'charset' => 'utf-8')));
- // unset mimedecoder & origmail - free memory
- unset($mobj2);
- unset($origmail);
- }
- // encode the body to base64 if it was sent originally in base64 by the pda
- // the encoded body is included in the forward
- if ($body_base64) $body = base64_encode($body);
- // forward
- if (isset($forward) && isset($parent) && $forward && $parent) {
- $this->imap_reopenFolder($parent);
- // receive entire mail (header + body)
- $origmail = @imap_fetchheader($this->_mbox, $forward, FT_PREFETCHTEXT | FT_UID) . @imap_body($this->_mbox, $forward, FT_PEEK | FT_UID);
- // build a new mime message, forward entire old mail as file
- list($aheader, $body) = $this->mail_attach("forwarded_message.eml",strlen($origmail),$origmail, $body, $forward_h_ct, $forward_h_cte);
- // unset origmail - free memory
- unset($origmail);
- // add boundary headers
- $headers .= "\n" . $aheader;
- }
- $headers .="\n";
- $send = @imap_mail ( $toaddr, $message->headers["subject"], $body, $headers, $ccaddr, $bccaddr);
- $errors = imap_errors();
- if (is_array($errors)) {
- foreach ($errors as $e) debugLog("IMAP-errors: $e");
- }
- // email sent?
- if (!$send) {
- debugLog("The email could not be sent. Last-IMAP-error: ". imap_last_error());
- }
- // add message to the sent folder
- // build complete headers
- $cheaders = "To: " . $toaddr. "\n";
- $cheaders .= $headers;
- $asf = false;
- //try to see if there are a folder with the annotation
- $sent=$this->readDefaultSentItemFolder();
- $body=str_replace("\n","\r\n",$body);
- $cheaders=str_replace(": ",": ",$cheaders);
- $cheaders=str_replace("\n","\r\n",$cheaders);
- if ($sent) {
- $asf = $this->addSentMessage($sent, $cheaders, $body);
- }
- else if ($this->sentFolder) {
- $asf = $this->addSentMessage($this->sentFolder, $cheaders, $body);
- debugLog("IMAP-SendMail: Outgoing mail saved in configured 'Sent' folder '".$this->sentFolder."': ". (($asf)?"success":"failed"));
- }
- // No Sent folder set, try defaults
- else {
- debugLog("IMAP-SendMail: No Sent mailbox set");
- if($this->addSentMessage("INBOX/Sent", $cheaders, $body)) {
- debugLog("IMAP-SendMail: Outgoing mail saved in 'INBOX/Sent'");
- $asf = true;
- }
- else if ($this->addSentMessage("Sent", $cheaders, $body)) {
- debugLog("IMAP-SendMail: Outgoing mail saved in 'Sent'");
- $asf = true;
- }
- else if ($this->addSentMessage("Sent Items", $cheaders, $body)) {
- debugLog("IMAP-SendMail: Outgoing mail saved in 'Sent Items'");
- $asf = true;
- }
- }
- $errors = imap_errors();
- if (is_array($errors)) {
- foreach ($errors as $e) debugLog("IMAP-errors: $e");
- }
- // unset mimedecoder - free memory
- unset($mobj);
- return ($send && $asf);
- }
- /* 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() {
- return $this->_wasteID;
- }
- private function GetMessagesListByType($foldertype,$cutoffdate)
- {
- $lastfolder="";
- $messages=array();
- $list = @imap_getmailboxes($this->_mbox, $this->_server, "*");
- if (is_array($list)) {
- $list = array_reverse($list);
- foreach ($list as $val) {
- //$folder=imap_utf7_decode(substr($val->name, strlen($this->_server)));
- $folder=substr($val->name, strlen($this->_server));
- //$this->saveFolderAnnotation($folder);
- $ft=$this->kolabFolderType($folder);
- if ($ft != $foldertype)
- {
- continue;
- }
- $isUser=false;
- $isShared=false;
- if (substr($folder,0,4) =="user"){$isUser=true;}
- if (substr($folder,0,6) =="shared"){$isShared=true;}
- $fa=$this->kolabReadFolderParam($folder);
- //here we must push theo object in the cache to
- //dont have to read it again at each message ( for the alarms)
- $this->CacheWriteFolderParam($folder,$fa);
- $fa->setFolder($folder);
- if ( ! $fa->isForSync($this->_devid))
- {
- //not set to sync
- continue;
- }
- //want user namespace ?
- /*
- if ( !KOLAB_USERFOLDER_DIARY && $foldertype == 2 && $isUser)
- {
- continue;
- }
- if ( !KOLAB_USERFOLDER_CONTACT && $foldertype == 1 && $isUser)
- {
- continue;
- }
- //want shared namespace ?
- if ( !KOLAB_SHAREDFOLDER_DIARY && $foldertype == 2 && $isShared)
- {
- continue;
- }
- if ( !KOLAB_SHAREDFOLDER_CONTACT && $foldertype == 1 && $isShared)
- {
- continue;
- }
- */
- if ( $this->CacheGetDefaultFolder($foldertype) == false)
- {
- //no default
- if (substr($folder,0,5) == "INBOX")
- {
- $n=array_pop(explode("/",$folder));
- $result=false;
- switch($foldertype)
- {
- case 1: $result=$this->isDefaultFolder($n,KOLAB_DEFAULTFOLDER_CONTACT);
- break;
- case 2: $result=$this->isDefaultFolder($n,KOLAB_DEFAULTFOLDER_DIARY);
- break;
- case 3: $result=$this->isDefaultFolder($n,KOLAB_DEFAULTFOLDER_TASK);
- break;
- }
- if ( $result == true)
- {
- $this->forceDefaultFolder($foldertype,$folder);
- }
- else
- {
- $lastfolder=$folder;
- }
- }
- }
- $this->imap_reopenFolder($folder);
-
- /*trying optimizing the reads*/
- /*if ($this->isFolderModified($folder) == false )
- {
- $this->Log("NOTICE : folder not modified $folder");
- $message_folder=$this->CacheReadMessageList($folder);
- if (count($message)> 0)
- {
- $messages=array_merge($messages,$message_folder);
- continue;
- }
- } */
- $overviews = @imap_fetch_overview($this->_mbox, "1:*",FT_UID);
- if (!$overviews) {
- debugLog("IMAP-GetMessageList: $folder Failed to retrieve overview");
- } else {
- $message_infolder=array();
- foreach($overviews as $overview) {
- $date = "";
- $vars = get_object_vars($overview);
- if (array_key_exists( "deleted", $vars) && $overview->deleted)
- continue;
- $message=$this->KolabStat($folder,$overview);
- if (! $message){continue;}
- //cutoffdate for appointment
- if ( $foldertype == 2)
- {
- //look for kolabuid
- $this->Log("try cutoffdate for message id ".$message["id"]);
- $enddate= $this->CacheReadEndDate($folder,$message["id"]);
- if ($enddate != - 1 && $cutoffdate > $enddate)
- {
- //cuteoffdate
- $this->Log("cuteoffDate :" . $message["id"] );
- continue;
- }
- if ( substr($folder,0,5) != "INBOX")
- {
- if ($this->CacheReadSensitivity($message["id"]))
- {
- //check if private for namespace <> INBOX
- continue;
- }
- }
- }
- //check if key is duplicated
- if (isset($checkId[$message["id"]]))
- {
- //uid exist
- $this->Log("Key : " .$message["id"] ." duplicated folder :" . $folder ." Imap id : " . $checkId[$message["id"]]);
- debugLog("Key : " .$message["id"] ." duplicated folder :" . $folder ." Imap id : " . $checkId[$message["id"]]);
- //rewrite the index to have the good imapid
- $id=array_pop(explode("/",$checkId[$message["id"]]));
- $this->CacheCreateIndex($folder,$message["id"],$id);
- continue;
- }
- else
- {
- $checkId[$message["id"]] = $message["mod"];
- }
- //here check the cutdate for appointments
- debugLog("ListMessage : " . $message["id"] . "->" . $message["mod"] ) ;
- $messages[]=$message;
- $message_infolder[]=$message;
- }
- $this->CacheStoreMessageList($folder,$message_infolder);
- }
- }
- //check if we found a default folder for this type
- if ( $this->CacheGetDefaultFolder($foldertype) == false)
- {
- //no we pur the last folder found as default;
- $this->forceDefaultFolder($foldertype,$lastfolder);
- }
-
- unset($checkId);
- unset($overviews);
- return $messages;
- }
- }
- private function statImapFolder($folder)
- {
- $info=imap_status($this->_mbox, $this->_server .$folder, SA_ALL) ;
- return serialize($info);
- }
- /* Should return a list (array) of messages, each entry being an associative array
- * with the same entries as StatMessage(). This function should return stable information; ie
- * if nothing has changed, the items in the array must be exactly the same. The order of
- * the items within the array is not important though.
- *
- * The cutoffdate is a date in the past, representing the date since which items should be shown.
- * This cutoffdate is determined by the user's setting of getting 'Last 3 days' of e-mail, etc. If
- * you ignore the cutoffdate, the user will not be able to select their own cutoffdate, but all
- * will work OK apart from that.
- */
- function GetMessageList($folderid, $cutoffdate)
- {
- $messages = array();
- $checkId = array();
- if ($folderid == "VIRTUAL/calendar")
- {
- //flat mode
- //search all folders of type calendar
- $messages=$this->GetMessagesListByType(2,$cutoffdate);
- }
- else if ($folderid == "VIRTUAL/contacts")
- {
- $messages=$this->GetMessagesListByType(1,$cutoffdate);
- }
- else if ($folderid == "VIRTUAL/tasks")
- {
- $messages=$this->GetMessagesListByType(3,$cutoffdate);
- }
- else
- {
- $this->imap_reopenFolder($folderid, true);
- //check if the folder as moved by imap stat
- /*
- if ($this->isFolderModified($folderid) == false )
- {
- $this->Log("NOTICE : folder not modified $folderid");
- $messages=$this->CacheReadMessageList($folderid);
- return $messages;
- } */
- $overviews = @imap_fetch_overview($this->_mbox, "1:*",FT_UID);
- if (!$overviews) {
- debugLog("IMAP-GetMessageList: $folderid Failed to retrieve overview");
- } else {
- foreach($overviews as $overview) {
- $date = "";
- $vars = get_object_vars($overview);
- // cut of deleted messages
- if (array_key_exists( "deleted", $vars) && $overview->deleted)
- continue;
- $folderType=$this->kolabFolderType($folderid);
- if ( $folderType> 0)
- {
- //kolab contacts and appointment special index
- //mode is the imap uid because kolab delete the message and recreate a newone in case
- //of modification
- $message=$this->KolabStat($folderid,$overview);
- if (! $message){continue;}
- //cutoffdate for appointment
- if ( $folderType == 2)
- {
- //look for kolabuid
- $this->Log("try cutoffdate for message id ".$message["id"]);
- $enddate= $this->CacheReadEndDate($folderid,$message["id"]);
- if ($enddate != - 1 && $cutoffdate > $enddate)
- {
- //cuteoffdate
- $this->Log("cuteoffDate too old");
- continue;
- }
- if ( substr($folderid,0,5) != "INBOX")
- {
- if ($this->CacheReadSensitivity($message["id"]))
- {
- //check if private for namespace <> INBOX
- continue;
- }
- }
- }
- //check if key is duplicated
- if (isset($checkId[$message["id"]]))
- {
- $this->Log("Key : " .$message["id"] ." duplicated folder :" . $folder ." Imap id : " . $checkId[$message["id"]]);
- debugLog("Key : " .$message["id"] ." duplicated folder :" . $folder ." Imap id : " . $checkId[$message["id"]]);
- //rewrite the index to have the good imapid
- $id=array_pop(explode("/",$checkId[$message["id"]]));
- $this->CacheCreateIndex($folder,$message["id"],$id);
- continue;
- }
- else
- {
- $checkId[$message["id"]] = $message["mod"];
- }
- //here check the cutdate for appointments
- debugLog("ListMessage : " . $message["id"] . "->" . $message["mod"] ) ;
- $messages[]=$message;
- }
- else
- {
- if (array_key_exists( "date", $vars)) {
- // message is out of range for cutoffdate, ignore it
- if(strtotime($overview->date) < $cutoffdate) continue;
- $date = $overview->date;
- }
- if (array_key_exists( "uid", $vars))
- {
- $message = array();
- $message["mod"] = $date;
- $message["id"] = $overview->uid;
- // 'seen' aka 'read' is the only flag we want to know about
- $message["flags"] = 0;
- if(array_key_exists( "seen", $vars) && $overview->seen)
- $message["flags"] = 1;
- array_push($messages, $message);
- }
- }
- }
- }
- //clean the index before leave
- $this->CacheIndexClean($messages) ;
- //$this->Log("Get Message List : " . count($messages)) ;
- }
- debugLog("MEM GetmessageList End:" . memory_get_usage()) ;
- $this->CacheStoreMessageList($folderid,$messages);
- return $messages;
- }
- private function isFolderModified($folder)
- {
- $newstatus=@imap_status($this->_mbox,$this->_server. $folder,SA_ALL);
- $oldstatus=$this->CacheReadImapStatus($folder);
- //found the old status;
- //we compare
- if ( $oldstatus->uidnext == $newstatus->uidnext && $oldstatus->messages == $newstatus->messages)
- {
- //the folder has not been modified
- return False;
- }
- $this->CacheStoreImapStatus($folder,$newstatus);
- return true;
- }
- /* This function is analogous to GetMessageList.
- *
- */
- function GetFolderList()
- {
-
- if ( $this->foMode == true)
- {
- return $this->GetFolderListFoMode();
- }
- else
- {
- return $this->GetFolderListFlMode();
- }
- }
- private function GetFolderListFlMode()
- {
- $folders = array();
- $list = @imap_getmailboxes($this->_mbox, $this->_server, "*");
- //add the virtual folders for contacts calendars and tasks
- $virtual=array("VIRTUAL/calendar","VIRTUAL/contacts","VIRTUAL/tasks");
- //$virtual=array("VIRTUAL/calendar","VIRTUAL/contacts");
- foreach ($virtual as $v)
- {
- $box=array();
- $box["id"]=$v;
- $box["mod"] =$v;
- $box["flags"]=0;
- $folders[]=$box;
- }
- if (is_array($list)) {
- $list = array_reverse($list);
- foreach ($list as $val) {
- $box = array();
- // cut off serverstring
- $box["flags"]=0;
- //$box["id"] = imap_utf7_decode(substr($val->name, strlen($this->_server)));
- $box["id"] =substr($val->name, strlen($this->_server));
- //rerid the annotations
- $this->saveFolderAnnotation($box["id"]);
- $foldertype=$this->readFolderAnnotation($box["id"]);
- //if folder type > 0 escape
- if ( substr($foldertype,0,5) == "event")
- {
- continue;
- }
- if ( substr($foldertype,0,7) == "contact")
- {
- continue;
- }
- if ( substr($foldertype,0,4) == "task")
- {
- continue;
- }
- //other folders (mails)
- //$box["id"] = imap_utf7_encode( $box["id"]);
- $fhir = explode("/", $box["id"]);
- if (count($fhir) > 1) {
- $box["mod"] = imap_utf7_encode(array_pop($fhir)); // mod is last part of path
- $box["parent"] = imap_utf7_encode(implode("/", $fhir)); // parent is all previous parts of path
- }
- else {
- $box["mod"] = imap_utf7_encode($box["id"]);
- $box["parent"] = "0";
- }
- $folders[]=$box;
- }
- }
- else {
- debugLog("GetFolderList: imap_list failed: " . imap_last_error());
- }
- return $folders;
- }
- private function GetFolderListFoMode() {
- $folders = array();
- $list = @imap_getmailboxes($this->_mbox, $this->_server, "*");
- $this->hasDefaultEventFolder=false;
- $this->hasDefaultContactFolder=false;
- $this->hasDefaultTaskFolder=false;
- if (is_array($list)) {
- //create the
- // reverse list to obtain folders in right order
- $list = array_reverse($list);
- foreach ($list as $val) {
- $box = array();
- // cut off serverstring
- $box["flags"]=0;
- //$box["id"] = imap_utf7_decode(substr($val->name, strlen($this->_server)));
- $box["id"]= substr($val->name, strlen($this->_server));
- //determine the type en default folder
- $isUser=false;
- $isShared=false;
- $isInbox=false;
- //rerid the annotations
- $this->saveFolderAnnotation($box["id"]);
- $foldertype=$this->readFolderAnnotation($box["id"]);
- $defaultfolder = false;
- //defaultfolder ?
- if ( $foldertype == "event.default")
- {
- $this->hasDefaultEventFolder=true;
- $defaultfolder = true;
- }
- if ( $foldertype == "contact.default")
- {
- $this->hasDefaultContactFolder=true;
- $defaultfolder = true;
- }
- if ( $foldertype == "task.default")
- {
- $this->hasDefaultTaskFolder=true;
- $defaultfolder = true;
- }
- // workspace of the folder;
- if (substr( $box["id"],0,6) == "shared")
- {
- //this is a shared folder
- $isShared=true;
- }
- if (substr( $box["id"],0,4) == "user")
- {
- //this is a User shared folder
- $isUser=true;
- }
- if (substr( $box["id"],0,5) == "INBOX")
- {
- $isInbox=true;
- }
- //selection of the folder depending to the setup
- if (! $defaultfolder)
- {
-
- //test annotation
- $fa=$this->kolabReadFolderParam($box["id"]);
- //for later use (in getMessage)
- $this->CacheWriteFolderParam($box["id"],$fa);
- $fa->setfolder($box["id"]);
- if ( ! $fa->isForSync($this->_devid))
- {
- //not set to sync
- continue;
- }
- }
- $this->Log("NOTICE SyncFolderList Add folder ".$box["id"]);
- //$box["id"] = imap_utf7_encode( $box["id"]);
- if ($isShared)
- {
- $fhir = explode(".", $box["id"]);
- $box["mod"] = imap_utf7_encode($fhir[1]);
- $box["parent"] = "shared";
- }
- elseif ($isUser)
- {
- $box["mod"] = imap_utf7_encode(array_pop($fhir));
- $box["parent"] = "user";
- }
- else
- {
- // explode hierarchies
- $fhir = explode("/", $box["id"]);
- $t=count($fhir);
- if (count($fhir) > 1) {
- $box["mod"] = imap_utf7_encode(array_pop($fhir)); // mod is last part of path
- $box["parent"] = imap_utf7_encode(implode("/", $fhir)); // parent is all previous parts of path
- }
- else {
- $box["mod"] = imap_utf7_encode($box["id"]);
- $box["parent"] = "0";
- }
- }
- $folders[]=$box;
- }
- }
- else {
- debugLog("GetFolderList: imap_list failed: " . imap_last_error());
- }
- return $folders;
- }
- /* GetFolder should return an actual SyncFolder object with all the properties set. Folders
- * are pretty simple really, having only a type, a name, a parent and a server ID.
- */
- function GetFolder($id) {
- $folder = new SyncFolder();
- $folder->serverid = $id;
- // explode hierarchy
- $fhir = explode("/", $id);
- if ( substr($id,0,6) == "shared")
- {
- $parent="shared";
- }
- else
- {
- $ftmp=$fhir;
- array_pop($ftmp);
- $parent=implode("/", $ftmp);
- }
- //get annotation type
- // compare on lowercase strings
- $lid = strtolower($id);
- $fimap=$id;
- if($lid == "inbox") {
- $folder->parentid = "0"; // Root
- $folder->displayname = "Inbox";
- $folder->type = SYNC_FOLDER_TYPE_INBOX;
- }
- // courier-imap outputs
- else if($lid == "inbox/drafts") {
- $folder->parentid = $fhir[0];
- $folder->displayname = "Drafts";
- $folder->type = SYNC_FOLDER_TYPE_DRAFTS;
- }
- else if($lid == "inbox/trash") {
- $folder->parentid = $fhir[0];
- $folder->displayname = "Trash";
- $folder->type = SYNC_FOLDER_TYPE_WASTEBASKET;
- $this->_wasteID = $id;
- }
- else if($lid == "inbox/sent") {
- $folder->parentid = $fhir[0];
- $folder->displayname = "Sent";
- $this->sentFolder=$id;
- $folder->type = SYNC_FOLDER_TYPE_SENTMAIL;
- $this->_sentID = $id;
- }
- // define the rest as other-folders
- //check if flatmode
- else if ( $this->foMode == False && $id == "VIRTUAL/calendar")
- {
- $folder->parentid ="VIRTUAL";
- $folder->displayname = $id;
- $folder->type = SYNC_FOLDER_TYPE_APPOINTMENT;
- $this->_sentID = $id;
- }
- else if ( $this->foMode == False && $id == "VIRTUAL/contacts")
- {
- $folder->parentid = "VIRTUAL";
- $folder->displayname = "Contacts";
- $folder->type = SYNC_FOLDER_TYPE_CONTACT;
- $this->_sentID = $id;
- }
- else if ( $this->foMode == False && $id == "VIRTUAL/tasks")
- {
- $folder->parentid = "VIRTUAL";
- $folder->displayname = $id;
- $folder->type = SYNC_FOLDER_TYPE_TASK;
- $this->_sentID = $id;
- }
- else if ( $this->kolabfolderType($id) == 1)
- {
- //contact kolab
- $folder->parentid = $parent;
- $folder->displayname = $this->folderDisplayName($id);
- $folder->type = $this->ActiveSyncFolderSyncType($id);
- $this->_sentID = $id;
- }
- else if ($this->kolabfolderType($id) == 2)
- {
- // shared folder in UPPER ,
- $folder->parentid = $parent;
- $folder->displayname = $this->folderDisplayName($id);
- $folder->type = $this->ActiveSyncFolderSyncType($id);
- $this->_sentID = $id;
- }
- else if ($this->kolabfolderType($id) == 3)
- {
- $folder->parentid = $parent;
- $folder->displayname = $this->folderDisplayName($id);
- $folder->type = $this->ActiveSyncFolderSyncType($id);
- $this->_sentID = $id;
- }
- else {
- if (count($fhir) > 1) {
- $folder->displayname = windows1252_to_utf8(imap_utf7_decode(array_pop($fhir)));
- $folder->parentid = implode("/", $fhir);
- }
- else {
- $folder->displayname = windows1252_to_utf8(imap_utf7_decode($id));
- $folder->parentid = "0";
- }
- $folder->type = SYNC_FOLDER_TYPE_OTHER;
- }
- //advanced debugging
- //debugLog("IMAP-GetFolder(id: '$id') -> " . print_r($folder, 1));
- return $folder;
- }
- /* 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)
- */
- private function folderDisplayName($folder)
- {
- $f = explode("/", $folder);
- if (substr($f[0],0,6) == "shared" )
- {
- // shared folder in UPPER
- $s=explode(".",$folder) ;
- return strtoupper(windows1252_to_utf8(imap_utf7_decode($s[1])));
- }
- if ($f[0] == "INBOX")
- {
- $type=$this->readFolderAnnotation($folder);
- if ($type =="contact.default" || $type =="event.default" || $type =="task.default")
- {
- //default folder all min lowaercase
- $r=windows1252_to_utf8(imap_utf7_decode($f[1]));
- return strtolower(windows1252_to_utf8(imap_utf7_decode(array_pop($f))));
- }
- else
- {
- //others AA problem when we have sub sub folder
- //must keep the last one
- return ucfirst(windows1252_to_utf8(imap_utf7_decode(array_pop($f))));
- }
- }
- if ($f[0] == "user")
- {
- $type=$this->readFolderAnnotation($folder);
- $t=explode(".",$type);
- //find the user
- $fname=array_pop($f);
- $r=windows1252_to_utf8(imap_utf7_decode($fname."(".$f[1].")"));
- return windows1252_to_utf8($r);
- }
- }
- function StatFolder($id) {
- $folder = $this->GetFolder($id);
- $stat = array();
- $stat["id"] = $id;
- $stat["parent"] = $folder->parentid;
- $stat["mod"] = $folder->displayname;
- return $stat;
- }
- /* Creates or modifies a folder
- * "folderid" => id of the parent folder
- * "oldid" => if empty -> new folder created, else folder is to be renamed
- * "displayname" => new folder name (to be created, or to be renamed to)
- * "type" => folder type, ignored in IMAP
- *
- */
- function ChangeFolder($folderid, $oldid, $displayname, $type){
- debugLog("ChangeFolder: (parent: '$folderid' oldid: '$oldid' displayname: '$displayname' type: '$type')");
- // go to parent mailbox
- $this->imap_reopenFolder($folderid);
- // build name for new mailbox
- $newname = $this->_server . str_replace(".", $this->_serverdelimiter, $folderid) . $this->_serverdelimiter . $displayname;
- $csts = false;
- // if $id is set => rename mailbox, otherwise create
- if ($oldid) {
- // rename doesn't work properly with IMAP
- // the activesync client doesn't support a 'changing ID'
- //$csts = imap_renamemailbox($this->_mbox, $this->_server . imap_utf7_encode(str_replace(".", $this->_serverdelimiter, $oldid)), $newname);
- }
- else {
- $csts = @imap_createmailbox($this->_mbox, $newname);
- }
- if ($csts) {
- return $this->StatFolder($folderid . "." . $displayname);
- }
- else
- return false;
- }
- /* 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.
- */
- function GetAttachmentData($attname) {
- debugLog("getAttachmentDate: (attname: '$attname')");
- list($folderid, $id, $part) = explode(":", $attname);
- $this->imap_reopenFolder($folderid);
- $mail = @imap_fetchheader($this->_mbox, $id, FT_PREFETCHTEXT | FT_UID) . @imap_body($this->_mbox, $id, FT_PEEK | FT_UID);
- $mobj = new Mail_mimeDecode($mail);
- $message = $mobj->decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'input' => $mail, 'crlf' => "\n", 'charset' => 'utf-8'));
- if (isset($message->parts[$part]->body))
- print $message->parts[$part]->body;
- // unset mimedecoder & mail
- unset($mobj);
- unset($mail);
- return true;
- }
- /* StatMessage should return message stats, analogous to the folder stats (StatFolder). Entries are:
- * 'id' …
Large files files are truncated, but you can click here to view the full file