Fix encoding issues; improvements for Funambol clients

This commit is contained in:
Jörg Lehrke 2010-01-08 13:09:36 +00:00
parent 058a820eb7
commit 49823ecd7e
5 changed files with 209 additions and 103 deletions

View File

@ -225,7 +225,7 @@ class addressbook_vcal extends addressbook_bo
}
foreach ($databaseFields as $databaseField)
{
$value = "";
$value = '';
if (!empty($databaseField))
{
@ -282,21 +282,44 @@ class addressbook_vcal extends addressbook_bo
{
$values = (array) $GLOBALS['egw']->translation->convert($values, $sysCharSet, $_charset);
$value = implode(',', $values); // just for the CHARSET recognition
if ($extra_charset_attribute && preg_match('/([\177-\377])/', $value))
if (($size > 0) && strlen($value) > $size)
{
$options['CHARSET'] = $_charset;
// let us try with only the first category
$value = $values[0];
if (strlen($value) > $size)
{
error_log(__FILE__ . __LINE__ . __METHOD__ . " vCalAddressbook $vcardField omitted due to maximum size $size");
// Horde::logMessage("vCalAddressbook $vcardField omitted due to maximum size $size",
// __FILE__, __LINE__, PEAR_LOG_WARNING);
continue;
}
$values = array();
}
if (preg_match('/[^\x20-\x7F]/', $value))
{
if ($extra_charset_attribute || $this->productName == 'kde')
{
$options['CHARSET'] = $_charset;
}
// KAddressbook requires non-ascii chars to be qprint encoded, other clients eg. nokia phones have trouble with that
if ($this->productName == 'kde' ||
($this->productManufacturer == 'funambol' && $this->productName == 'blackberry plug-in'))
if ($this->productName == 'kde')
{
$options['ENCODING'] = 'QUOTED-PRINTABLE';
}
else
elseif ($this->productManufacturer == 'funambol')
{
$options['ENCODING'] = 'FUNAMBOL-QP';
}
elseif (preg_match('/([\000-\012\015\016\020-\037\075])/', $value))
{
$options['ENCODING'] = 'QUOTED-PRINTABLE';
}
elseif (!$extra_charset_attribute)
{
$options['ENCODING'] = '';
}
}
$hasdata++;
$hasdata++;
}
break;
@ -331,33 +354,29 @@ class addressbook_vcal extends addressbook_bo
{
$value = $GLOBALS['egw']->translation->convert(trim($value), $sysCharSet, $_charset);
$values[] = $value;
if ($extra_charset_attribute)
if (preg_match('/[^\x20-\x7F]/', $value))
{
if (preg_match('/([\177-\377])/', $value))
if ($extra_charset_attribute || $this->productName == 'kde')
{
$options['CHARSET'] = $_charset;
// KAddressbook requires non-ascii chars to be qprint encoded, other clients eg. nokia phones have trouble with that
if ($this->productName == 'kde' ||
($this->productManufacturer == 'funambol' && $this->productName == 'blackberry plug-in'))
{
$options['ENCODING'] = 'QUOTED-PRINTABLE';
}
else
{
$options['ENCODING'] = '';
}
}
// protect the CardDAV
if (preg_match('/([\000-\012\015\016\020-\037\075])/', $value))
// KAddressbook requires non-ascii chars to be qprint encoded, other clients eg. nokia phones have trouble with that
if ($this->productName == 'kde')
{
$options['ENCODING'] = 'QUOTED-PRINTABLE';
}
}
else
{
// avoid that these options are inserted from horde code
$options['CHARSET'] = '';
$options['ENCODING'] = '';
elseif ($this->productManufacturer == 'funambol')
{
$options['ENCODING'] = 'FUNAMBOL-QP';
}
elseif (preg_match('/([\000-\012\015\016\020-\037\075])/', $value))
{
$options['ENCODING'] = 'QUOTED-PRINTABLE';
}
elseif (!$extra_charset_attribute)
{
$options['ENCODING'] = '';
}
}
if ($vcardField == 'TEL' && $entry['tel_prefer'] &&
($databaseField == $entry['tel_prefer']))

View File

@ -513,10 +513,16 @@ class calendar_ical extends calendar_boupdate
break;
case 'CATEGORIES':
if ($event['category'])
if ($event['category'] && ($values['CATEGORIES'] = $this->get_categories($event['category'])))
{
$attributes['CATEGORIES'] = '';
$values['CATEGORIES'] = $this->get_categories($event['category']);
if (count($values['CATEGORIES']) == 1)
{
$attributes['CATEGORIES'] = array_shift($values['CATEGORIES']);
}
else
{
$attributes['CATEGORIES'] = '';
}
}
break;
@ -698,12 +704,37 @@ class calendar_ical extends calendar_boupdate
$vevent->setAttribute($key, $valueData, $paramData, true, $valuesData);
$options = array();
if ($paramData['CN']) $valueData .= $paramData['CN']; // attendees or organizer CN can contain utf-8 content
/*if($key != 'RRULE' && preg_match('/([\000-\012\015\016\020-\037\075])/',$valueData)) {
$options['ENCODING'] = 'QUOTED-PRINTABLE';
}*/
if ($this->productManufacturer != 'groupdav' && preg_match('/([\177-\377])/', $valueData))
if (preg_match('/[^\x20-\x7F]/', $valueData))
{
$options['CHARSET'] = 'UTF-8';
switch ($this->productManufacturer)
{
case 'groupdav':
if ($this->productName == 'kde')
{
$options['CHARSET'] = 'UTF-8';
$options['ENCODING'] = 'QUOTED-PRINTABLE';
}
else
{
$options['CHARSET'] = '';
if (preg_match('/([\000-\012\015\016\020-\037\075])/', $valueData))
{
$options['ENCODING'] = 'QUOTED-PRINTABLE';
}
else
{
$options['ENCODING'] = '';
}
}
break;
case 'funambol':
$options['ENCODING'] = 'FUNAMBOL-QP';
default:
// force UTF-8
$options['CHARSET'] = 'UTF-8';
}
}
if (preg_match('/([\000-\012])/', $valueData))
{
@ -764,13 +795,13 @@ class calendar_ical extends calendar_boupdate
{
if ($this->log) error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n".array2string($_vcalData)."\n",3,$this->logfile);
if (!is_array($this->supportedFields)) $this->setSupportedFields();
if (!($events = $this->icaltoegw($_vcalData,$cal_id,$etag,$recur_date)))
{
return false;
}
if (!is_array($this->supportedFields)) $this->setSupportedFields();
// check if we are importing an event series with exceptions in CalDAV
// only first event / series master get's cal_id from URL
// other events are exceptions and need to be checked if they are new
@ -784,6 +815,7 @@ class calendar_ical extends calendar_boupdate
}
foreach ($events as $event)
{
if ($this->log) error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n".array2string($event)."\n",3,$this->logfile);
$updated_id = false;
$event_info = $this->get_event_info($event);
@ -1275,6 +1307,12 @@ class calendar_ical extends calendar_boupdate
'etag' => 'etag',
);
$defaultFields['funambol'] = $defaultFields['basic'] + array(
'category' => 'category',
'non_blocking' => 'non_blocking',
'recurrence' => 'recurrence',
);
$defaultFields['evolution'] = $defaultFields['basic'] + array(
'participants' => 'participants',
'owner' => 'owner',
@ -1402,7 +1440,7 @@ class calendar_ical extends calendar_boupdate
break;
case 'funambol':
$this->supportedFields = $defaultFields['synthesis'];
$this->supportedFields = $defaultFields['funambol'];
break;
// the fallback for SyncML
@ -1430,6 +1468,7 @@ class calendar_ical extends calendar_boupdate
$vcal = new Horde_iCalendar;
if (!$vcal->parsevCalendar($_vcalData))
{
if ($this->log) error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."(): No vCalendar Container found!\n",3,$this->logfile);
if ($this->tzid)
{
date_default_timezone_set($GLOBALS['egw_info']['server']['server_timezone']);
@ -2047,7 +2086,6 @@ class calendar_ical extends calendar_boupdate
// fall through
case 'LAST-MODIFIED': // will be written direct to the event
$event['modified'] = $attributes['value'];
break;
}
}
@ -2127,6 +2165,7 @@ class calendar_ical extends calendar_boupdate
}
if ($this->calendarOwner) $event['owner'] = $this->calendarOwner;
if ($this->log) error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n".array2string($event)."\n",3,$this->logfile);
//Horde::logMessage("vevent2egw:\n" . print_r($event, true),
// __FILE__, __LINE__, PEAR_LOG_DEBUG);
return $event;

View File

@ -169,20 +169,40 @@ class infolog_ical extends infolog_bo
if ($field == 'RELATED-TO')
{
$options = array('RELTYPE' => 'PARENT');
$options = array('RELTYPE' => 'PARENT',
'CHARSET' => 'UTF-8');
}
else
{
$options = array();
$options = array('CHARSET' => 'UTF-8');
}
/*if(preg_match('/([\000-\012\015\016\020-\037\075])/', $value)) {
$options['ENCODING'] = 'QUOTED-PRINTABLE';
}*/
if ($this->productManufacturer != 'groupdav'
&& preg_match('/([\177-\377])/',$value))
if (preg_match('/[^\x20-\x7F]/', $value))
{
$options['CHARSET'] = 'UTF-8';
switch ($this->productManufacturer)
{
case 'groupdav':
if ($this->productName == 'kde')
{
$options['ENCODING'] = 'QUOTED-PRINTABLE';
}
else
{
$options['CHARSET'] = '';
if (preg_match('/([\000-\012\015\016\020-\037\075])/', $value))
{
$options['ENCODING'] = 'QUOTED-PRINTABLE';
}
else
{
$options['ENCODING'] = '';
}
}
break;
case 'funambol':
$options['ENCODING'] = 'FUNAMBOL-QP';
}
}
$vevent->setAttribute($field, $value, $options);
}
@ -462,19 +482,48 @@ class infolog_ical extends infolog_bo
break;
case 'text/x-vnote':
if (!empty($note['info_cat']))
{
$cats = $this->get_categories(array($note['info_cat']));
$note['info_cat'] = $GLOBALS['egw']->translation->convert($cats[0],
$GLOBALS['egw']->translation->charset(), 'UTF-8');
}
$vnote = new Horde_iCalendar_vnote();
$options = array('CHARSET' => 'UTF-8');
$vNote->setAttribute('VERSION', '1.1');
foreach (array( 'SUMMARY' => $note['info_subject'],
'BODY' => $note['info_des'],
foreach (array( 'SUMMARY' => $note['info_subject'],
'BODY' => $note['info_des'],
'CATEGORIES' => $note['info_cat'],
) as $field => $value)
{
$vnote->setAttribute($field, $value);
if ($this->productManufacturer != 'groupdav'
&& preg_match('/([\177-\377])/', $value))
$options = array('CHARSET' => 'UTF-8');
if (preg_match('/[^\x20-\x7F]/', $value))
{
$vevent->setParameter($field, $options);
switch ($this->productManufacturer)
{
case 'groupdav':
if ($this->productName == 'kde')
{
$options['ENCODING'] = 'QUOTED-PRINTABLE';
}
else
{
$options['CHARSET'] = '';
if (preg_match('/([\000-\012\015\016\020-\037\075])/', $value))
{
$options['ENCODING'] = 'QUOTED-PRINTABLE';
}
else
{
$options['ENCODING'] = '';
}
}
break;
case 'funambol':
$options['ENCODING'] = 'FUNAMBOL-QP';
}
}
$vevent->setAttribute($field, $value, $options);
}
if ($note['info_startdate'])
{
@ -483,18 +532,6 @@ class infolog_ical extends infolog_bo
$vnote->setAttribute('DCREATED',$GLOBALS['egw']->contenthistory->getTSforAction('infolog_note',$_noteID,'add'));
$vnote->setAttribute('LAST-MODIFIED',$GLOBALS['egw']->contenthistory->getTSforAction('infolog_note',$_noteID,'modify'));
if (!empty($note['info_cat']))
{
$cats = $this->get_categories(array($note['info_cat']));
$value = $cats[0];
$vnote->setAttribute('CATEGORIES', $value);
if ($this->productManufacturer != 'groupdav'
&& preg_match('/([\177-\377])/', $value))
{
$vevent->setParameter('CATEGORIES', $options);
}
}
#$vnote->setAttribute('CLASS',$taskData['info_access'] == 'public' ? 'PUBLIC' : 'PRIVATE');
return $vnote->exportvCalendar();

View File

@ -48,7 +48,7 @@ class Horde_SyncML_Command_Sync_ContentSyncElement extends Horde_SyncML_Command_
}
*/
if (isset($this->_content) && !$this->_moreData) {
$this->_content = trim($this->_content);
//$this->_content = trim($this->_content);
$this->_contentSize = strlen($this->_content);
if (strtolower($this->_contentFormat) == 'b64') {
$this->_content = base64_encode($this->_content);

View File

@ -602,6 +602,7 @@ class Horde_iCalendar {
// Unfold any folded lines.
if ($this->isOldFormat()) {
// old formats force folding at whitespace which must therefore be preserved
$vCal = preg_replace('/[\r\n]+([ \t])/', '\1', $vCal);
} else {
$vCal = preg_replace('/[\r\n]+[ \t]/', '', $vCal);
@ -899,7 +900,7 @@ class Horde_iCalendar {
&& (!$this->isOldFormat() || empty($param_value))) {
continue;
}
if ($param_name == 'ENCODING' && empty($param_value)) {
if ($param_name == 'ENCODING') {
continue;
}
/* Skip VALUE=DATE for vCalendar 1.0 data, not allowed. */
@ -1080,20 +1081,20 @@ class Horde_iCalendar {
// Text containing newlines or ASCII >= 127 must be BASE64
// or QUOTED-PRINTABLE encoded. Currently we use
// QUOTED-PRINTABLE as default.
if (preg_match("/[^\x20-\x7F]/", $value) &&
!isset($params['ENCODING'])) {
$params['ENCODING'] = 'QUOTED-PRINTABLE';
$params_str .= ';ENCODING=QUOTED-PRINTABLE';
// Add CHARSET as well. At least the synthesis client
// gets confused otherwise
if (!isset($params['CHARSET'])) {
$params['CHARSET'] = NLS::getCharset();
$params_str .= ';CHARSET=' . $params['CHARSET'];
}
if (preg_match('/[^\x20-\x7F]/', $value) &&
!isset($params['ENCODING'])) {
$params['ENCODING'] = 'QUOTED-PRINTABLE';
}
if (preg_match('/([\177-\377])/', $value) &&
!isset($params['CHARSET'])) {
// Add CHARSET as well. At least the synthesis client
// gets confused otherwise
$params['CHARSET'] = NLS::getCharset();
$params_str .= ';CHARSET=' . $params['CHARSET'];
}
} else {
if (is_array($attribute['values']) &&
count($attribute['values'])) {
count($attribute['values']) > 1) {
$values = $attribute['values'];
if ($name == 'N' || $name == 'ADR' || $name == 'ORG') {
$glue = ';';
@ -1118,30 +1119,40 @@ class Horde_iCalendar {
}
if (!empty($params['ENCODING']) && strlen(trim($value))) {
switch($params['ENCODING']) {
case 'Q':
case 'QUOTED-PRINTABLE':
$value = str_replace("\r", '', $value);
$result .= $name . $params_str . ':'
. str_replace('=0A', '=0D=0A',
$this->_quotedPrintableEncode($value))
. $this->_newline;
break;
case 'B':
case 'BASE64':
$attr_string = $name . $params_str . ":" . $this->_newline . ' ' . $this->_base64Encode($value);
$attr_string = String::wordwrap($attr_string, 75, $this->_newline . ' ',
true, 'utf-8', true);
$result .= $attr_string . $this->_newline;
if ($this->isOldFormat()) {
$result .= $this->_newline; // Append an empty line
}
break;
}
switch($params['ENCODING']) {
case 'Q':
case 'QUOTED-PRINTABLE':
$params_str .= ';ENCODING=' . $params['ENCODING'];
$value = str_replace("\r", '', $value);
$result .= $name . $params_str . ':'
. str_replace('=0A', '=0D=0A',
$this->_quotedPrintableEncode($value))
. $this->_newline;
break;
case 'FUNAMBOL-QP':
// Funambol does not support wrapping and needs some special quoting
$params_str .= ';ENCODING=QUOTED-PRINTABLE';
$value = str_replace(array('<', "\r"), array('&lt;', ''), $value);
$result .= $name . $params_str . ':'
. str_replace('=0A', '=0D=0A',
$this->_quotedPrintableEncode($value, false))
. $this->_newline;
break;
case 'B':
case 'BASE64':
$params_str .= ';ENCODING=' . $params['ENCODING'];
$attr_string = $name . $params_str . ':' . $this->_newline . ' ' . $this->_base64Encode($value);
$attr_string = String::wordwrap($attr_string, 75, $this->_newline . ' ',
true, 'utf-8', true);
$result .= $attr_string . $this->_newline;
if ($this->isOldFormat()) {
$result .= $this->_newline; // Append an empty line
}
}
} else {
$value = str_replace(array("\r", "\n"), array('', '\\n'), $value);
$attr_string = $name . $params_str;
if (!empty($value) || $value === 0 || (is_string($value) && strlen($value) > 0)) {
if (strlen($value) > 0) {
$attr_string .= ':' . $value;
} elseif ($name != 'RRULE') {
$attr_string .= ':';
@ -1537,7 +1548,7 @@ class Horde_iCalendar {
*
* @return string The quoted-printable encoded string.
*/
function _quotedPrintableEncode($input = '')
function _quotedPrintableEncode($input = '', $withFolding=true)
{
$output = $line = '';
$len = strlen($input);
@ -1555,7 +1566,7 @@ class Horde_iCalendar {
}
$line .= $chunk;
// Wrap long lines (rule 5)
if (strlen($line) + 1 > 76) {
if ($withFolding && strlen($line) + 1 > 76) {
$line = String::wordwrap($line, 75, "=\r\n", true, 'us-ascii', true);
$newline = strrchr($line, "\r\n");
if ($newline !== false) {
@ -1567,7 +1578,7 @@ class Horde_iCalendar {
continue;
}
// Wrap at line breaks for better readability (rule 4).
if (substr($line, -3) == '=0A') {
if ($withFolding && substr($line, -3) == '=0A') {
$output .= $line . "=\r\n";
$line = '';
}