From: Wilfried Göesgens Date: Sun, 27 Jan 2008 17:27:32 +0000 (+0000) Subject: * support dele command X-Git-Tag: v7.86~2550 X-Git-Url: https://code.citadel.org/?p=citadel.git;a=commitdiff_plain;h=ff30a327244aec2c9c77913e494b24fb5b04503c * support dele command * z-push plugin * sample edit config command --- diff --git a/ctdlphp/ctdlelements.php b/ctdlphp/ctdlelements.php index a0d422f46..e03b07832 100644 --- a/ctdlphp/ctdlelements.php +++ b/ctdlphp/ctdlelements.php @@ -54,7 +54,10 @@ function display_message($msgnum) { } // Do message text - echo $fields["text"] . "
"; + if ($fields['formatet_text'] != "") + echo $fields['formatet_text'] . "
"; + else + echo $fields["text"] . "
"; echo '
' ; } diff --git a/ctdlphp/ctdlprotocol.php b/ctdlphp/ctdlprotocol.php index b08ed6a3a..6f434822a 100644 --- a/ctdlphp/ctdlprotocol.php +++ b/ctdlphp/ctdlprotocol.php @@ -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 = "
\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("
\n

Message Body Follows

", 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 ("
", 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 .= "" . htmlspecialchars($buf) . "
\n" ; + $txt .= "\r\n".$buf; + $modified_ .= "" . htmlspecialchars($buf) . "
\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)); } diff --git a/ctdlphp/ctdlsession.php b/ctdlphp/ctdlsession.php index a47ef2b01..c9614c753 100644 --- a/ctdlphp/ctdlsession.php +++ b/ctdlphp/ctdlsession.php @@ -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 ; diff --git a/ctdlphp/myinfo.php b/ctdlphp/myinfo.php index a58c150a0..0328da71b 100644 --- a/ctdlphp/myinfo.php +++ b/ctdlphp/myinfo.php @@ -19,18 +19,36 @@ list($num_msgs, $response, $msgs) = ctdl_msgs("", ""); echo "num_msgs: " . $num_msgs . "
\n" ; echo "response: " . htmlspecialchars($response) . "
\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) . "
" ; + 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 "\n
\n".$vcard."
"; +echo "\n---------------------------------------
\n". $vcard."
"; echo "putting it back in!"; @@ -53,10 +71,25 @@ end:vcard "; preg_replace('/Sir/', 'Mrs',$vcard); echo "Now its:
\n".$vcard."
"; -$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']); +} ?>
Sample links
diff --git a/ctdlphp/z-push/citadel.php b/ctdlphp/z-push/citadel.php new file mode 100644 index 000000000..00a9ff776 --- /dev/null +++ b/ctdlphp/z-push/citadel.php @@ -0,0 +1,597 @@ +_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