[dev] Kolab Webclient: Kolab:: library
Stuart Bingë
s.binge at codefusion.co.za
Wed Jan 28 05:55:57 PST 2004
On Wednesday 28 January 2004 12:26, Jan Schneider wrote:
> Simple use String::convertCharset($text, $from) if you wan't to convert
> *to* the user's charset and String::convertCharset($text,
> NLS::getCharset(), $to) to convert *from* the user's charset.
Thanks for the help, Jan. Here's an updated version of the library that
implements some of the changes you suggested, namely the iconv to String::
and MIME:: conversion, and the conversion from Net_HTTP_Client to
HTTP_Request and WebDAV_Client.
Cheers,
--
Stuart Bingë
Code Fusion cc. <http://www.codefusion.co.za/>
Tel: +27 11 391 1412
Mobile: +27 83 298 9727
Email: s.binge at codefusion.co.za
-------------- next part --------------
<?php
/**
* The Kolab:: utility library provides various functions for dealing with a
* Kolab server (i.e. functions that relate to Cyrus IMAP, WebDAV, etc.).
*
* Copyright 2003 Code Fusion, cc.
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* Written by Stuart Bingë <s.binge at codefusion.co.za>
*
*
* CHANGELOG:
*
* 2004-01-28 <s.binge at codefusion.co.za>
* - Replaced the various iconv() functions with Horde counterparts from
* the MIME:: and String:: libraries.
* - Replaced the Net_HTTP_Client code with its equivalent that uses
* HTTP_Request for GETs, and PHP streams with the WebDAV_Client wrapper
* for WebDAV PUTs.
* - Fixed a small bug in openCyrusConnection() when false was being
* returned (instead of raising Horde::fatal) on a certain error condition.
*
* 2004-01-28 <s.binge at codefusion.co.za>
* - Initial release to the Horde project.
*/
require_once HORDE_LIBS . 'HTTP/WebDAV/Client.php';
require_once HORDE_LIBS . 'HTTP/Request.php';
require_once HORDE_LIBS . 'Horde/iCalendar.php';
require_once HORDE_LIBS . 'Horde/String.php';
require_once HORDE_LIBS . 'Horde/MIME.php';
require_once HORDE_BASE . '/lib/Horde.php';
require_once HORDE_BASE . '/lib/NLS.php';
/**
* The 'newline' character sequence used by Cyrus IMAP.
*/
define('CYRUS_NL', "\r\n");
/**
* The Does Not Exist error message returned by the imap_last_error()
* function if a specified mailbox does not exist on the IMAP server.
*/
define('ERR_MBOX_DNE', 'Mailbox does not exist');
/**
* The name of an X-Header used in various messages to provide category
* information for the relevant object (note, task, etc).
*/
define('X_HEAD_CAT', 'X-Horde-Category');
class Kolab {
/* ---------- GENERAL FUNCTIONS ---------- */
/**
* Convertes any newlines in the specified text to cyrus format.
*
* @access public
*
* @param string $text The text to convert.
*
* @return string $text with all newlines replaced by CRNL.
*/
function cyrusNewlines($text)
{
return preg_replace("/\r\n|\n|\r/s", "\r\n", $text);
}
/**
* Convertes any newlines in the specified text to unix format.
*
* @access public
*
* @param string $text The text to convert.
*
* @return string $text with all newlines replaced by NL.
*/
function unixNewlines($text)
{
return preg_replace("/\r\n|\n|\r/s", "\n", $text);
}
/* ---------- GENERAL KOLAB FUNCTIONS ---------- */
/**
* Strips any superfluos domain suffix from an email address. This was
* done as Kolab uses 'name at maildomain' user names, whereas horde was
* returning 'name at maildomain@hostname' addresses for the username.
*
* @access public
*
* @param string $address The address to strip the domain from, of the
* form 'a at b@c' where any/all of '@b' and '@c'
* may be missing.
*
* @return string A string of the form 'a at b'.
*/
function stripKolabUsername($address)
{
return preg_replace('/^([^@]*(@[^@]*)?).*$/', "\$1", $address);
}
/**
* Strips any superfluos domain suffix from an email address.
*
* @access public
*
* @param string $address The address to strip the domain from, of the
* form 'a at b@c' where any/all of '@b' and '@c'
* may be missing.
*
* @return string A string of the form 'a'.
*/
function stripBaseUsername($address)
{
return preg_replace('/^([^@]*).*$/', "\$1", $address);
}
/**
* Returns the username of the currently logged on Horde user, suitable
* for use in other Kolab authentication procedures (assuming Horde is
* using LDAP authentication against the Kolab server).
*
* @access public
*
* @return string The current users login name.
*/
function getUser()
{
return Kolab::stripKolabUsername(Auth::getAuth());
}
/**
* Returns the password of the currently logged on Horde user, suitable
* for use in other Kolab authentication procedures (assuming Horde is
* using LDAP authentication against the Kolab server).
*
* @access public
*
* @return string The current users login password.
*/
function getPassword()
{
return Auth::getCredential('password');
}
/**
* Returns the username and password of the currently logged on Horde user,
* suitable for use in other Kolab authentication procedures (assuming
* Horde is using LDAP authentication against the Kolab server).
*
* @access public
*
* @return array An array of the form (username, password).
*/
function getAuthentication()
{
return array(Kolab::getUser(), Kolab::getPassword());
}
/* ---------- CYRUS/IMAP FUNCTIONS ---------- */
/**
* Returns an IMAP server address formatted for use in the PHP IMAP
* functions.
*
* @access public
*
* @param string $host The address of the IMAP server, of the form
* 'host:port'.
*
* @return string An address string suitable for use in functions such as
* imap_open().
*/
function imapServerURI($host)
{
return '{' . $host . '/imap/notls}';
}
/**
* Returns an address of a Cyrus mailbox, formatted for use in the PHP IMAP
* functions.
*
* @access public
*
* @param string $host The address of the Cyrus server.
* @param optional string $mailbox The name of the mailbox (defaults to
* the Inbox).
*
* @return string A mailbox address string suitable for use in functions
* such as imap_open().
*/
function cyrusMailboxURI($host, $mailbox = '')
{
if (!empty($mailbox)) $mailbox = '/' . imap_utf7_encode($mailbox);
return Kolab::imapServerURI($host) . "INBOX$mailbox";
}
/**
* Tests if $error was the last IMAP error that was generated.
*
* @access public
*
* @param string $error The error to test against.
*
* @return boolean True if $error was the last IMAP error.
*/
function testIMAPError($error)
{
return strcasecmp(imap_last_error(), $error) == 0;
}
/**
* Returns an IMAP stream, connected to a specified Cyrus server, with a
* specified mailbox opened (optionally creating the mailbox if it does
* not exist). Raises a fatal Horde error if something goes wrong.
*
* @access public
*
* @param string $server The server to open the IMAP connection
* to.
* @param string $mailbox The mailbox to open on $server.
* @param optional boolean $create True if $mailbox is to be created if it
* does not exist.
*
* @return resource An open IMAP stream on success.
*/
function openCyrusConnection($server, $mailbox, $create = true)
{
list($user, $pass) = Kolab::getAuthentication();
$box = Kolab::cyrusMailboxURI($server, $mailbox);
$imapstream = @imap_open($box, $user, $pass); // Firstly try a straight imap_open()
if ($create && Kolab::testIMAPError(ERR_MBOX_DNE)) { // Box does not exist, try to create it
if ($imapstream !== false) @imap_close($imapstream);
$imapstream = @imap_open(Kolab::imapServerURI($server), $user, $pass, OP_HALFOPEN);
if ($imapstream === false)
Horde::fatal(
PEAR::raiseError(sprintf(_('Unable to open mailbox %s: ' . imap_last_error()), $box)),
__FILE__,
__LINE__
);
if (!@imap_createmailbox($imapstream, $box))
Horde::fatal(
PEAR::raiseError(sprintf(_('Unable to create mailbox %s: ' . imap_last_error()), $box)),
__FILE__,
__LINE__
);
$imapstream = @imap_reopen($box); // Successfully created the box, now try to open it
}
if ($imapstream === false)
Horde::fatal(
PEAR::raiseError(sprintf(_('Unable to open mailbox %s: ' . imap_last_error()), $box)),
__FILE__,
__LINE__
);
return $imapstream;
}
/**
* Closes a specified IMAP stream, expunging all the messages flagged for
* deletion.
*
* @access public
*
* @param resource $imapstream A reference to an open IMAP stream,
* connected to the mailbox of interest.
* This is set to NULL if the connection
* was terminated successfully.
* @param optional boolean $expunge True to expunge the mailbox before
* closing.
*
* @return boolean True if the stream was closed successfully.
*/
function closeImapConnection(&$imapstream, $expunge = true)
{
$result = true;
if (isset($imapstream)) {
$result = @imap_close($imapstream, ($expunge ? CL_EXPUNGE : 0));
if ($result) $imapstream = NULL;
}
return $result;
}
/**
* Ensures that the specified IMAP connection is alive - reconnects
* if the stream has been disconnected.
*
* @access public
*
* @param resource $imapstream A reference to an open IMAP stream,
* connected to the mailbox of interest.
* @param string $server The server to open the IMAP connection
* to.
* @param string $mailbox The mailbox to open on $server.
* @param optional boolean $create True if $mailbox is to be created if it
* does not exist.
*
*/
function persistentCyrusConnection(&$imapstream, $server, $mailbox, $create = true)
{
if (!isset($imapstream) || !imap_ping($imapstream)) {
if (isset($imapstream)) Kolab::closeImapConnection($imapstream);
$imapstream = Kolab::openCyrusConnection($server, $mailbox, $create);
}
}
/**
* Returns a hash of the message headers of a specified message.
*
* @access public
*
* @param resource $imapstream An open IMAP stream, connected to the
* mailbox of interest.
* @param integer $messageid The message from which to read the headers.
*
* @return array A hash of the headers, where each 'key => value' pair in
* the hash corresponds to a 'Name: Value' header line. This
* array is empty if an error occurs.
*/
function getMessageHeaders($imapstream, $messageid)
{
$headers = array();
$headerlines = explode(CYRUS_NL, @imap_fetchheader($imapstream, $messageid, FT_UID));
foreach ($headerlines as $headerline) {
if (empty($headerline)) continue;
list($hname, $hval) = explode(':', MIME::decode($headerline), 2);
$headers[trim($hname)] = trim($hval);
}
return $headers;
}
/**
* Returns the value of a header attribute in a hash returned by
* Kolab::getHeaderHash(), or a specified default value if the header
* attribute does not exist.
*
* @access public
*
* @param array $headers The message header hash.
* @param string $name The attribute to search for.
* @param optional mixed $default The value to return if $name does not
* exist in $headers.
*
* @return mixed The value of $default.
*/
function getHeaderValue(&$headers, $name, $default = '')
{
return array_key_exists($name, $headers) ? $headers[$name] : $default;
}
/**
* Returns the body of a specified mail message.
*
* @access public
*
* @param resource $imapstream An open IMAP stream, connected to the
* mailbox of interest.
* @param integer $messageid The message to read.
* @param optional boolean $convert True to convert the body from UTF-8 to
* the current users character set.
*
* @return mixed (string) The body of mail message $messageid.
* (boolean) False on error.
*/
function getMessageBody($imapstream, $messageid, $convert = false)
{
$body = @imap_body($imapstream, $messageid, FT_UID);
return ($convert ? String::convertCharset($body, 'UTF-8') : $body);
}
/**
* Returns a list of messages in the specified mailbox, sorted by date.
*
* @access public
*
* @param resource $imapstream An open IMAP stream, connected to the
* mailbox of interest.
*
* @return array A list of the messages in the mailbox opened on
* $imapstream, sorted by date.
*/
function getMessages($imapstream)
{
return @imap_sort($imapstream, SORTDATE, 0, SE_UID);
}
/**
* Finds a set of messages in the specified mailbox that match the specified
* criteria.
*
* @access public
*
* @param resource $imapstream An open IMAP stream, connected to the
* mailbox of interest.
* @param string $criteria The search criteria (see imap_search() for
* the format of this string).
*
* @return mixed (array) A list of the messages in the mailbox opened on
* $imapstream that match $criteria.
* (boolean) False on failure.
*/
function findMessages($imapstream, $criteria)
{
return @imap_search($imapstream, $criteria, SE_UID);
}
/**
* Adds a message to the specified mailbox.
*
* @access public
*
* @param resource $imapstream An open IMAP stream, connected to the
* mailbox of interest.
* @param string $mailbox The cyrusMailboxURI() formatted name of the
* mailbox to add the message to.
* @param string $conttype The mime content type of the message.
* @param string $subject The message subject.
* @param string $body The message body. Newlines are automatically
* converted to the corrent type.
* @param optional string $ua The user agent which is adding the message.
* @param optional array $headers A hash containing any extra headers to
* append. Each 'key => value' pair is written
* as 'key: value' in the message header.
* @param optional boolean $convert True to convert the message body to
* UTF-8 before adding the message.
*
* @return mixed (boolean) True on success.
* (object) PEAR_Error on failure.
*/
function addMessage($imapstream, $mailbox, $conttype, $subject, $body,
$ua = '', $headers = array(), $convert = false)
{
$user = MIME::encodeAddress(Kolab::getUser());
$msg = 'Content-Type: ' . MIME::encode($conttype . ($convert ? '; Charset="UTF-8"' : '')) . CYRUS_NL;
$msg .= 'From: ' . $user . CYRUS_NL;
$msg .= 'Reply-To: ' . '' . CYRUS_NL;
$msg .= 'To: ' . $user . CYRUS_NL;
$msg .= 'User-Agent: ' . MIME::encode('Horde' . (empty($ua) ? '' : '/' . $ua) . '/Kolab') . CYRUS_NL;
$msg .= 'Date: ' . date('r') . CYRUS_NL;
$msg .= 'Subject: ' . MIME::encode($subject) . CYRUS_NL;
foreach ($headers as $key => $value)
$msg .= $key . ': ' . MIME::encode($value) . CYRUS_NL;
if ($convert) {
$body = String::convertCharset($body, NLS::getCharset(), 'UTF-8');
}
$msg .= CYRUS_NL . Kolab::cyrusNewlines($body);
if (!@imap_append($imapstream, $mailbox, $msg))
return PEAR::raiseError(
sprintf(
_('Unable to add message from %s to mailbox %s: ' . imap_last_error()),
$user,
$mailbox
)
);
return true;
}
/**
* Deletes a message in the specified mailbox.
*
* @access public
*
* @param resource $imapstream An open IMAP stream, connected to the
* mailbox of interest.
* @param integer $messageid The message to delete.
* @param optional boolean $expunge True to expunge the mailbox after
* deletion.
*/
function deleteMessage($imapstream, $messageid, $expunge = false)
{
@imap_delete($imapstream, $messageid, FT_UID);
if ($expunge) @imap_expunge($imapstream);
}
/* ---------- WEBDAV FUNCTIONS ---------- */
/**
* Retrieves the contents of a specified users VFB file, stored on a
* specified WebDAV server.
*
* @access public
*
* @param string $server The address of the WebDAV server, of the
* form host:port.
* @param string $folder The folder on $server where the VFB file is
* stored.
* @param optional string $user The name of the user whose VFB file is to
* be retrieved. Defaults to the current user.
*
* @return mixed (string) The contents of the users VFB file, suitable for
* parsing by a Horde_iCalendar object.
* (object) PEAR_Error on failure.
*/
function retrieveFreeBusy($server, $folder, $user = '')
{
list($uname, $pass) = Kolab::getAuthentication();
if (empty($user)) $user = $uname;
$http = &new HTTP_Request(
"http://$server/$folder/$user.vfb",
array(
'user' => $uname,
'pass' => $pass
)
);
$result = $http->sendRequest();
if ($result !== true) return $result;
$status = $http->getResponseCode();
if ($status != 200) {
// Try `user' instead of `user at domain', for backward compatibility
$http->reset(
"http://$server/$folder/" . Kolab::stripBaseUsername($user) . ".vfb",
array(
'user' => $uname,
'pass' => $pass
)
);
$status = $http->getResponseCode();
if ($status != 200)
return PEAR::raiseError(sprintf(
_('Unable to retrieve free/busy information for user %s on server %s'),
$user,
$server
));
}
return $http->getResponseBody();
}
/**
* Stores the specified VFB data in the current users VFB file on the
* specified WebDAV server.
*
* @access public
*
* @param string $server The address of the WebDAV server, of the form
* host:port.
* @param string $folder The folder on $server where the VFB file is stored.
* @param string $vfb The new VFB data to store.
*
* @return mixed (boolean) True on success.
* (object) PEAR_Error on failure.
*/
function storeFreeBusy($server, $folder, $vfb)
{
list($user, $pass) = Kolab::getAuthentication();
$path = 'webdav://' . urlencode($user) . ':' . urlencode($pass) . "@$server/$folder/$user.vfb";
if (!$fh = fopen($path, 'w'))
return PEAR::raiseError(sprintf(
_('Unable to store free/busy information for user %s on server %s'),
$user,
$server
));
if (!fwrite($fh, $vfb)) {
fclose($fh);
return PEAR::raiseError(sprintf(
_('Unable to store free/busy information for user %s on server %s'),
$user,
$server
));
}
fclose($fh);
return true;
}
/**
* Retrieves the value of a specified attribute in a Horde_iCalendar object,
* or a default value if the attribute does not exist.
*
* @access private
*
* @param Horde_iCalendar* $component The component of interest.
* @param string $attribute The attribute of interest.
* @param mixed $default The value to return if $attribute
* does not exist.
*
* @return mixed (string) The value of $attribute.
* (mixed) $default if $attribute does not exist.
*/
function _getAttr(&$component, $attribute, $default = '')
{
$tmp = $component->getAttribute($attribute);
if (is_a($tmp, 'PEAR_Error')) return $default;
return $tmp;
}
/**
* Compiles and uploads a free/busy VFB file for the current user. The busy
* times are obtained from reading iCalendar events in the users calendar
* folder. By default the VFB file spans 8 weeks, starting at the time when
* the function is called.
*
* @access private
*
* @param optional string $calserver The server address where the calendar
* folder is located.
* @param optional string $calfolder The name of the calendar folder.
* @param optional string $vfbserver The server address where the free/busy
* folder is located.
* @param optional string $vfbfolder The name of the free/busy folder.
* @param optional integer $period The period (in seconds) from which
* busy information should be drawn from.
* This period begins when the function
* is called.
*
* @return mixed (boolean) True on success.
* (object) PEAR_Error on failure.
*/
function compileFreeBusy($calserver = 'localhost', $calfolder = 'Calendar',
$vfbserver = 'localhost', $vfbfolder = '/freebusy/',
$period = 4838400)
{
$imap = Kolab::openCyrusConnection($calserver, $calfolder);
$vfbcont = new Horde_iCalendar;
$vfb =& $vfbcont->newComponent('VFREEBUSY', $vfbcont);
$cal = new Horde_iCalendar;
$cal->setAttribute('ORGANIZER', 'MAILTO:' . Kolab::getUser());
$vfbstart = time();
$vfbend = $vfbstart + $period;
$vfb->setAttribute('DTSTART', $vfbstart);
$vfb->setAttribute('DTEND', $vfbend);
$msgs = Kolab::getMessages($imap);
foreach ($msgs as $msg) {
$cal->parsevCalendar(Kolab::getMessageBody($imap, $msg));
$components = $cal->getComponents();
$ispresent = false;
foreach ($components as $c)
{
if ($ispresent) break;
if (!is_a($c, 'Horde_iCalendar_vevent')) continue;
$ispresent = true;
$start = Kolab::_getAttr($c, 'DTSTART', 0);
$end = Kolab::_getAttr($c, 'DTEND', 0);
if ($start > $vfbend || $end < $vfbstart) continue;
$start = max($start, $vfbstart);
$end = min($end, $vfbend);
$vfb->addBusyPeriod('BUSY', $start, $end);
}
}
Kolab::closeImapConnection($imap);
$vfb->simplify();
$vfbcont->addComponent($vfb);
$vfbfile = $vfbcont->exportvCalendar();
return Kolab::storeFreeBusy($vfbserver, $vfbfolder, $vfbfile);
}
}
More information about the dev
mailing list