/**
  * returns VObject of input data
  * 
  * @param   mixed  $blob
  * @return  \Sabre\VObject\Component\VCalendar
  */
 public static function getVObject($blob)
 {
     if ($blob instanceof \Sabre\VObject\Component\VCalendar) {
         return $blob;
     }
     if (is_resource($blob)) {
         $blob = stream_get_contents($blob);
     }
     $blob = Tinebase_Core::filterInputForDatabase($blob);
     $vcalendar = self::readVCalBlob($blob);
     return $vcalendar;
 }
 /**
  * parse headers and set 'date', 'from', 'to', 'cc', 'bcc', 'subject', 'sender' fields
  * 
  * @param array $_headers
  * @return void
  */
 public function parseHeaders(array $_headers)
 {
     // remove duplicate headers (which can't be set twice in real life)
     foreach (array('date', 'from', 'subject', 'sender') as $field) {
         if (isset($_headers[$field]) && is_array($_headers[$field])) {
             $_headers[$field] = $_headers[$field][0];
         }
     }
     // @see 0008644: error when sending mail with note (wrong charset)
     $this->subject = isset($_headers['subject']) ? Tinebase_Core::filterInputForDatabase(Felamimail_Message::convertText($_headers['subject'])) : null;
     if (isset($_headers['date']) || array_key_exists('date', $_headers)) {
         $this->sent = Felamimail_Message::convertDate($_headers['date']);
     } elseif (isset($_headers['resent-date']) || array_key_exists('resent-date', $_headers)) {
         $this->sent = Felamimail_Message::convertDate($_headers['resent-date']);
     }
     $punycodeConverter = Felamimail_Controller_Message::getInstance()->getPunycodeConverter();
     foreach (array('to', 'cc', 'bcc', 'from', 'sender') as $field) {
         if (isset($_headers[$field])) {
             if (is_array($_headers[$field])) {
                 $value = array();
                 foreach ($_headers[$field] as $headerValue) {
                     $value = array_merge($value, Felamimail_Message::convertAddresses($headerValue, $punycodeConverter));
                 }
                 $this->{$field} = $value;
             } else {
                 $value = Felamimail_Message::convertAddresses($_headers[$field], $punycodeConverter);
                 switch ($field) {
                     case 'from':
                         $this->from_email = isset($value[0]) && (isset($value[0]['email']) || array_key_exists('email', $value[0])) ? $value[0]['email'] : '';
                         $this->from_name = isset($value[0]) && (isset($value[0]['name']) || array_key_exists('name', $value[0])) && !empty($value[0]['name']) ? $value[0]['name'] : $this->from_email;
                         break;
                     case 'sender':
                         $this->sender = isset($value[0]) && (isset($value[0]['email']) || array_key_exists('email', $value[0])) ? '<' . $value[0]['email'] . '>' : '';
                         if (isset($value[0]) && (isset($value[0]['name']) || array_key_exists('name', $value[0])) && !empty($value[0]['name'])) {
                             $this->sender = '"' . $value[0]['name'] . '" ' . $this->sender;
                         }
                         break;
                     default:
                         $this->{$field} = $value;
                 }
             }
         }
     }
 }
 /**
  * testSaveMessageNoteWithInvalidChar
  *
  * @see 0008644: error when sending mail with note (wrong charset)
  */
 public function testSaveMessageWithInvalidChar()
 {
     $subject = "😊";
     // :-) emoji
     $messageData = $this->_getMessageData('', $subject);
     $this->_foldersToClear[] = 'INBOX';
     $this->_json->saveMessage($messageData);
     $message = $this->_searchForMessageBySubject(Tinebase_Core::filterInputForDatabase($subject));
 }
    /**
     * testSendMailveopeAPIMessage
     *
     * - envolpe amored message into PGP MIME structure
     */
    public function testSendMailveopeAPIMessage()
    {
        $subject = 'testSendMailveopeAPIMessage';
        $messageData = $this->_getMessageData('', $subject);
        $messageData['body'] = '-----BEGIN PGP MESSAGE-----
Version: Mailvelope v1.3.3
Comment: https://www.mailvelope.com

wcFMA/0LJF28pDbGAQ//YgtsmEZN+pgIJiBDb7iYwPEOchDRIEjGOx543KF6
5YigW9p39pfcJgvGfT8x9cUIrYGxyw5idPSOEftYXyjjGaOYGaKpRSR4hI83
OcJSlEHKq72xhg04mNpCjjJ8dLBstPcQ7tDtsA8Nfb4PwkUYB9IhIBnARg+n
NvrN8mSA2UnY9ElFCvf30sar8EuM5swAjbk64C8TIypMy/Bg4T93zRdxwik6
7BCcbOpm/2PTsiVYBOTcU4+XdG5eyTENXH58M6UTxTD4/g7Qi5PjN+PxyXqf
v2Y1k9F49Y1egf2QJ2r4PX0EWS8SaynSHiIoBsp1xb07nLwZwCdMPG1QNPpF
l2FqlS4dEuQTdkv0deMvd7gtiNynRTAVcJc1ZC6RuWJ+EH2jA49XWkn14eRC
e5jMtPPudkhubnN9Je5lwatGKbJGyuXh+IaM0E0WQMZ3dm8+ST1l4WpVuGbw
KozLUiTRJP9UoxWOcwpQOnzcSlc4rHmWdtF0y3usM9u9GPREqpNUWkEyEEuv
XdZE7rKKj22dJHLCXxAQEh3m29Y2WVaq50YbtEZ+SwwbrHhxP4+FJEru+byh
fiZ47sVW2KvYGJPvbFoSZHiSvMecxDg8BVwe+naZXww/Rwa/TlaX4bYyzpUG
KeJUAzWEfFpJ0+yAvMGQEC7psIJ9NCx149C4ujiQmajSwhUB3XANcmCGB0wm
JjcqC4AHvc7/t4MrQZm0F/W+nrMmNqbZk+gylVrPs9rFEqu7wbjwTmsFA3sS
LkenvQIxBali6uzCR+nd09REqcYirG9cLti39DW048lhhG/ml+gAxxNEaSpG
NbIoV/3w8n7sAIM1fjuHne8bX0gWG43TTjU8MwSMryG7tCOG5u+Cebh6TAoY
NzbX2dpDhOYq5zXdCgKU4P3eh0csSs4UrqFT3TdAxIGrQJ7KrXvB6+N8gRZo
FcUaR+zrRPJjPUZfi46ecP5SG/tM5ea1hqvkwEnEpqjLmCUxqB+rfxx46USX
hMZd2ukUv6kEKv3EUDsRYu1SlDLhDLhWNx8RJae5XkMR+eUUMyNNVwbeMQbB
VAcMcaPITTk84sH7XElr9eF6sCUN4V79OSBRPGY/aNGrcwcoDSD4Hwu+Lw9w
Q+1n8EQ66gAkbJzCNd5GaYMZR9echkBaD/rdWDS3ktcrMehra+h44MTQONV9
8W+RI+IT5jaIXtB4jePmGjsJjbC9aEhTRBRkUnPA7phgknc52dD74AY/6lzK
yd4uZ6S3vhurJW0Vt4iBWJzhFNiSODh5PzteeNzCVAkGMsQvy1IHk0d3uzcE
0tEuSh8fZOFGB4fvMx9Mk8oAU92wfj4J7AVpSo5oRdxMqAXfaYKqfr2Gn++q
E5LClhVIBbFXclCoe0RYNz4wtxjeeYbP40Bq5g0JvPutD/dBMp8hz8Qt+yyG
d8X4/KmQIXyFZ8aP17GMckE5GVVvY9y89eWnWuTUJdwM540hB/EJNeHHTE5y
N2FSLGcmNkvE+3H7BczQ2ZI1SZDhof+umbUst0qoQW+hHmY3CSma48yGAVox
52u2t7hosHCfpf631Ve/6fcICo8vJ2Qfufu2BGIMlSfx4WzUuaMQBynuxFSa
IbVx8ZTO7dJRKrg72aFmWTf0uNla7vicAhpiLWobyNYcZbIjrAGDfg==
=BaAn
-----END PGP MESSAGE-----';
        $this->_foldersToClear[] = 'INBOX';
        $this->_json->saveMessage($messageData);
        $message = $this->_searchForMessageBySubject(Tinebase_Core::filterInputForDatabase($subject));
        $fullMessage = $this->_json->getMessage($message['id']);
        $this->assertContains('multipart/encrypted', $fullMessage['headers']['content-type']);
        $this->assertContains('protocol="application/pgp-encrypted"', $fullMessage['headers']['content-type']);
        $this->assertCount(2, $fullMessage['structure']['parts']);
        $this->assertEquals('application/pgp-encrypted', $fullMessage['structure']['parts'][1]['contentType']);
        $this->assertEquals('application/octet-stream', $fullMessage['structure']['parts'][2]['contentType']);
        return $fullMessage;
    }
 /**
  * get and decode message body
  * 
  * @param Felamimail_Model_Message $_message
  * @param string $_partId
  * @param string $_contentType
  * @param Felamimail_Model_Account $_account
  * @return string
  * 
  * @todo multipart_related messages should deliver inline images
  */
 protected function _getAndDecodeMessageBody(Felamimail_Model_Message $_message, $_partId, $_contentType, $_account = NULL)
 {
     $structure = $_message->getPartStructure($_partId);
     if (empty($structure)) {
         if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) {
             Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' Empty structure, could not find body parts of message ' . $_message->subject);
         }
         return '';
     }
     $bodyParts = $_message->getBodyParts($structure, $_contentType);
     if (empty($bodyParts)) {
         if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) {
             Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' Could not find body parts of message ' . $_message->subject);
         }
         return '';
     }
     $messageBody = '';
     foreach ($bodyParts as $partId => $partStructure) {
         $bodyPart = $this->getMessagePart($_message, $partId, TRUE, $partStructure);
         $body = Tinebase_Mail::getDecodedContent($bodyPart, $partStructure);
         if ($partStructure['contentType'] != Zend_Mime::TYPE_TEXT) {
             $bodyCharCountBefore = strlen($body);
             $body = $this->_purifyBodyContent($body, $_message->getId());
             $bodyCharCountAfter = strlen($body);
             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
                 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Purifying removed ' . ($bodyCharCountBefore - $bodyCharCountAfter) . ' / ' . $bodyCharCountBefore . ' characters.');
             }
             if ($_message->text_partid && $bodyCharCountAfter < $bodyCharCountBefore / 10) {
                 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
                     Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Purify may have removed (more than 9/10) too many chars, using alternative text message part.');
                 }
                 $result = $this->_getAndDecodeMessageBody($_message, $_message->text_partid, Zend_Mime::TYPE_TEXT, $_account);
                 return Felamimail_Message::convertContentType(Zend_Mime::TYPE_TEXT, Zend_Mime::TYPE_HTML, $result);
             }
         }
         if (!($_account !== NULL && $_account->display_format === Felamimail_Model_Account::DISPLAY_CONTENT_TYPE && $bodyPart->type == Zend_Mime::TYPE_TEXT)) {
             $body = Felamimail_Message::convertContentType($partStructure['contentType'], $_contentType, $body);
             if ($bodyPart->type == Zend_Mime::TYPE_TEXT && $_contentType == Zend_Mime::TYPE_HTML) {
                 $body = Felamimail_Message::replaceUris($body);
                 $body = Felamimail_Message::replaceEmails($body);
             }
         } else {
             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
                 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Do not convert ' . $bodyPart->type . ' part to ' . $_contentType);
             }
         }
         $body = Felamimail_Message::replaceTargets($body);
         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
             Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Adding part ' . $partId . ' to message body.');
         }
         $messageBody .= Tinebase_Core::filterInputForDatabase($body);
     }
     return $messageBody;
 }
 /**
  * (non-PHPdoc)
  * @see Tinebase_Server_Interface::handle()
  */
 public function handle(\Zend\Http\Request $request = null, $body = null)
 {
     $this->_request = $request instanceof \Zend\Http\Request ? $request : Tinebase_Core::get(Tinebase_Core::REQUEST);
     $this->_body = $body !== null ? $body : fopen('php://input', 'r');
     $request = $request instanceof \Zend\Http\Request ? $request : new \Zend\Http\PhpEnvironment\Request();
     // only for debugging
     //Tinebase_Core::getLogger()->DEBUG(__METHOD__ . '::' . __LINE__ . " raw request: " . $request->__toString());
     // handle CORS requests
     if ($request->getHeaders()->has('ORIGIN') && !$request->getHeaders()->has('X-FORWARDED-HOST')) {
         /**
          * First the client sends a preflight request
          * 
          * METHOD: OPTIONS
          * Access-Control-Request-Headers:x-requested-with, content-type
          * Access-Control-Request-Method:POST
          * Origin:http://other.site
          * Referer:http://other.site/example.html
          * User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36
          * 
          * We have to respond with
          * 
          * Access-Control-Allow-Credentials:true
          * Access-Control-Allow-Headers:x-requested-with, x-tine20-request-type, content-type, x-tine20-jsonkey
          * Access-Control-Allow-Methods:POST
          * Access-Control-Allow-Origin:http://other.site
          * 
          * Then the client sends the standard JSON request with two additional headers
          * 
          * METHOD: POST
          * Origin:http://other.site
          * Referer:http://other.site/example.html
          * Standard-JSON-Rquest-Headers...
          * 
          * We have to add two additional headers to our standard response
          * 
          * Access-Control-Allow-Credentials:true
          * Access-Control-Allow-Origin:http://other.site
          */
         $origin = $request->getHeaders('ORIGIN')->getFieldValue();
         $uri = \Zend\Uri\UriFactory::factory($origin);
         if (in_array($uri->getScheme(), array('http', 'https'))) {
             $allowedOrigins = array_merge((array) Tinebase_Core::getConfig()->get(Tinebase_Config::ALLOWEDJSONORIGINS, array()), array($this->_request->getServer('SERVER_NAME')));
             if (in_array($uri->getHost(), $allowedOrigins)) {
                 // this headers have to be sent, for any CORS'ed JSON request
                 header('Access-Control-Allow-Origin: ' . $origin);
                 header('Access-Control-Allow-Credentials: true');
             }
             // check for CORS preflight request
             if ($request->getMethod() == \Zend\Http\Request::METHOD_OPTIONS && $request->getHeaders()->has('ACCESS-CONTROL-REQUEST-METHOD')) {
                 $this->_methods = array('handleCors');
                 if (in_array($uri->getHost(), $allowedOrigins)) {
                     header('Access-Control-Allow-Methods: POST');
                     header('Access-Control-Allow-Headers: x-requested-with, x-tine20-request-type, content-type, x-tine20-jsonkey');
                     header('Access-Control-Max-Age: 3600');
                     // cache result of OPTIONS request for 1 hour
                 } else {
                     Tinebase_Core::getLogger()->WARN(__METHOD__ . '::' . __LINE__ . " unhandled CORS preflight request from {$origin}");
                     Tinebase_Core::getLogger()->INFO(__METHOD__ . '::' . __LINE__ . " you may want to set \"'allowedJsonOrigins' => array('{$uri->getHost()}'),\" to config.inc.php");
                     Tinebase_Core::getLogger()->DEBUG(__METHOD__ . '::' . __LINE__ . " allowed origins: " . print_r($allowedOrigins, TRUE));
                 }
                 // stop further processing => is OPTIONS request
                 return;
             }
         }
     }
     $exception = false;
     if (Tinebase_Session::sessionExists()) {
         try {
             Tinebase_Core::startCoreSession();
         } catch (Zend_Session_Exception $zse) {
             $exception = new Tinebase_Exception_AccessDenied('Not Authorised', 401);
             // expire session cookie for client
             Tinebase_Session::expireSessionCookie();
         }
     }
     if ($exception === false) {
         try {
             Tinebase_Core::initFramework();
         } catch (Exception $exception) {
             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
                 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' initFramework exception: ' . $exception);
             }
         }
     }
     $json = $request->getContent();
     $json = Tinebase_Core::filterInputForDatabase($json);
     if (substr($json, 0, 1) == '[') {
         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
             Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' batched request');
         }
         $isBatchedRequest = true;
         $requests = Zend_Json::decode($json);
     } else {
         $isBatchedRequest = false;
         $requests = array(Zend_Json::decode($json));
     }
     if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
         $_requests = $requests;
         foreach (array('password', 'oldPassword', 'newPassword') as $field) {
             if (isset($requests[0]["params"][$field])) {
                 $_requests[0]["params"][$field] = "*******";
             }
         }
         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
             Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' is JSON request. rawdata: ' . print_r($_requests, true));
         }
     }
     $response = array();
     foreach ($requests as $requestOptions) {
         if ($requestOptions !== NULL) {
             $request = new Zend_Json_Server_Request();
             $request->setOptions($requestOptions);
             $response[] = $exception ? $this->_handleException($request, $exception) : $this->_handle($request);
         } else {
             if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) {
                 Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' Got empty request options: skip request.');
             }
             $response[] = NULL;
         }
     }
     if (!headers_sent()) {
         header('Content-type: application/json');
     }
     echo $isBatchedRequest ? '[' . implode(',', $response) . ']' : $response[0];
 }
 /**
  * update foreign key values
  * 
  * @param string $_mode create|update
  * @param Tinebase_Record_Interface $_record
  * 
  * @todo support update mode
  */
 protected function _updateForeignKeys($_mode, Tinebase_Record_Interface $_record)
 {
     if ($_mode == 'create') {
         foreach ($this->_foreignTables as $key => $foreign) {
             if (!isset($_record->{$key}) || empty($_record->{$key})) {
                 continue;
             }
             if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) {
                 Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' ' . $key . ': ' . print_r($_record->{$key}, TRUE));
             }
             foreach ($_record->{$key} as $data) {
                 if ($key == 'flags') {
                     $data = array('flag' => $data, 'folder_id' => $_record->folder_id);
                 } else {
                     // need to filter input as 'name' could contain invalid chars (emojis, ...) here
                     foreach ($data as $field => $value) {
                         $data[$field] = Tinebase_Core::filterInputForDatabase($data[$field]);
                     }
                 }
                 $data['message_id'] = $_record->getId();
                 $this->_db->insert($this->_tablePrefix . $foreign['table'], $data);
             }
         }
     }
 }