* support dele command
authorWilfried Göesgens <willi@citadel.org>
Sun, 27 Jan 2008 17:27:32 +0000 (17:27 +0000)
committerWilfried Göesgens <willi@citadel.org>
Sun, 27 Jan 2008 17:27:32 +0000 (17:27 +0000)
* z-push plugin
* sample edit config command

ctdlphp/ctdlelements.php
ctdlphp/ctdlprotocol.php
ctdlphp/ctdlsession.php
ctdlphp/myinfo.php
ctdlphp/z-push/citadel.php [new file with mode: 0644]

index a0d422f4652d0bb7141f048007782d484af0704a..e03b0783213f78398b89fe7c1e4dec4b0547a1d5 100644 (file)
@@ -54,7 +54,10 @@ function display_message($msgnum) {
        }
 
        // Do message text
-       echo $fields["text"] . "<BR>";
+       if ($fields['formatet_text'] != "")
+               echo $fields['formatet_text'] . "<BR>";
+       else 
+               echo $fields["text"] . "<BR>";
 
        echo '</TD></TR></TABLE><BR>' ;
 }
index b08ed6a3ad09843c378166d7eaf2c9b8beb6a624..6f434822a946f0c6f4e96a4ff0b2ae0f344df122 100644 (file)
@@ -372,7 +372,7 @@ function become_logged_in($server_parms) {
 function ctdl_get_serv_info() {
        serv_puts("INFO");
        $reply = read_array();
-       if ((count($reply) == 18) &&
+       if ((count($reply) == 22) &&
            substr($reply[0], 0, 1) == "1") {
                $server_info=array();
                $server_info["serv_nodename"]  = $reply[1];
@@ -390,8 +390,12 @@ function ctdl_get_serv_info() {
                return $server_info;
        }
        else 
-               die ("didn't understand the reply to the INFO command");
-
+       {
+               dbgprintf_wrapin ("didn't understand the reply to the INFO command".
+                                 print_r($reply, TRUE), false);
+               
+               die ("CTDLPHP: didn't understand the reply to the INFO command");
+       }
 }
 
 //
@@ -434,6 +438,26 @@ function ctdl_mesg($msgname) {
        return($msgtext);
 }
 
+//
+// Delete a Message.
+// http://www.citadel.org/doku.php/documentation:appproto:room_indexes_and_messages#dele.delete.a.message
+
+function ctdl_dele($msgname) {
+       global $clientsocket;
+
+       $msgtext = "<DIV ALIGN=CENTER>\n";
+
+       serv_puts("DELE " . $msgname);
+       $response = serv_gets();
+
+       if (substr($response[0], 0, 1) == "1") {
+               return TRUE;
+       }
+       else {
+               return FALSE;
+       }
+}
+
 /* http://www.citadel.org/doku.php/documentation:appproto:connection#mesg.read.system.message */
 //// TODO: is this still supported?
 function ctdl_mrtg($what) {
@@ -687,7 +711,9 @@ function ctdl_fetch_message($msgnum) {
                        if (CITADEL_DEBUG_CITPROTO == 1)
                                dbgprintf_wrapout("</div>\n<h3>Message Body Follows</h3><div class='ctdldbgRead'>", false);
                        // We're in the text body.  New loop here.
-                       $fields["text"] = ctdl_msg4_from_server();
+                       $texts = ctdl_msg4_from_server();
+                       $fields["text"] = $texts[0];
+                       $fields["formated_text"]=$texts[1];
                        if (CITADEL_DEBUG_CITPROTO == 1)
                                dbgprintf_wrapout ("</div>", false);
                        return array(TRUE, substr($response, 4), $fields);
@@ -719,7 +745,10 @@ function ctdl_fetch_message_rfc822($msgnum) {
        while ($buf = serv_gets(TRUE)) {
 //             dbgprintf_wrapout($buf, true);
                if ($buf=="000")
+               {
+                       $message .= "\n.\n";
                        break;
+               }
                $message = $message . "\n" . $buf;
                $buf = "";
        }
@@ -735,6 +764,7 @@ function ctdl_fetch_message_rfc822($msgnum) {
 function ctdl_msg4_from_server() {
 
        $txt = "";
+       $modified_txt = "";
        $msgformat = "text/plain";
        $in_body = FALSE;
 
@@ -755,7 +785,9 @@ function ctdl_msg4_from_server() {
                                $txt .= $buf;
                        }
                        else if (!strcasecmp($msgformat, "text/plain")) {
-                               $txt .= "<TT>" . htmlspecialchars($buf) . "</TT><BR>\n" ;
+                               $txt .= "\r\n".$buf;
+                               $modified_ .= "<TT>" . htmlspecialchars($buf) . "</TT><BR>\n" ;
+
                        }
                        else if (!strcasecmp($msgformat, "text/x-citadel-variformat")) {
                                if (substr($previous_line, 0, 1) == " ") {
@@ -770,7 +802,7 @@ function ctdl_msg4_from_server() {
                }
        }
 
-       return($txt);
+       return(array($txt, $modified_txt));
 }
 
 
index a47ef2b011f0b4d40fa7b254e4455376ff846a4b..c9614c753ef57d2b24ee068d67e2d4eed051f4ca 100644 (file)
@@ -34,11 +34,15 @@ function establish_citadel_session() {
        // session, and it's found in the /tmp directory.
 
        $sockname = "/tmp/" . $session . ".socket" ;
-
+       $errno = 0; 
+       $errstr = "";
        if (is_array(stat($sockname)))
                $clientsocket = fsockopen(SOCKET_PREFIX.$sockname, 0, $errno, $errstr, 5);
        else
                $clientsocket = false;
+//// TODO: if we get connection refused...
+       echo "$socketname - $errno - $errstr";
+       
        if (!$clientsocket) {
                // It ain't there, dude.  Open up the proxy. (C version)
                //$cmd = "./sessionproxy " . $sockname ;
index a58c150a0cdaadc36dc81ea0919cd1b972ad77c1..0328da71b0f1a4b20e99c88b0f02e1497d45614b 100644 (file)
@@ -19,18 +19,36 @@ list($num_msgs, $response, $msgs) = ctdl_msgs("", "");
 
 echo "num_msgs: " . $num_msgs . "<BR>\n" ;
 echo "response: " . htmlspecialchars($response) . "<BR>\n" ;
-
+$Webcit_Preferences = array();
+$Webcit_PrefMsgid = 0;
 if ($num_msgs > 0) foreach ($msgs as $msgnum) {
        print_r($msgnum);
-       $result = get_message_partlist($msgnum);
-       if (is_array($result) &&
-           ($result[4]=="text/x-vcard"))
+
+       // Fetch the message from the server
+       list($ok, $response, $fields) = ctdl_fetch_message($msgnum);
+
+       // Bail out gracefully if the message isn't there.
+       if (!$ok) {
+               echo "Error: " . htmlspecialchars($response) . "<BR>" ;
+               return false;
+       }
+       if (isset($fields['part']))
        {
-               list($size, $vcard) = download_attachment($msgnum, $result[2]);
+               $parts = explode('|', $fields['part']);
+               print_r($parts);
+               if ($parts[4]=="text/x-vcard")
+                       list($size, $vcard) = download_attachment($msgnum, $result[2]);
+               else
+                       ctdl_dele($msgnum);
        }
+       else if ($fields['subj'] == "__ WebCit Preferences __")
+       {
+               $Webcit_PrefMsgid = $msgnum;
+               $Webcit_Preferences = $fields;
+       } 
 }
 
-echo "</TABLE>\n <pre>\n".$vcard."</pre>";
+echo "</TABLE>\n--------------------------------------- <pre>\n". $vcard."</pre>";
 
 echo "putting it back in!";
 
@@ -53,10 +71,25 @@ end:vcard
 ";
 preg_replace('/Sir/', 'Mrs',$vcard);
 echo "Now its: <pre>\n".$vcard."</pre>";
-$entearray=array();
+$entarray=array();
 $entarray['post']=1;
 $entarray['format_type']=FMT_RFC822;
 enter_message_0($entarray, "text/x-vcard; charset=UTF-8", $vcard);
+
+
+if ($Webcit_PrefMsgid != '0')
+{
+       ctdl_dele($Webcit_PrefMsgid);
+       
+       $entarray=array();
+       $entarray['post']=1;
+       $entarray['format_type']=FMT_FIXED;
+       $entarray['subject'] = $Webcit_Preferences['subj'];
+       $entarray['anon_flag'] = '0';
+       enter_message_0($entarray,
+                       "", 
+                       "somevar|somevalue\r\n".$Webcit_Preferences['text']);
+}
 ?>
 
 <BR>Sample links<BR>
diff --git a/ctdlphp/z-push/citadel.php b/ctdlphp/z-push/citadel.php
new file mode 100644 (file)
index 0000000..00a9ff7
--- /dev/null
@@ -0,0 +1,597 @@
+<?
+/***********************************************
+* File      :   maildir.php
+* Project   :   Z-Push
+* Descr     :   This backend is based on
+*                              'BackendDiff' which handles the
+*                              intricacies of generating
+*                              differentials from static
+*                              snapshots. This means that the
+*                              implementation here needs no
+*                              state information, and can simply
+*                              return the current state of the
+*                              messages. The diffbackend will
+*                              then compare the current state
+*                              to the known last state of the PDA
+*                              and generate change increments
+*                              from that.
+*
+* Created   :   01.10.2007
+*
+* © Zarafa Deutschland GmbH, www.zarafaserver.de
+* This file is distributed under GPL v2.
+* Consult LICENSE file for details
+************************************************/
+
+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('ctdlprotocol.php');
+
+include_once('ctdlsession.php');
+
+
+class BackendCitadel extends BackendDiff 
+{
+    /* 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) {
+           debugLog ("Logging in.\n");
+           establish_citadel_session();
+           $usr = explode ('\\', $username);
+///        debugLog(print_r($usr, true));
+           debugLog($password);
+           if (count ($usr) == 2)
+                   $username = $usr[1];
+           $ret = login_existing_user($username, $password);
+           if ($ret[0] != TRUE)
+                   echo $ret[1];
+           return $ret[0];
+    }
+
+    /* 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) {
+           debugLog ("Setup\n");
+        $this->_user = $user;
+        $this->_devid = $devid;
+        $this->_protocolversion = $protocolversion;
+        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.
+     */
+    function SendMessage($rfc822) {
+           debugLog("SendMessage\n");
+        // Unimplemented
+        return true;
+    }
+    
+    /* 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() {
+           debugLog("GetWasteBasket");
+        return "Trash";
+    }
+    
+    /* 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) {
+           debugLog("GetMessageList $folderid $cutoffdate");
+///        $this->moveNewToCur();
+
+           ctdl_goto ($folderid);
+        
+#        if($folderid != "root")
+#           return false;
+            
+        // return stats of all messages in a dir. We can do this faster than
+        // just calling statMessage() on each message; We still need fstat()
+        // information though, so listing 10000 messages is going to be
+        // rather slow (depending on filesystem, etc)
+        
+        // we also have to filter by the specified cutoffdate so only the 
+        // last X days are retrieved. Normally, this would mean that we'd
+        // have to open each message, get the Received: header, and check
+        // whether that is in the filter range. Because this is much too slow, we
+        // are depending on the creation date of the message instead, which should
+        // normally be just about the same, unless you just did some kind of import.
+        
+           $message = ctdl_msgs("","");
+           debugLog(print_r($message, true), true);
+           $messages = array();
+           
+           if ($message[0] > 0) for ($i=0; $i < $message[0]; $i ++)
+           {
+                   $thismessage["id"] = $message[2][$i];
+                   $thismessage["flags"] = 0;
+                   $thismessage["flags"] |= 1; // 'seen' aka 'read' is the only flag we want to know about
+                   array_push($messages, $thismessage);
+                   
+           }
+           return $messages;
+//        $messages = array();
+//        $dirname = $this->getPath();
+//        
+//        $dir = opendir($dirname);
+//        
+//        if(!$dir)
+//            return false;
+//        
+//        while($entry = readdir($dir)) {
+//            if($entry{0} == ".")
+//                continue;
+//                
+//            $message = array();
+//            
+//            $stat = stat("$dirname/$entry");
+//
+//            if($stat["mtime"] < $cutoffdate) {
+//                // message is out of range for curoffdate, ignore it
+//                continue;
+//            }
+//                            
+//            $message["mod"] = $stat["mtime"];
+//            
+//            $matches = array();
+//            
+//            // Flags according to http://cr.yp.to/proto/maildir.html (pretty authoritative - qmail author's website)
+//            if(!preg_match("/([^:]+):2,([PRSTDF]*)/",$entry,$matches))
+//                continue;
+//            $message["id"] = $matches[1];
+//            $message["flags"] = 0;
+//            
+//            if(strpos($matches[2],"S") !== false) {
+//                $message["flags"] |= 1; // 'seen' aka 'read' is the only flag we want to know about
+//            }
+//            
+//            array_push($messages, $message);
+//        }
+//        
+//        return $messages;
+    }
+    
+    /* This function is analogous to GetMessageList. In simple implementations like this one,
+     * you probably just return one folder.
+     */
+    function GetFolderList() {
+           $folders = array();
+           debugLog("GetFolderList");
+           $ret = ctdl_knrooms(); /// TODO: should we just get the rooms with new messages in them? No.
+           if ($ret[0])
+           {
+                   $fldr = $ret[1];
+                   foreach ($fldr as $folder)
+                   {      // hide contacts and calendar here... TODO: do we realy need to?
+                           if (($folder['name'] != 'Calendar') && ($folder['name'] != 'Contacts'))
+                           {
+                                   $folders[] = array("id"     => $folder['name'], 
+                                                      "parent" => $folder['floor'], 
+                                                      "mod"    => "Inbox");
+                                   
+                           }
+                   }
+                   return $folders;
+           }
+           else return false;
+           
+        
+///        $inbox = array();
+///        $inbox["id"] = "root";
+///        $inbox["parent"] = "0";
+///        $inbox["mod"] = "Inbox";
+///        
+///        $folders[]=$inbox;
+///        
+///        $sub = array();
+///        $sub["id"] = "sub";
+///        $sub["parent"] = "root";
+///        $sub["mod"] = "Sub";
+///        
+/////        $folders[]=$sub;
+///        
+///        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) {
+           debugLog("GetFolder $id");
+           $ret = ctdl_goto ($id);
+//         debugLog(print_r($ret, true));
+           $box = new SyncFolder();
+           $box->serverid = $id;
+           $box->parentid = $ret['floorid'];
+           $box->displayname = $ret['roomname'];
+           switch ($ret['defaultview'])
+           {
+           case VIEW_BBS:
+                   $box->type = SYNC_FOLDER_TYPE_OTHER;
+                   break;
+           case VIEW_MAILBOX:
+                   $box->type = SYNC_FOLDER_TYPE_INBOX;
+                   break;
+           case VIEW_ADDRESSBOOK:
+                   $box->type = SYNC_FOLDER_TYPE_OTHER;
+                   break;
+           case VIEW_CALENDAR:
+                   $box->type = SYNC_FOLDER_TYPE_OTHER;
+                   break;
+           case VIEW_TASKS:
+                   $box->type = SYNC_FOLDER_TYPE_OTHER;
+                   break;
+           case VIEW_NOTES:
+                   $box->type = SYNC_FOLDER_TYPE_OTHER;
+                   break;
+           }
+           return $box;
+//        if($id == "root") {
+//            $inbox = new SyncFolder();
+//            
+//            $inbox->serverid = $id;
+//            $inbox->parentid = "0"; // Root
+//            $inbox->displayname = "Inbox";
+//            $inbox->type = SYNC_FOLDER_TYPE_INBOX;
+//            
+//            return $inbox;
+//        } else if($id = "sub") {
+//            $inbox = new SyncFolder();
+//            $inbox->serverid = $id;
+//            $inbox->parentid = "root";
+//            $inbox->displayname = "Sub";
+//            $inbox->type = SYNC_FOLDER_TYPE_OTHER;
+//            
+//            return $inbox;
+//        } else {
+//            return false;
+//        }
+    }
+    
+    /* 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)
+     */
+    function StatFolder($id) {
+debugLog("Statfolder $id");
+        $folder = $this->GetFolder($id);
+        
+        $stat = array();
+        $stat["id"] = $id;
+        $stat["parent"] = $folder->parentid;
+        $stat["mod"] = $folder->displayname;
+        
+        return $stat;
+    }
+
+    /* 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("GetAttachmentData");
+        list($id, $part) = explode(":", $attname);
+        
+        $fn = $this->findMessage($id);
+        
+        // Parse e-mail
+        $rfc822 = file_get_contents($this->getPath() . "/$fn");
+        
+        $message = Mail_mimeDecode::decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'input' => $rfc822, 'crlf' => "\n", 'charset' => 'utf-8'));
+        return $message->parts[$part]->body;
+    }
+
+    /* StatMessage should return message stats, analogous to the folder stats (StatFolder). Entries are:
+     * '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.
+     */
+     
+    function StatMessage($folderid, $id) {
+           debugLog("StatMessage $folderid $id");
+           return array ("id" => "$id", "flags" => 0, "mod", "12345");
+//
+//        $dirname = $this->getPath();
+//        $fn = $this->findMessage($id);
+//        if(!$fn)
+//            return false;
+//
+//        $stat = stat("$dirname/$fn");
+//
+//        $entry = array();
+//        $entry["id"] = $id;
+//        $entry["flags"] = 0;
+//
+//        if(strpos($fn,"S"))
+//            $entry["flags"] |= 1;
+//        $entry["mod"] = $stat["mtime"];
+//                
+//        return $entry;
+    }
+    
+    /* GetMessage should return the actual SyncXXX object type. You may or may not use the '$folderid' parent folder
+     * identifier here.
+     * Note that mixing item types is illegal and will be blocked by the engine; ie returning an Email object in a 
+     * Tasks folder will not do anything. The SyncXXX objects should be filled with as much information as possible, 
+     * but at least the subject, body, to, from, etc.
+     */
+    function GetMessage($folderid, $id) {
+           debugLog("GetMessge $folderid $id");
+#        if($folderid != 'root')
+#            return false;
+            
+//        $fn = $this->findMessage($id);
+
+        // Get flags, etc
+        $stat = $this->StatMessage($folderid, $id);
+        
+        // Parse e-mail
+        $rfc822 = $this->findMessage($id);
+#file_get_contents($this->getPath() . "/" . $fn);
+        debugLog("-------------------".print_r($rfc822, true));
+       $params = array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true,  'crlf' => "\r\n", 'charset' => 'utf-8');
+       $decoder = new Mail_mimeDecode($rfc822);
+        $message = $decoder->decode();
+
+       debugLog(print_r($message, true));
+        $output = new SyncMail();
+
+        $output->body = str_replace("\n", "\r\n", $this->getBody($message));
+        $output->bodysize = strlen($output->body);
+        $output->bodytruncated = 0;
+        $output->datereceived = $this->parseReceivedDate($message->headers["received"][0]);
+        $output->displayto = $message->headers["to"];
+        $output->importance = $message->headers["x-priority"];
+        $output->messageclass = "IPM.Note";
+        $output->subject = $message->headers["subject"];
+        $output->read = $stat["flags"];
+        $output->to = $message->headers["to"];
+        $output->cc = $message->headers["cc"];
+        $output->from = $message->headers["from"];
+        $output->reply_to = isset($message->headers["reply-to"]) ? $message->headers["reply-to"] : null;
+
+        // Attachments are only searched in the top-level part
+        $n = 0;
+        if(isset($message->parts)) {
+            foreach($message->parts as $part) {
+                if($part->ctype_primary == "application") {
+                    $attachment = new SyncAttachment();
+                    $attachment->attsize = strlen($part->body);
+                    
+                    if(isset($part->d_parameters['filename']))
+                        $attname = $part->d_parameters['filename'];
+                    else if(isset($part->ctype_parameters['name']))
+                        $attname = $part->ctype_parameters['name'];
+                    else if(isset($part->headers['content-description']))
+                        $attname = $part->headers['content-description'];
+                    else $attname = "unknown attachment";
+                    
+                    $attachment->displayname = $attname;
+                    $attachment->attname = $id . ":" . $n;
+                    $attachment->attmethod = 1;
+                    $attachment->attoid = isset($part->headers['content-id']) ? $part->headers['content-id'] : "";
+                    
+                    array_push($output->attachments, $attachment);
+                }
+                $n++;
+            }
+        }
+        
+        return $output;
+    }
+    
+    /* This function is 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 PDA
+     * as it will be seen as a 'new' item. This means that if you don't implement this function, you will
+     * be able to delete messages on the PDA, but as soon as you sync, you'll get the item back
+     */
+    function DeleteMessage($folderid, $id) {
+           debugLog("DeleteMessage");
+        if($folderid != 'root')
+            return false;
+            
+        $fn = $this->findMessage($id);
+
+        if(!$fn)
+            return true; // success because message has been deleted already
+
+        
+        if(!unlink($this->getPath() . "/$fn")) {
+            return true; // success - message may have been deleted in the mean time (since findMessage)
+        }
+
+        return true;
+    }
+    
+    /* This should change 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 PDA will trigger
+     * a full resync of the item from the server
+     */
+    function SetReadFlag($folderid, $id, $flags) {
+           debugLog("SetReadFlag");
+        if($folderid != 'root')
+            return false;
+            
+        $fn = $this->findMessage($id);
+        
+        if(!$fn)
+            return true; // message may have been deleted
+        
+        if(!preg_match("/([^:]+):2,([PRSTDF]*)/",$fn,$matches))
+            return false;
+
+        // remove 'seen' (S) flag            
+        if(!$flags) {
+            $newflags = str_replace("S","",$matches[2]);
+        } else {
+            // make sure we don't double add the 'S' flag
+            $newflags = str_replace("S","",$matches[2]) . "S";
+        }
+        
+        $newfn = $matches[1] . ":2," . $newflags;
+        // rename if required
+        if($fn != $newfn) 
+            rename($this->getPath() ."/$fn", $this->getPath() . "/$newfn");
+        
+        return true;
+    }
+    
+    /* This function is called when a message has been changed on the PDA. You should parse the new
+     * message here and save the changes to disk. The return value must be whatever would be returned
+     * from StatMessage() after the message has been saved. This means that both the 'flags' and the 'mod'
+     * properties of the StatMessage() item may change via ChangeMessage().
+     * Note that this function will never be called on E-mail items as you can't change e-mail items, you
+     * can only set them as 'read'.
+     */
+    function ChangeMessage($folderid, $id, $message) {
+           debugLog("ChangeMessage");
+        return false;
+    }
+    
+    /* 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
+     */
+    function MoveMessage($folderid, $id, $newfolderid) {
+           debugLog("MoveMessage");
+        return false;
+    }
+
+    // ----------------------------------------
+    // maildir-specific internals
+    
+    function findMessage($id) {
+           debugLog("findMessage $id");
+        // We could use 'this->_folderid' for path info but we currently
+        // only support a single INBOX. We also have to use a glob '*'
+        // because we don't know the flags of the message we're looking for.
+
+           $msg = ctdl_fetch_message_rfc822($id);
+           if ($msg[0])
+                   return $msg[1];
+           else
+                   return false;
+//        $dirname = $this->getPath();
+//        $dir = opendir($dirname);
+//        
+//        while($entry = readdir($dir)) {
+//            if(strpos($entry,$id) === 0)
+//                return $entry;
+//        }
+//        return false; // not found
+    }
+    
+    /* Parse the message and return only the plaintext body
+     */
+    function getBody($message) {
+           debugLog("getBody -> $message <-");
+        $body = "";
+        $htmlbody = "";
+        
+        $this->getBodyRecursive($message, "plain", $body);
+        
+        if(!isset($body) || $body === "") {
+            $this->getBodyRecursive($message, "html", $body);
+            // HTML conversion goes here
+        }
+        
+        return $body;
+    }
+    
+    // Get all parts in the message with specified type and concatenate them together, unless the
+    // Content-Disposition is 'attachment', in which case the text is apparently an attachment
+    function getBodyRecursive($message, $subtype, &$body) {
+           debugLog("GetBodyRecursive $subtype".print_r($message, true));
+        if(strcasecmp($message->ctype_primary,"text")==0 && strcasecmp($message->ctype_secondary,$subtype)==0 && isset($message->body))
+            $body .= $message->body;
+        
+        if(strcasecmp($message->ctype_primary,"multipart")==0) {
+            foreach($message->parts as $part) {
+                if(!isset($part->disposition) || strcasecmp($part->disposition,"attachment"))  {
+                    $this->getBodyRecursive($part, $subtype, $body);
+                }
+            }
+        }
+    }
+
+    function parseReceivedDate($received) {
+           debugLog("parseRecivedDate");
+           $pos = strpos($received, ";");
+        if(!$pos)
+            return false;
+            
+        $datestr = substr($received, $pos+1);
+        $datestr = ltrim($datestr);
+        
+        return strtotime($datestr);
+    }
+    
+    /* moves everything in Maildir/new/* to Maildir/cur/
+     */
+    function moveNewToCur() {
+           debugLog("moveNewToCur");
+        $newdirname = MAILDIR_BASE . "/" . $this->_user . "/" . MAILDIR_SUBDIR . "/new";
+        
+        $newdir = opendir($newdirname);
+        
+        while($newentry = readdir($newdir)) {
+            if($newentry{0} == ".")
+                continue;
+                
+            // link/unlink == move. This is the way to move the message according to cr.yp.to
+            link($newdirname . "/" . $newentry, $this->getPath() . "/" . $newentry . ":2,");
+            unlink($newdirname . "/" . $newentry);
+        }
+    }
+    
+    /* The path we're working on
+     */
+    function getPath() {
+           debugLog("GetPath");
+        return MAILDIR_BASE . "/" . $this->_user . "/" . MAILDIR_SUBDIR . "/cur";
+    }
+};
+
+
+?>
\ No newline at end of file