mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-09-07 00:17:03 +03:00
Redesign Raw handling as encodeRawKey/decodeRawKey doesn't need encryption
This commit is contained in:
parent
402de0d569
commit
bd7f9856f2
8 changed files with 73 additions and 115 deletions
|
|
@ -35,9 +35,7 @@ export function MimeToMessage(data, message)
|
||||||
attachment.url = part.dataUrl;
|
attachment.url = part.dataUrl;
|
||||||
attachment.estimatedSize = part.body.length;
|
attachment.estimatedSize = part.body.length;
|
||||||
/*
|
/*
|
||||||
attachment.isThumbnail = false;
|
|
||||||
attachment.contentLocation = '';
|
attachment.contentLocation = '';
|
||||||
attachment.download = '';
|
|
||||||
attachment.folder = '';
|
attachment.folder = '';
|
||||||
attachment.uid = '';
|
attachment.uid = '';
|
||||||
attachment.mimeIndex = part.id;
|
attachment.mimeIndex = part.id;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import ko from 'ko';
|
import ko from 'ko';
|
||||||
|
|
||||||
import { FileInfo, FileType } from 'Common/File';
|
import { FileInfo, FileType } from 'Common/File';
|
||||||
import { stopEvent } from 'Common/Globals';
|
import { stopEvent, SettingsGet, SettingsCapa } from 'Common/Globals';
|
||||||
|
import { b64EncodeJSONSafe } from 'Common/Utils';
|
||||||
import {
|
import {
|
||||||
attachmentDownload,
|
attachmentDownload,
|
||||||
serverRequestRaw
|
serverRequestRaw
|
||||||
|
|
@ -23,10 +24,8 @@ export class AttachmentModel extends AbstractModel {
|
||||||
this.fileName = '';
|
this.fileName = '';
|
||||||
this.fileNameExt = '';
|
this.fileNameExt = '';
|
||||||
this.fileType = FileType.Unknown;
|
this.fileType = FileType.Unknown;
|
||||||
this.isThumbnail = false;
|
|
||||||
this.cId = '';
|
this.cId = '';
|
||||||
this.contentLocation = '';
|
this.contentLocation = '';
|
||||||
this.download = '';
|
|
||||||
this.folder = '';
|
this.folder = '';
|
||||||
this.uid = '';
|
this.uid = '';
|
||||||
this.url = '';
|
this.url = '';
|
||||||
|
|
@ -126,6 +125,17 @@ export class AttachmentModel extends AbstractModel {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get download() {
|
||||||
|
return b64EncodeJSONSafe({
|
||||||
|
folder: this.folder,
|
||||||
|
uid: this.uid,
|
||||||
|
mimeIndex: this.mimeIndex,
|
||||||
|
mimeType: this.mimeType,
|
||||||
|
fileName: this.fileName,
|
||||||
|
accountHash: SettingsGet('accountHash')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
|
|
@ -144,7 +154,7 @@ export class AttachmentModel extends AbstractModel {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
hasThumbnail() {
|
hasThumbnail() {
|
||||||
return this.isThumbnail && !this.isLinked();
|
return SettingsCapa('AttachmentThumbnails') && this.isImage() && !this.isLinked();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { i18n } from 'Common/Translator';
|
||||||
|
|
||||||
import { doc, SettingsGet } from 'Common/Globals';
|
import { doc, SettingsGet } from 'Common/Globals';
|
||||||
import { encodeHtml, plainToHtml, htmlToPlain, cleanHtml } from 'Common/Html';
|
import { encodeHtml, plainToHtml, htmlToPlain, cleanHtml } from 'Common/Html';
|
||||||
import { forEachObjectEntry } from 'Common/Utils';
|
import { forEachObjectEntry, b64EncodeJSONSafe } from 'Common/Utils';
|
||||||
import { serverRequestRaw, proxy } from 'Common/Links';
|
import { serverRequestRaw, proxy } from 'Common/Links';
|
||||||
import { addObservablesTo, addComputablesTo } from 'External/ko';
|
import { addObservablesTo, addComputablesTo } from 'External/ko';
|
||||||
|
|
||||||
|
|
@ -59,7 +59,6 @@ export class MessageModel extends AbstractModel {
|
||||||
this.folder = '';
|
this.folder = '';
|
||||||
this.uid = 0;
|
this.uid = 0;
|
||||||
this.hash = '';
|
this.hash = '';
|
||||||
this.requestHash = '';
|
|
||||||
this.from = new EmailCollectionModel;
|
this.from = new EmailCollectionModel;
|
||||||
this.to = new EmailCollectionModel;
|
this.to = new EmailCollectionModel;
|
||||||
this.cc = new EmailCollectionModel;
|
this.cc = new EmailCollectionModel;
|
||||||
|
|
@ -178,6 +177,16 @@ export class MessageModel extends AbstractModel {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get requestHash() {
|
||||||
|
return b64EncodeJSONSafe({
|
||||||
|
folder: this.folder,
|
||||||
|
uid: this.uid,
|
||||||
|
mimeType: 'message/rfc822',
|
||||||
|
fileName: (this.subject() || 'message-' + this.hash) + '.eml',
|
||||||
|
accountHash: SettingsGet('accountHash')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
toggleTag(keyword) {
|
toggleTag(keyword) {
|
||||||
toggleTag(this, keyword);
|
toggleTag(this, keyword);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,24 +18,6 @@ namespace MailSo\Base;
|
||||||
abstract class Utils
|
abstract class Utils
|
||||||
{
|
{
|
||||||
|
|
||||||
public static function fileHasThumbnail(string $sFileName) : bool
|
|
||||||
{
|
|
||||||
static $aCache = array();
|
|
||||||
|
|
||||||
$sExt = static::GetFileExtension($sFileName);
|
|
||||||
if (isset($aCache[$sExt])) {
|
|
||||||
return $aCache[$sExt];
|
|
||||||
}
|
|
||||||
|
|
||||||
$aCache[$sExt] = (
|
|
||||||
\extension_loaded('gd')
|
|
||||||
|| \extension_loaded('gmagick')
|
|
||||||
|| \extension_loaded('imagick')
|
|
||||||
) && \in_array($sExt, ['png', 'gif', 'jpg', 'jpeg', 'webp']);
|
|
||||||
|
|
||||||
return $aCache[$sExt];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function NormalizeCharset(string $sEncoding, bool $bAsciAsUtf8 = false) : string
|
public static function NormalizeCharset(string $sEncoding, bool $bAsciAsUtf8 = false) : string
|
||||||
{
|
{
|
||||||
$sEncoding = \preg_replace('/^iso8/', 'iso-8', \strtolower($sEncoding));
|
$sEncoding = \preg_replace('/^iso8/', 'iso-8', \strtolower($sEncoding));
|
||||||
|
|
|
||||||
|
|
@ -59,20 +59,10 @@ class Attachment implements \JsonSerializable
|
||||||
#[\ReturnTypeWillChange]
|
#[\ReturnTypeWillChange]
|
||||||
public function jsonSerialize()
|
public function jsonSerialize()
|
||||||
{
|
{
|
||||||
$aResult = \array_merge([
|
return \array_merge([
|
||||||
'@Object' => 'Object/Attachment',
|
'@Object' => 'Object/Attachment',
|
||||||
'folder' => $this->sFolder,
|
'folder' => $this->sFolder,
|
||||||
'uid' => $this->iUid
|
'uid' => $this->iUid
|
||||||
], $this->oBodyStructure->jsonSerialize());
|
], $this->oBodyStructure->jsonSerialize());
|
||||||
$aResult['isThumbnail'] = \MailSo\Base\Utils::fileHasThumbnail($aResult['fileName']);
|
|
||||||
$oActions = \RainLoop\Api::Actions();
|
|
||||||
$aResult['download'] = $oActions->encodeRawKey(array(
|
|
||||||
'folder' => $aResult['folder'],
|
|
||||||
'uid' => $aResult['uid'],
|
|
||||||
'mimeIndex' => $aResult['mimeIndex'],
|
|
||||||
'mimeType' => $aResult['mimeType'],
|
|
||||||
'fileName' => $aResult['fileName']
|
|
||||||
));
|
|
||||||
return $aResult;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1080,13 +1080,19 @@ class Actions
|
||||||
|
|
||||||
public function encodeRawKey(array $aValues): string
|
public function encodeRawKey(array $aValues): string
|
||||||
{
|
{
|
||||||
return \SnappyMail\Crypt::EncryptUrlSafe($aValues, \sha1(APP_SALT . $this->getAccountFromToken()->Hash()));
|
$aValues['accountHash'] = $this->getAccountFromToken()->Hash();
|
||||||
|
return \MailSo\Base\Utils::UrlSafeBase64Encode(\json_encode($aValues));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function decodeRawKey(string $sRawKey): array
|
protected function decodeRawKey(string $sRawKey): array
|
||||||
{
|
{
|
||||||
return empty($sRawKey) ? []
|
return empty($sRawKey) ? []
|
||||||
: (\SnappyMail\Crypt::DecryptUrlSafe($sRawKey, \sha1(APP_SALT . $this->getAccountFromToken()->Hash())) ?: []);
|
: (\json_decode(\MailSo\Base\Utils::UrlSafeBase64Decode($sRawKey), true) ?: []);
|
||||||
|
/*
|
||||||
|
if (empty($aValues['accountHash']) || $aValues['accountHash'] !== $oAccount->Hash()) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
public function StaticCache(): string
|
public function StaticCache(): string
|
||||||
|
|
|
||||||
|
|
@ -9,21 +9,28 @@ trait Raw
|
||||||
*/
|
*/
|
||||||
public function RawViewAsPlain() : bool
|
public function RawViewAsPlain() : bool
|
||||||
{
|
{
|
||||||
$oAccount = $this->initMailClientConnection();
|
$oAccount = $this->getAccountFromToken();
|
||||||
|
$sRawKey = $this->GetActionParam('RawKey', '');
|
||||||
$aValues = $this->decodeRawKey((string) $this->GetActionParam('RawKey', ''));
|
$aValues = $this->decodeRawKey($sRawKey);
|
||||||
|
if (!empty($aValues['folder']) && !empty($aValues['uid'])
|
||||||
$sFolder = isset($aValues['folder']) ? (string) $aValues['folder'] : '';
|
&& !empty($aValues['accountHash']) && $aValues['accountHash'] === $oAccount->Hash()
|
||||||
$iUid = isset($aValues['uid']) ? (int) $aValues['uid'] : 0;
|
) {
|
||||||
$sMimeIndex = isset($aValues['mimeIndex']) ? (string) $aValues['mimeIndex'] : '';
|
$this->verifyCacheByKey($sRawKey);
|
||||||
|
$this->initMailClientConnection();
|
||||||
\header('Content-Type: text/plain');
|
\header('Content-Type: text/plain');
|
||||||
|
return $this->MailClient()->MessageMimeStream(
|
||||||
return $this->MailClient()->MessageMimeStream(function ($rResource) {
|
function ($rResource) use ($sRawKey) {
|
||||||
if (\is_resource($rResource)) {
|
if (\is_resource($rResource)) {
|
||||||
\MailSo\Base\Utils::FpassthruWithTimeLimitReset($rResource);
|
$this->cacheByKey($sRawKey);
|
||||||
}
|
\MailSo\Base\Utils::FpassthruWithTimeLimitReset($rResource);
|
||||||
}, $sFolder, $iUid, $sMimeIndex);
|
}
|
||||||
|
},
|
||||||
|
(string) $aValues['folder'],
|
||||||
|
(int) $aValues['uid'],
|
||||||
|
isset($aValues['mimeIndex']) ? (string) $aValues['mimeIndex'] : ''
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function RawDownload() : bool
|
public function RawDownload() : bool
|
||||||
|
|
@ -69,71 +76,36 @@ trait Raw
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function RawPublic() : bool
|
/**
|
||||||
{
|
* Message, Message Attachment or Zip
|
||||||
$sRawKey = (string) $this->GetActionParam('RawKey', '');
|
*/
|
||||||
$this->verifyCacheByKey($sRawKey);
|
|
||||||
|
|
||||||
$sHash = $sRawKey;
|
|
||||||
$sData = '';
|
|
||||||
|
|
||||||
if (!empty($sHash)) {
|
|
||||||
$sData = $this->StorageProvider()->Get(null,
|
|
||||||
\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY,
|
|
||||||
\RainLoop\KeyPathHelper::PublicFile($sHash)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$aMatch = array();
|
|
||||||
if (!empty($sData) && 0 === \strpos($sData, 'data:') &&
|
|
||||||
\preg_match('/^data:([^:]+):/', $sData, $aMatch) && !empty($aMatch[1]))
|
|
||||||
{
|
|
||||||
$sContentType = \trim($aMatch[1]);
|
|
||||||
if (\in_array($sContentType, array('image/png', 'image/jpg', 'image/jpeg'))) {
|
|
||||||
$this->cacheByKey($sRawKey);
|
|
||||||
|
|
||||||
\header('Content-Type: '.$sContentType);
|
|
||||||
echo \preg_replace('/^data:[^:]+:/', '', $sData);
|
|
||||||
unset($sData);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function rawSmart(bool $bDownload, bool $bThumbnail = false) : bool
|
private function rawSmart(bool $bDownload, bool $bThumbnail = false) : bool
|
||||||
{
|
{
|
||||||
$sRawKey = (string) $this->GetActionParam('RawKey', '');
|
$sRawKey = (string) $this->GetActionParam('RawKey', '');
|
||||||
|
|
||||||
$oAccount = $this->getAccountFromToken();
|
$oAccount = $this->getAccountFromToken();
|
||||||
$aValues = $this->decodeRawKey($sRawKey);
|
$aValues = $this->decodeRawKey($sRawKey);
|
||||||
|
if (empty($aValues['accountHash']) || $aValues['accountHash'] !== $oAccount->Hash()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$sRange = \MailSo\Base\Http::GetHeader('Range');
|
$sRange = \MailSo\Base\Http::GetHeader('Range');
|
||||||
|
|
||||||
$aMatch = array();
|
$aMatch = array();
|
||||||
$sRangeStart = $sRangeEnd = '';
|
$sRangeStart = $sRangeEnd = '';
|
||||||
$bIsRangeRequest = false;
|
$bIsRangeRequest = false;
|
||||||
|
|
||||||
if (!empty($sRange) && 'bytes=0-' !== \strtolower($sRange)
|
if (!empty($sRange) && 'bytes=0-' !== \strtolower($sRange)
|
||||||
&& \preg_match('/^bytes=([0-9]+)-([0-9]*)/i', \trim($sRange), $aMatch))
|
&& \preg_match('/^bytes=([0-9]+)-([0-9]*)/i', \trim($sRange), $aMatch))
|
||||||
{
|
{
|
||||||
$sRangeStart = $aMatch[1];
|
$sRangeStart = $aMatch[1];
|
||||||
$sRangeEnd = $aMatch[2];
|
$sRangeEnd = $aMatch[2];
|
||||||
|
|
||||||
$bIsRangeRequest = true;
|
$bIsRangeRequest = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$sFolder = isset($aValues['folder']) ? (string) $aValues['folder'] : '';
|
|
||||||
$iUid = isset($aValues['uid']) ? (int) $aValues['uid'] : 0;
|
|
||||||
$sMimeIndex = isset($aValues['mimeIndex']) ? (string) $aValues['mimeIndex'] : '';
|
$sMimeIndex = isset($aValues['mimeIndex']) ? (string) $aValues['mimeIndex'] : '';
|
||||||
|
|
||||||
$sContentTypeIn = isset($aValues['mimeType']) ? (string) $aValues['mimeType'] : '';
|
$sContentTypeIn = isset($aValues['mimeType']) ? (string) $aValues['mimeType'] : '';
|
||||||
$sFileNameIn = isset($aValues['fileName']) ? (string) $aValues['fileName'] : '';
|
$sFileNameIn = isset($aValues['fileName']) ? (string) $aValues['fileName'] : '';
|
||||||
$sFileHashIn = isset($aValues['fileHash']) ? (string) $aValues['fileHash'] : '';
|
|
||||||
|
|
||||||
if (!empty($sFileHashIn)) {
|
if (!empty($aValues['fileHash'])) {
|
||||||
$this->verifyCacheByKey($sRawKey);
|
$this->verifyCacheByKey($sRawKey);
|
||||||
|
|
||||||
// https://github.com/the-djmaze/snappymail/issues/144
|
// https://github.com/the-djmaze/snappymail/issues/144
|
||||||
|
|
@ -147,7 +119,7 @@ trait Raw
|
||||||
|
|
||||||
$sFileNameOut = $this->MainClearFileName($sFileNameIn, $sContentTypeIn, $sMimeIndex);
|
$sFileNameOut = $this->MainClearFileName($sFileNameIn, $sContentTypeIn, $sMimeIndex);
|
||||||
|
|
||||||
$rResource = $this->FilesProvider()->GetFile($oAccount, $sFileHashIn);
|
$rResource = $this->FilesProvider()->GetFile($oAccount, (string) $aValues['fileHash']);
|
||||||
if (\is_resource($rResource)) {
|
if (\is_resource($rResource)) {
|
||||||
\header('Content-Type: '.$sContentTypeOut);
|
\header('Content-Type: '.$sContentTypeOut);
|
||||||
\header('Content-Disposition: attachment; '.
|
\header('Content-Disposition: attachment; '.
|
||||||
|
|
@ -162,11 +134,15 @@ trait Raw
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (!empty($sFolder) && 0 < $iUid)
|
|
||||||
{
|
$sFolder = isset($aValues['folder']) ? (string) $aValues['folder'] : '';
|
||||||
$this->verifyCacheByKey($sRawKey);
|
$iUid = isset($aValues['uid']) ? (int) $aValues['uid'] : 0;
|
||||||
|
if (empty($sFolder) || 1 > $iUid) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->verifyCacheByKey($sRawKey);
|
||||||
|
|
||||||
$this->initMailClientConnection();
|
$this->initMailClientConnection();
|
||||||
|
|
||||||
$self = $this;
|
$self = $this;
|
||||||
|
|
|
||||||
|
|
@ -94,8 +94,6 @@ trait Response
|
||||||
if ($mResponse instanceof \MailSo\Mail\Message) {
|
if ($mResponse instanceof \MailSo\Mail\Message) {
|
||||||
$aResult = $mResponse->jsonSerialize();
|
$aResult = $mResponse->jsonSerialize();
|
||||||
|
|
||||||
$oAccount = $this->getAccountFromToken();
|
|
||||||
|
|
||||||
if (!$aResult['dateTimeStampInUTC'] || $this->Config()->Get('labs', 'date_from_headers', true)) {
|
if (!$aResult['dateTimeStampInUTC'] || $this->Config()->Get('labs', 'date_from_headers', true)) {
|
||||||
$iDateTimeStampInUTC = $mResponse->HeaderTimeStampInUTC;
|
$iDateTimeStampInUTC = $mResponse->HeaderTimeStampInUTC;
|
||||||
if ($iDateTimeStampInUTC) {
|
if ($iDateTimeStampInUTC) {
|
||||||
|
|
@ -103,20 +101,9 @@ trait Response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// \MailSo\Mime\EmailCollection
|
|
||||||
foreach (['replyTo','from','to','cc','bcc','sender','deliveredTo'] as $prop) {
|
|
||||||
$aResult[$prop] = $this->responseObject($aResult[$prop], $prop);
|
|
||||||
}
|
|
||||||
|
|
||||||
$sSubject = $aResult['subject'];
|
|
||||||
$aResult['requestHash'] = $this->encodeRawKey(array(
|
|
||||||
'folder' => $aResult['folder'],
|
|
||||||
'uid' => $aResult['uid'],
|
|
||||||
'mimeType' => 'message/rfc822',
|
|
||||||
'fileName' => (\strlen($sSubject) ? \MailSo\Base\Utils::SecureFileName($sSubject) : 'message-'.$aResult['uid']) . '.eml'
|
|
||||||
));
|
|
||||||
|
|
||||||
if (!$sParent) {
|
if (!$sParent) {
|
||||||
|
$oAccount = $this->getAccountFromToken();
|
||||||
|
|
||||||
$aResult['draftInfo'] = $mResponse->DraftInfo;
|
$aResult['draftInfo'] = $mResponse->DraftInfo;
|
||||||
$aResult['unsubsribeLinks'] = $mResponse->UnsubsribeLinks;
|
$aResult['unsubsribeLinks'] = $mResponse->UnsubsribeLinks;
|
||||||
$aResult['references'] = $mResponse->References;
|
$aResult['references'] = $mResponse->References;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue