/** * 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); } } } }