Commit c003e3b6 authored by Sebastian Kummer's avatar Sebastian Kummer

Merge pull request #55 in ZP/z-push from...

Merge pull request #55 in ZP/z-push from bugfix/ZP-702-Attachments-are-1-or-2-bytes-short to develop

* commit 'bb60677f':
  ZP-702 Implement seeking capability to MAPIStreamWrapper. Add stream size to log output.
  ZP-702 Attachments are 1 or 2 bytes short. Released under the Affero GNU General Public License (AGPL) version 3. Fixed empty attachment data written, because stream was not at the beginning eg. by a previous call to fstat. Also logging now number of bytes written and not length of stream, to be able to detect similar problems.
  ZP-702 Implement plain and base64 encoded stream support into the wbxml encoder and streamer. Removed padding filter and added a null-char-replace filter used when streaming plain streams (tested but not part of this ticket).
  ZP-702 Attachments are 1 or 2 bytes short if size is not 3 byte aligned. Released under the Affero GNU General Public License (AGPL) version 3.

(cherry picked from commit 64f7bbb2)

Conflicts:
	src/lib/wbxml/replacenullcharfilter.php
parent f915315a
...@@ -96,6 +96,27 @@ class MAPIStreamWrapper { ...@@ -96,6 +96,27 @@ class MAPIStreamWrapper {
return $data; return $data;
} }
/**
* Stream "seek" functionality.
*
* @param int $offset
* @param int $whence
* @return boolean
*/
public function stream_seek($offset, $whence = SEEK_SET) {
switch($whence) {
case SEEK_SET:
$mapiWhence = STREAM_SEEK_SET;
break;
case SEEK_END:
$mapiWhence = STREAM_SEEK_END;
break;
default:
$mapiWhence = STREAM_SEEK_CUR;
}
return mapi_stream_seek($this->mapistream, $offset, $mapiWhence);
}
/** /**
* Returns the current position on stream * Returns the current position on stream
* *
...@@ -145,4 +166,4 @@ class MAPIStreamWrapper { ...@@ -145,4 +166,4 @@ class MAPIStreamWrapper {
stream_wrapper_register(MAPIStreamWrapper::PROTOCOL, "MAPIStreamWrapper") stream_wrapper_register(MAPIStreamWrapper::PROTOCOL, "MAPIStreamWrapper")
?> ?>
\ No newline at end of file
...@@ -70,7 +70,6 @@ include_once('lib/core/statemanager.php'); ...@@ -70,7 +70,6 @@ include_once('lib/core/statemanager.php');
include_once('lib/core/devicemanager.php'); include_once('lib/core/devicemanager.php');
include_once('lib/core/zpush.php'); include_once('lib/core/zpush.php');
include_once('lib/core/zlog.php'); include_once('lib/core/zlog.php');
include_once('lib/core/paddingfilter.php');
include_once('lib/interface/ibackend.php'); include_once('lib/interface/ibackend.php');
include_once('lib/interface/ichanges.php'); include_once('lib/interface/ichanges.php');
include_once('lib/interface/iexportchanges.php'); include_once('lib/interface/iexportchanges.php');
...@@ -299,4 +298,4 @@ include_once('version.php'); ...@@ -299,4 +298,4 @@ include_once('version.php');
// end gracefully // end gracefully
ZLog::WriteEnd(); ZLog::WriteEnd();
?> ?>
\ No newline at end of file
...@@ -55,13 +55,15 @@ class Streamer implements Serializable { ...@@ -55,13 +55,15 @@ class Streamer implements Serializable {
const STREAMER_TYPE_DATE = 1; const STREAMER_TYPE_DATE = 1;
const STREAMER_TYPE_HEX = 2; const STREAMER_TYPE_HEX = 2;
const STREAMER_TYPE_DATE_DASHES = 3; const STREAMER_TYPE_DATE_DASHES = 3;
const STREAMER_TYPE_STREAM = 4; const STREAMER_TYPE_STREAM = 4; // deprecated
const STREAMER_TYPE_IGNORE = 5; const STREAMER_TYPE_IGNORE = 5;
const STREAMER_TYPE_SEND_EMPTY = 6; const STREAMER_TYPE_SEND_EMPTY = 6;
const STREAMER_TYPE_NO_CONTAINER = 7; const STREAMER_TYPE_NO_CONTAINER = 7;
const STREAMER_TYPE_COMMA_SEPARATED = 8; const STREAMER_TYPE_COMMA_SEPARATED = 8;
const STREAMER_TYPE_SEMICOLON_SEPARATED = 9; const STREAMER_TYPE_SEMICOLON_SEPARATED = 9;
const STREAMER_TYPE_MULTIPART = 10; const STREAMER_TYPE_MULTIPART = 10;
const STREAMER_TYPE_STREAM_ASBASE64 = 11;
const STREAMER_TYPE_STREAM_ASPLAIN = 12;
protected $mapping; protected $mapping;
public $flags; public $flags;
...@@ -316,23 +318,11 @@ class Streamer implements Serializable { ...@@ -316,23 +318,11 @@ class Streamer implements Serializable {
else if(isset($map[self::STREAMER_TYPE]) && $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_HEX) { else if(isset($map[self::STREAMER_TYPE]) && $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_HEX) {
$encoder->content(strtoupper(bin2hex($this->$map[self::STREAMER_VAR]))); $encoder->content(strtoupper(bin2hex($this->$map[self::STREAMER_VAR])));
} }
else if(isset($map[self::STREAMER_TYPE]) && $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM) { else if(isset($map[self::STREAMER_TYPE]) && $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM_ASPLAIN) {
//encode stream with base64 $encoder->contentStream($this->$map[self::STREAMER_VAR], false);
$stream = $this->$map[self::STREAMER_VAR]; }
$stat = fstat($stream); else if(isset($map[self::STREAMER_TYPE]) && ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM_ASBASE64 || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM)) {
// the padding size muss be calculated for the entire stream, $encoder->contentStream($this->$map[self::STREAMER_VAR], true);
// the base64 filter seems to process 8192 byte chunks correctly itself
$padding = (isset($stat['size']) && $stat['size'] > 8192) ? ($stat['size'] % 3) : 0;
$paddingfilter = stream_filter_append($stream, 'padding.'.$padding);
$base64filter = stream_filter_append($stream, 'convert.base64-encode');
$d = "";
while (!feof($stream)) {
$d .= fgets($stream, 4096);
}
$encoder->content($d);
stream_filter_remove($base64filter);
stream_filter_remove($paddingfilter);
} }
// implode comma or semicolon arrays into a string // implode comma or semicolon arrays into a string
else if(isset($map[self::STREAMER_TYPE]) && is_array($this->$map[self::STREAMER_VAR]) && else if(isset($map[self::STREAMER_TYPE]) && is_array($this->$map[self::STREAMER_VAR]) &&
...@@ -464,4 +454,4 @@ class Streamer implements Serializable { ...@@ -464,4 +454,4 @@ class Streamer implements Serializable {
} }
} }
?> ?>
\ No newline at end of file
...@@ -52,11 +52,11 @@ class SyncItemOperationsAttachment extends SyncObject { ...@@ -52,11 +52,11 @@ class SyncItemOperationsAttachment extends SyncObject {
$mapping = array( $mapping = array(
SYNC_AIRSYNCBASE_CONTENTTYPE => array ( self::STREAMER_VAR => "contenttype"), SYNC_AIRSYNCBASE_CONTENTTYPE => array ( self::STREAMER_VAR => "contenttype"),
SYNC_ITEMOPERATIONS_DATA => array ( self::STREAMER_VAR => "data", SYNC_ITEMOPERATIONS_DATA => array ( self::STREAMER_VAR => "data",
self::STREAMER_TYPE => self::STREAMER_TYPE_STREAM, self::STREAMER_TYPE => self::STREAMER_TYPE_STREAM_ASBASE64,
self::STREAMER_PROP => self::STREAMER_TYPE_MULTIPART), self::STREAMER_PROP => self::STREAMER_TYPE_MULTIPART),
); );
parent::SyncObject($mapping); parent::SyncObject($mapping);
} }
} }
?> ?>
\ No newline at end of file
<?php <?php
/*********************************************** /***********************************************
* File : paddingfilter.php * File : replacenullcharfilter.php
* Project : Z-Push * Project : Z-Push
* Descr : Our own filter for stream padding with zero strings. * Descr : Filters null characters out of a stream.
* *
* Created : 18.07.2012 * Created : 11.09.2015
* *
* Copyright 2007 - 2013 Zarafa Deutschland GmbH * Copyright 2015 Zarafa Deutschland GmbH
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3, * it under the terms of the GNU Affero General Public License, version 3,
...@@ -41,20 +41,10 @@ ...@@ -41,20 +41,10 @@
* Consult LICENSE file for details * Consult LICENSE file for details
************************************************/ ************************************************/
/* Define our filter class class replace_nullchar_filter extends php_user_filter {
*
* Usage: stream_filter_append($stream, 'padding.X');
* where X is a number a stream will be padded to be
* multiple of (e.g. padding.3 will pad the stream
* to be multiple of 3 which is useful in base64
* encoding).
*
* */
class padding_filter extends php_user_filter {
private $padding = 4; // default padding
/** /**
* This method is called whenever data is read from or written to the attached stream * This method is called whenever data is read from or written to the attached stream.
* *
* @see php_user_filter::filter() * @see php_user_filter::filter()
* *
...@@ -69,33 +59,13 @@ class padding_filter extends php_user_filter { ...@@ -69,33 +59,13 @@ class padding_filter extends php_user_filter {
*/ */
function filter($in, $out, &$consumed, $closing) { function filter($in, $out, &$consumed, $closing) {
while ($bucket = stream_bucket_make_writeable($in)) { while ($bucket = stream_bucket_make_writeable($in)) {
if ($this->padding != 0 && $bucket->datalen < 8192) { $bucket->data = str_replace("\0", "", $bucket->data);
$bucket->data .= str_pad($bucket->data, $this->padding, 0x0); $consumed += $bucket->datalen;
}
$consumed += ($this->padding != 0 && $bucket->datalen < 8192) ? ($bucket->datalen + $this->padding) : $bucket->datalen;
stream_bucket_append($out, $bucket); stream_bucket_append($out, $bucket);
} }
return PSFS_PASS_ON; return PSFS_PASS_ON;
} }
/**
* Called when creating the filter
*
* @see php_user_filter::onCreate()
*
* @access public
* @return boolean
*/
function onCreate() {
$delim = strrpos($this->filtername, '.');
if ($delim !== false) {
$padding = substr($this->filtername, $delim + 1);
if (is_numeric($padding))
$this->padding = $padding;
}
return true;
}
} }
stream_filter_register("padding.*", "padding_filter"); stream_filter_register('replacenullchar', 'replace_nullchar_filter');
?> ?>
\ No newline at end of file
...@@ -165,12 +165,12 @@ class WBXMLEncoder extends WBXMLDefs { ...@@ -165,12 +165,12 @@ class WBXMLEncoder extends WBXMLDefs {
} }
/** /**
* Puts content on the output stack * Puts content on the output stack.
* *
* @param $content * @param string $content
* *
* @access public * @access public
* @return string * @return
*/ */
public function content($content) { public function content($content) {
// We need to filter out any \0 chars because it's the string terminator in WBXML. We currently // We need to filter out any \0 chars because it's the string terminator in WBXML. We currently
...@@ -183,6 +183,29 @@ class WBXMLEncoder extends WBXMLDefs { ...@@ -183,6 +183,29 @@ class WBXMLEncoder extends WBXMLDefs {
$this->_content($content); $this->_content($content);
} }
/**
* Puts content of a stream on the output stack.
*
* @param resource $stream
* @param boolean $asBase64 if true, the data will be encoded as base64, default: false
*
* @access public
* @return
*/
public function contentStream($stream, $asBase64 = false) {
if (!$asBase64) {
include_once('lib/wbxml/replacenullcharfilter.php');
$rnc_filter = stream_filter_append($stream, 'replacenullchar');
}
$this->_outputStack();
$this->_contentStream($stream, $asBase64);
if (!$asBase64) {
stream_filter_remove($rnc_filter);
}
}
/** /**
* Gets the value of multipart * Gets the value of multipart
* *
...@@ -269,9 +292,10 @@ class WBXMLEncoder extends WBXMLDefs { ...@@ -269,9 +292,10 @@ class WBXMLEncoder extends WBXMLDefs {
} }
/** /**
* Outputs actual data * Outputs actual data.
* *
* @access private * @access private
* @param string $content
* @return * @return
*/ */
private function _content($content) { private function _content($content) {
...@@ -280,6 +304,37 @@ class WBXMLEncoder extends WBXMLDefs { ...@@ -280,6 +304,37 @@ class WBXMLEncoder extends WBXMLDefs {
$this->outTermStr($content); $this->outTermStr($content);
} }
/**
* Outputs actual data coming from a stream, optionally encoded as base64.
*
* @access private
* @param resource $stream
* @param boolean $asBase64
* @return
*/
private function _contentStream($stream, $asBase64) {
// write full stream, including the finalizing terminator to the output stream (stuff outTermStr() would do)
$this->outByte(WBXML_STR_I);
fseek($stream, 0, SEEK_SET);
if ($asBase64) {
$out_filter = stream_filter_append($this->_out, 'convert.base64-encode');
}
$written = stream_copy_to_stream($stream, $this->_out);
if ($asBase64) {
stream_filter_remove($out_filter);
}
fwrite($this->_out, chr(0));
// data is out, do some logging
$stat = fstat($stream);
$logContent = sprintf("<<< written %d of %d bytes of %s data >>>", $written, $stat['size'], $asBase64 ? "base64 encoded":"plain");
$this->logContent($logContent);
// write the meta data also to the _outLog stream, WBXML_STR_I was already written by outByte() above
fwrite($this->_outLog, $logContent);
fwrite($this->_outLog, chr(0));
}
/** /**
* Outputs an actual end tag * Outputs an actual end tag
* *
...@@ -470,7 +525,7 @@ class WBXMLEncoder extends WBXMLDefs { ...@@ -470,7 +525,7 @@ class WBXMLEncoder extends WBXMLDefs {
/** /**
* Logs content to ZLog * Logs content to ZLog
* *
* @param $content * @param string $content
* *
* @access private * @access private
* @return * @return
...@@ -539,4 +594,4 @@ class WBXMLEncoder extends WBXMLDefs { ...@@ -539,4 +594,4 @@ class WBXMLEncoder extends WBXMLDefs {
} }
} }
?> ?>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment