mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-06-28 14:55:48 +00:00
Bugfix handling attachments MIME type / content-type as it was broken.
This commit is contained in:
@@ -22,7 +22,8 @@ const
|
||||
ics: 'text/calendar',
|
||||
xml: 'text/xml',
|
||||
json: app+'json',
|
||||
asc: app+'pgp-signature',
|
||||
// asc: app+'pgp-signature',
|
||||
// asc: app+'pgp-keys',
|
||||
p10: app+'pkcs10',
|
||||
p7c: app+'pkcs7-mime',
|
||||
p7m: app+'pkcs7-mime',
|
||||
|
||||
@@ -27,6 +27,7 @@ export class ComposeAttachmentModel extends AbstractModel {
|
||||
fileName: fileName,
|
||||
size: size,
|
||||
tempName: '',
|
||||
type: '', // application/octet-stream
|
||||
|
||||
progress: 0,
|
||||
error: '',
|
||||
@@ -54,7 +55,7 @@ export class ComposeAttachmentModel extends AbstractModel {
|
||||
return null === localSize ? '' : FileInfo.friendlySize(localSize);
|
||||
},
|
||||
|
||||
mimeType: () => FileInfo.getContentType(this.fileName()),
|
||||
mimeType: () => this.type() || FileInfo.getContentType(this.fileName()),
|
||||
fileExt: () => FileInfo.getExtension(this.fileName()),
|
||||
|
||||
iconClass: () => FileInfo.getIconClass(this.fileExt(), this.mimeType())
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
SetSystemFoldersNotification
|
||||
} from 'Common/EnumsUser';
|
||||
|
||||
import { pInt, isArray, arrayLength, forEachObjectEntry } from 'Common/Utils';
|
||||
import { pInt, isArray, arrayLength } from 'Common/Utils';
|
||||
import { encodeHtml, HtmlEditor, htmlToPlain } from 'Common/Html';
|
||||
import { koArrayWithDestroy, addObservablesTo, addComputablesTo, addSubscribablesTo } from 'External/ko';
|
||||
|
||||
@@ -973,28 +973,22 @@ export class ComposePopupView extends AbstractViewPopup {
|
||||
if (arrayLength(downloads)) {
|
||||
Remote.request('MessageUploadAttachments',
|
||||
(iError, oData) => {
|
||||
if (!iError) {
|
||||
forEachObjectEntry(oData.Result, (tempName, id) => {
|
||||
const attachment = this.getAttachmentById(id);
|
||||
if (attachment) {
|
||||
attachment.tempName(tempName);
|
||||
attachment
|
||||
.waiting(false)
|
||||
.uploading(false)
|
||||
.complete(true);
|
||||
const result = oData?.Result;
|
||||
downloads.forEach((id, index) => {
|
||||
const attachment = this.getAttachmentById(id);
|
||||
if (attachment) {
|
||||
attachment
|
||||
.waiting(false)
|
||||
.uploading(false)
|
||||
.complete(true);
|
||||
if (iError || !result?.[index]) {
|
||||
attachment.error(getUploadErrorDescByCode(UploadErrorCode.NoFileUploaded));
|
||||
} else {
|
||||
attachment.tempName(result[index].TempName);
|
||||
attachment.type(result[index].MimeType);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.attachments.forEach(attachment => {
|
||||
if (attachment?.fromMessage) {
|
||||
attachment
|
||||
.waiting(false)
|
||||
.uploading(false)
|
||||
.complete(true)
|
||||
.error(getUploadErrorDescByCode(UploadErrorCode.NoFileUploaded));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
{
|
||||
Attachments: downloads
|
||||
@@ -1123,6 +1117,7 @@ export class ComposePopupView extends AbstractViewPopup {
|
||||
attachment.size(attachmentJson.Size ? pInt(attachmentJson.Size) : 0);
|
||||
attachment.tempName(attachmentJson.TempName ? attachmentJson.TempName : '');
|
||||
attachment.isInline = false;
|
||||
attachment.type(attachmentJson.MimeType);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1178,20 +1173,6 @@ export class ComposePopupView extends AbstractViewPopup {
|
||||
return this.attachments.find(item => item && id === item.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Object}
|
||||
*/
|
||||
prepareAttachmentsForSendOrSave() {
|
||||
const result = {};
|
||||
this.attachments.forEach(item => {
|
||||
if (item?.complete() && item?.tempName() && item?.enabled()) {
|
||||
result[item.tempName()] = [item.fileName(), item.isInline ? '1' : '0', item.CID, item.contentLocation];
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MessageModel} message
|
||||
*/
|
||||
@@ -1237,6 +1218,7 @@ export class ComposePopupView extends AbstractViewPopup {
|
||||
if (message) {
|
||||
let reply = [ComposeType.Reply, ComposeType.ReplyAll].includes(type);
|
||||
if (reply || [ComposeType.Forward, ComposeType.Draft, ComposeType.EditAsNew].includes(type)) {
|
||||
// item instanceof AttachmentModel
|
||||
message.attachments.forEach(item => {
|
||||
if (!reply || item.isLinked()) {
|
||||
const attachment = new ComposeAttachmentModel(
|
||||
@@ -1249,6 +1231,7 @@ export class ComposePopupView extends AbstractViewPopup {
|
||||
item.contentLocation
|
||||
);
|
||||
attachment.fromMessage = true;
|
||||
attachment.type(item.mimeType);
|
||||
this.addAttachment(attachment);
|
||||
}
|
||||
});
|
||||
@@ -1405,6 +1388,20 @@ export class ComposePopupView extends AbstractViewPopup {
|
||||
|
||||
async getMessageRequestParams(sSaveFolder, draft)
|
||||
{
|
||||
// Prepare ComposeAttachmentModel attachments
|
||||
const attachments = {};
|
||||
this.attachments.forEach(item => {
|
||||
if (item?.complete() && item?.tempName() && item?.enabled()) {
|
||||
attachments[item.tempName()] = {
|
||||
name: item.fileName(),
|
||||
inline: item.isInline,
|
||||
cid: item.CID,
|
||||
location: item.contentLocation,
|
||||
type: item.mimeType()
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const
|
||||
identity = this.currentIdentity(),
|
||||
params = {
|
||||
@@ -1422,7 +1419,7 @@ export class ComposePopupView extends AbstractViewPopup {
|
||||
InReplyTo: this.sInReplyTo,
|
||||
References: this.sReferences,
|
||||
MarkAsImportant: this.markAsImportant() ? 1 : 0,
|
||||
Attachments: this.prepareAttachmentsForSendOrSave(),
|
||||
Attachments: attachments,
|
||||
// Only used at send, not at save:
|
||||
Dsn: this.requestDsn() ? 1 : 0,
|
||||
ReadReceiptRequest: this.requestReadReceipt() ? 1 : 0
|
||||
|
||||
@@ -69,8 +69,8 @@ export class OpenPgpImportPopupView extends AbstractViewPopup {
|
||||
this.close();
|
||||
}
|
||||
|
||||
onShow() {
|
||||
this.key('');
|
||||
onShow(key) {
|
||||
this.key(key || '');
|
||||
this.keyError(false);
|
||||
this.keyErrorMessage('');
|
||||
}
|
||||
|
||||
@@ -55,10 +55,17 @@ import { MimeToMessage } from 'Mime/Utils';
|
||||
|
||||
import { MessageModel } from 'Model/Message';
|
||||
|
||||
import { showScreenPopup } from 'Knoin/Knoin';
|
||||
import { OpenPgpImportPopupView } from 'View/Popup/OpenPgpImport';
|
||||
import { GnuPGUserStore } from 'Stores/User/GnuPG';
|
||||
import { OpenPGPUserStore } from 'Stores/User/OpenPGP';
|
||||
|
||||
const
|
||||
oMessageScrollerDom = () => elementById('messageItem') || {},
|
||||
|
||||
currentMessage = MessageUserStore.message;
|
||||
currentMessage = MessageUserStore.message,
|
||||
|
||||
fetchRaw = url => rl.fetch(url).then(response => response.ok && response.text());
|
||||
|
||||
export class MailMessageView extends AbstractViewRight {
|
||||
constructor() {
|
||||
@@ -284,22 +291,23 @@ export class MailMessageView extends AbstractViewRight {
|
||||
|
||||
el = eqs(event, '.attachmentsPlace .attachmentName');
|
||||
if (el) {
|
||||
const attachment = ko.dataFor(el);
|
||||
if (attachment?.linkDownload()) {
|
||||
if ('message/rfc822' == attachment.mimeType) {
|
||||
const attachment = ko.dataFor(el), url = attachment?.linkDownload();
|
||||
if (url) {
|
||||
if ('application/pgp-keys' == attachment.mimeType
|
||||
&& (OpenPGPUserStore.isSupported() || GnuPGUserStore.isSupported())) {
|
||||
fetchRaw(url).then(text =>
|
||||
showScreenPopup(OpenPgpImportPopupView, [text])
|
||||
);
|
||||
} else if ('message/rfc822' == attachment.mimeType) {
|
||||
// TODO
|
||||
rl.fetch(attachment.linkDownload()).then(response => {
|
||||
if (response.ok) {
|
||||
response.text().then(text => {
|
||||
const oMessage = new MessageModel();
|
||||
MimeToMessage(text, oMessage);
|
||||
// cleanHTML
|
||||
oMessage.viewPopupMessage();
|
||||
});
|
||||
}
|
||||
fetchRaw(url).then(text => {
|
||||
const oMessage = new MessageModel();
|
||||
MimeToMessage(text, oMessage);
|
||||
// cleanHTML
|
||||
oMessage.viewPopupMessage();
|
||||
});
|
||||
} else {
|
||||
download(attachment.linkDownload(), attachment.fileName);
|
||||
download(url, attachment.fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,180 +501,6 @@ abstract class Utils
|
||||
return false === $iLast ? '' : \strtolower(\substr($sFileName, $iLast + 1));
|
||||
}
|
||||
|
||||
public static function MimeContentType(string $sFileName) : string
|
||||
{
|
||||
$sResult = 'application/octet-stream';
|
||||
$sFileName = \trim(\strtolower($sFileName));
|
||||
|
||||
if ('winmail.dat' === $sFileName)
|
||||
{
|
||||
return 'application/ms-tnef';
|
||||
}
|
||||
|
||||
$aMimeTypes = array(
|
||||
|
||||
'eml' => 'message/rfc822',
|
||||
'mime' => 'message/rfc822',
|
||||
'txt' => 'text/plain',
|
||||
'text' => 'text/plain',
|
||||
'def' => 'text/plain',
|
||||
'list' => 'text/plain',
|
||||
'in' => 'text/plain',
|
||||
'ini' => 'text/plain',
|
||||
'log' => 'text/plain',
|
||||
'sql' => 'text/plain',
|
||||
'cfg' => 'text/plain',
|
||||
'conf' => 'text/plain',
|
||||
'rtx' => 'text/richtext',
|
||||
'vcard' => 'text/vcard',
|
||||
'vcf' => 'text/vcard',
|
||||
'htm' => 'text/html',
|
||||
'html' => 'text/html',
|
||||
'csv' => 'text/csv',
|
||||
'ics' => 'text/calendar',
|
||||
'ifb' => 'text/calendar',
|
||||
'xml' => 'text/xml',
|
||||
'json' => 'application/json',
|
||||
'swf' => 'application/x-shockwave-flash',
|
||||
'hlp' => 'application/winhlp',
|
||||
'wgt' => 'application/widget',
|
||||
'chm' => 'application/vnd.ms-htmlhelp',
|
||||
'asc' => 'application/pgp-signature',
|
||||
'p10' => 'application/pkcs10',
|
||||
'p7c' => 'application/pkcs7-mime',
|
||||
'p7m' => 'application/pkcs7-mime',
|
||||
'p7s' => 'application/pkcs7-signature',
|
||||
'torrent' => 'application/x-bittorrent',
|
||||
|
||||
// scripts
|
||||
'js' => 'application/javascript',
|
||||
'pl' => 'text/perl',
|
||||
'css' => 'text/css',
|
||||
'asp' => 'text/asp',
|
||||
'php' => 'application/x-php',
|
||||
|
||||
// images
|
||||
'png' => 'image/png',
|
||||
'jpg' => 'image/jpeg',
|
||||
'jpeg' => 'image/jpeg',
|
||||
'jpe' => 'image/jpeg',
|
||||
'jfif' => 'image/jpeg',
|
||||
'gif' => 'image/gif',
|
||||
'bmp' => 'image/bmp',
|
||||
'cgm' => 'image/cgm',
|
||||
'ief' => 'image/ief',
|
||||
'ico' => 'image/x-icon',
|
||||
'tif' => 'image/tiff',
|
||||
'tiff' => 'image/tiff',
|
||||
'svg' => 'image/svg+xml',
|
||||
'svgz' => 'image/svg+xml',
|
||||
'djv' => 'image/vnd.djvu',
|
||||
'djvu' => 'image/vnd.djvu',
|
||||
'webp' => 'image/webp',
|
||||
|
||||
// archives
|
||||
'zip' => 'application/zip',
|
||||
'7z' => 'application/x-7z-compressed',
|
||||
'rar' => 'application/x-rar-compressed',
|
||||
'exe' => 'application/x-msdownload',
|
||||
'dll' => 'application/x-msdownload',
|
||||
'scr' => 'application/x-msdownload',
|
||||
'com' => 'application/x-msdownload',
|
||||
'bat' => 'application/x-msdownload',
|
||||
'msi' => 'application/x-msdownload',
|
||||
'cab' => 'application/vnd.ms-cab-compressed',
|
||||
'gz' => 'application/x-gzip',
|
||||
'tgz' => 'application/x-gzip',
|
||||
'bz' => 'application/x-bzip',
|
||||
'bz2' => 'application/x-bzip2',
|
||||
'deb' => 'application/x-debian-package',
|
||||
'tar' => 'application/x-tar',
|
||||
|
||||
// fonts
|
||||
'psf' => 'application/x-font-linux-psf',
|
||||
'otf' => 'application/x-font-otf',
|
||||
'pcf' => 'application/x-font-pcf',
|
||||
'snf' => 'application/x-font-snf',
|
||||
'ttf' => 'application/x-font-ttf',
|
||||
'ttc' => 'application/x-font-ttf',
|
||||
|
||||
// audio
|
||||
'aac' => 'audio/aac',
|
||||
'flac' => 'audio/flac',
|
||||
'mp3' => 'audio/mpeg',
|
||||
'aif' => 'audio/aiff',
|
||||
'aifc' => 'audio/aiff',
|
||||
'aiff' => 'audio/aiff',
|
||||
'wav' => 'audio/x-wav',
|
||||
'midi' => 'audio/midi',
|
||||
'mp4a' => 'audio/mp4',
|
||||
'ogg' => 'audio/ogg',
|
||||
'weba' => 'audio/webm',
|
||||
'm3u' => 'audio/x-mpegurl',
|
||||
|
||||
// video
|
||||
'qt' => 'video/quicktime',
|
||||
'mov' => 'video/quicktime',
|
||||
'avi' => 'video/x-msvideo',
|
||||
'mpg' => 'video/mpeg',
|
||||
'mpeg' => 'video/mpeg',
|
||||
'mpe' => 'video/mpeg',
|
||||
'm1v' => 'video/mpeg',
|
||||
'm2v' => 'video/mpeg',
|
||||
'3gp' => 'video/3gpp',
|
||||
'3g2' => 'video/3gpp2',
|
||||
'h261' => 'video/h261',
|
||||
'h263' => 'video/h263',
|
||||
'h264' => 'video/h264',
|
||||
'jpgv' => 'video/jpgv',
|
||||
'mp4' => 'video/mp4',
|
||||
'mp4v' => 'video/mp4',
|
||||
'mpg4' => 'video/mp4',
|
||||
'ogv' => 'video/ogg',
|
||||
'webm' => 'video/webm',
|
||||
'm4v' => 'video/x-m4v',
|
||||
'asf' => 'video/x-ms-asf',
|
||||
'asx' => 'video/x-ms-asf',
|
||||
'wm' => 'video/x-ms-wm',
|
||||
'wmv' => 'video/x-ms-wmv',
|
||||
'wmx' => 'video/x-ms-wmx',
|
||||
'wvx' => 'video/x-ms-wvx',
|
||||
'movie' => 'video/x-sgi-movie',
|
||||
|
||||
// adobe
|
||||
'pdf' => 'application/pdf',
|
||||
'psd' => 'image/vnd.adobe.photoshop',
|
||||
'ai' => 'application/postscript',
|
||||
'eps' => 'application/postscript',
|
||||
'ps' => 'application/postscript',
|
||||
|
||||
// ms office
|
||||
'doc' => 'application/msword',
|
||||
'dot' => 'application/msword',
|
||||
'rtf' => 'application/rtf',
|
||||
'xls' => 'application/vnd.ms-excel',
|
||||
'ppt' => 'application/vnd.ms-powerpoint',
|
||||
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
||||
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
|
||||
// open office
|
||||
'odt' => 'application/vnd.oasis.opendocument.text',
|
||||
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
|
||||
'odp' => 'application/vnd.oasis.opendocument.presentation'
|
||||
|
||||
);
|
||||
|
||||
$sExt = static::GetFileExtension($sFileName);
|
||||
if (\strlen($sExt) && isset($aMimeTypes[$sExt]))
|
||||
{
|
||||
$sResult = $aMimeTypes[$sExt];
|
||||
}
|
||||
|
||||
return $sResult;
|
||||
}
|
||||
|
||||
public static function ContentTypeType(string $sContentType, string $sFileName) : string
|
||||
{
|
||||
$sContentType = \strtolower($sContentType);
|
||||
@@ -695,6 +521,7 @@ abstract class Utils
|
||||
case 'application/x-bzip2':
|
||||
case 'application/x-debian-package':
|
||||
case 'application/x-tar':
|
||||
case 'application/gtar':
|
||||
return 'archive';
|
||||
|
||||
case 'application/msword':
|
||||
|
||||
@@ -22,46 +22,28 @@ class Attachment
|
||||
*/
|
||||
private $rResource;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $sFileName;
|
||||
private string $sFileName;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $iFileSize;
|
||||
private int $iFileSize;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $sCID;
|
||||
private string $sCID;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $bIsInline;
|
||||
private bool $bIsInline;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $bIsLinked;
|
||||
private bool $bIsLinked;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $aCustomContentTypeParams;
|
||||
private array $aCustomContentTypeParams;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $sContentLocation;
|
||||
private string $sContentLocation;
|
||||
|
||||
private string $sContentType;
|
||||
|
||||
/**
|
||||
* @param resource $rResource
|
||||
*/
|
||||
function __construct($rResource, string $sFileName, int $iFileSize, bool $bIsInline,
|
||||
bool $bIsLinked, string $sCID, array $aCustomContentTypeParams = [], string $sContentLocation = '')
|
||||
bool $bIsLinked, string $sCID, array $aCustomContentTypeParams = [],
|
||||
string $sContentLocation = '', string $sContentType = '')
|
||||
{
|
||||
$this->rResource = $rResource;
|
||||
$this->sFileName = $sFileName;
|
||||
@@ -71,6 +53,10 @@ class Attachment
|
||||
$this->sCID = $sCID;
|
||||
$this->aCustomContentTypeParams = $aCustomContentTypeParams;
|
||||
$this->sContentLocation = $sContentLocation;
|
||||
$this->sContentType = $sContentType
|
||||
?: \SnappyMail\File\MimeType::fromStream($rResource, $sFileName)
|
||||
?: \SnappyMail\File\MimeType::fromFilename($sFileName)
|
||||
?: 'application/octet-stream';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,7 +69,7 @@ class Attachment
|
||||
|
||||
public function ContentType() : string
|
||||
{
|
||||
return \MailSo\Base\Utils::MimeContentType($this->sFileName);
|
||||
return $this->sContentType;
|
||||
}
|
||||
|
||||
public function CustomContentTypeParams() : array
|
||||
|
||||
@@ -997,18 +997,23 @@ class Actions
|
||||
|
||||
if ($oAccount && UPLOAD_ERR_OK === $iError && \is_array($aFile)) {
|
||||
$sSavedName = 'upload-post-' . \md5($aFile['name'] . $aFile['tmp_name']);
|
||||
|
||||
// Detect content-type
|
||||
$type = \SnappyMail\File\MimeType::fromFile($aFile['tmp_name'], $aFile['name'])
|
||||
?: \SnappyMail\File\MimeType::fromFilename($aFile['name']);
|
||||
if ($type) {
|
||||
$aFile['type'] = $type;
|
||||
$sSavedName .= \SnappyMail\File\MimeType::toExtension($type);
|
||||
}
|
||||
|
||||
if (!$this->FilesProvider()->MoveUploadedFile($oAccount, $sSavedName, $aFile['tmp_name'])) {
|
||||
$iError = Enumerations\UploadError::ON_SAVING;
|
||||
} else {
|
||||
$sUploadName = $aFile['name'];
|
||||
$iSize = $aFile['size'];
|
||||
$sMimeType = $aFile['type'];
|
||||
|
||||
$aResponse['Attachment'] = array(
|
||||
'Name' => $sUploadName,
|
||||
'Name' => $aFile['name'],
|
||||
'TempName' => $sSavedName,
|
||||
'MimeType' => $sMimeType,
|
||||
'Size' => (int)$iSize
|
||||
'MimeType' => $aFile['type'],
|
||||
'Size' => (int) $aFile['size']
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1041,9 +1046,12 @@ class Actions
|
||||
$iError = $this->GetActionParam('Error', Enumerations\UploadError::UNKNOWN);
|
||||
|
||||
if ($oAccount && UPLOAD_ERR_OK === $iError && \is_array($aFile)) {
|
||||
$sMimeType = \strtolower(\MailSo\Base\Utils::MimeContentType($aFile['name']));
|
||||
if (\in_array($sMimeType, array('image/png', 'image/jpg', 'image/jpeg'))) {
|
||||
$sSavedName = 'upload-post-' . \md5($aFile['name'] . $aFile['tmp_name']);
|
||||
$sMimeType = \SnappyMail\File\MimeType::fromFile($aFile['tmp_name'], $aFile['name'])
|
||||
?: \SnappyMail\File\MimeType::fromFilename($aFile['name'])
|
||||
?: $aFile['type'];
|
||||
if (\in_array($sMimeType, array('image/png', 'image/jpg', 'image/jpeg', 'image/webp'))) {
|
||||
$sSavedName = 'upload-post-' . \md5($aFile['name'] . $aFile['tmp_name'])
|
||||
. \SnappyMail\File\MimeType::toExtension($sContentType);
|
||||
if (!$this->FilesProvider()->MoveUploadedFile($oAccount, $sSavedName, $aFile['tmp_name'])) {
|
||||
$iError = Enumerations\UploadError::ON_SAVING;
|
||||
} else {
|
||||
|
||||
@@ -603,37 +603,53 @@ trait Messages
|
||||
try
|
||||
{
|
||||
$aAttachments = $this->GetActionParam('Attachments', array());
|
||||
if (\is_array($aAttachments) && \count($aAttachments))
|
||||
{
|
||||
$mResult = array();
|
||||
foreach ($aAttachments as $sAttachment)
|
||||
{
|
||||
if ($aValues = \RainLoop\Utils::DecodeKeyValuesQ($sAttachment))
|
||||
{
|
||||
if (!\is_array($aAttachments)) {
|
||||
$aAttachments = [];
|
||||
}
|
||||
if (\count($aAttachments)) {
|
||||
$oFilesProvider = $this->FilesProvider();
|
||||
foreach ($aAttachments as $mIndex => $sAttachment) {
|
||||
$aAttachments[$mIndex] = false;
|
||||
if ($aValues = \RainLoop\Utils::DecodeKeyValuesQ($sAttachment)) {
|
||||
$sFolder = isset($aValues['Folder']) ? (string) $aValues['Folder'] : '';
|
||||
$iUid = isset($aValues['Uid']) ? (int) $aValues['Uid'] : 0;
|
||||
$sMimeIndex = isset($aValues['MimeIndex']) ? (string) $aValues['MimeIndex'] : '';
|
||||
|
||||
$sTempName = \md5($sAttachment);
|
||||
if (!$this->FilesProvider()->FileExists($oAccount, $sTempName))
|
||||
{
|
||||
$sTempName = \sha1($sAttachment);
|
||||
if (!$oFilesProvider->FileExists($oAccount, $sTempName)) {
|
||||
$this->MailClient()->MessageMimeStream(
|
||||
function($rResource, $sContentType, $sFileName, $sMimeIndex = '') use ($oAccount, &$mResult, $sTempName, $sAttachment, $self) {
|
||||
if (is_resource($rResource))
|
||||
{
|
||||
$sContentType = (empty($sFileName)) ? 'text/plain' : \MailSo\Base\Utils::MimeContentType($sFileName);
|
||||
$sFileName = $self->MainClearFileName($sFileName, $sContentType, $sMimeIndex);
|
||||
function($rResource, $sContentType, $sFileName, $sMimeIndex = '') use ($oAccount, $sTempName, $self, &$aAttachments, $mIndex) {
|
||||
if (\is_resource($rResource)) {
|
||||
$sContentType =
|
||||
$sContentType
|
||||
?: \SnappyMail\File\MimeType::fromStream($rResource, $sFileName)
|
||||
?: \SnappyMail\File\MimeType::fromFilename($sFileName)
|
||||
?: 'application/octet-stream'; // 'text/plain'
|
||||
|
||||
if ($self->FilesProvider()->PutFile($oAccount, $sTempName, $rResource))
|
||||
{
|
||||
$mResult[$sTempName] = $sAttachment;
|
||||
// $sFileName = $self->MainClearFileName($sFileName, $sContentType, $sMimeIndex);
|
||||
$sTempName .= \SnappyMail\File\MimeType::toExtension($sContentType);
|
||||
|
||||
if ($self->FilesProvider()->PutFile($oAccount, $sTempName, $rResource)) {
|
||||
$aAttachments[$mIndex] = [
|
||||
// 'Name' => $sFileName,
|
||||
'TempName' => $sTempName,
|
||||
'MimeType' => $sContentType
|
||||
// 'Size' => 0
|
||||
];
|
||||
}
|
||||
}
|
||||
}, $sFolder, $iUid, $sMimeIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
$mResult[$sTempName] = $sAttachment;
|
||||
} else {
|
||||
$rResource = $oFilesProvider->GetFile($oAccount, $sTempName);
|
||||
$sContentType = \SnappyMail\File\MimeType::fromStream($rResource, $sTempName)
|
||||
?: \SnappyMail\File\MimeType::fromFilename($sTempName)
|
||||
?: 'application/octet-stream'; // 'text/plain'
|
||||
$aAttachments[$mIndex] = [
|
||||
// 'Name' => $sFileName,
|
||||
'TempName' => $sTempName,
|
||||
'MimeType' => $sContentType
|
||||
// 'Size' => $oFilesProvider->FileSize($oAccount, $sTempName)
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -644,7 +660,7 @@ trait Messages
|
||||
throw new ClientException(Notifications::MailServerError, $oException);
|
||||
}
|
||||
|
||||
return $this->DefaultResponse(__FUNCTION__, $mResult);
|
||||
return $this->DefaultResponse(__FUNCTION__, $aAttachments);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1231,10 +1247,11 @@ trait Messages
|
||||
{
|
||||
foreach ($aAttachments as $sTempName => $aData)
|
||||
{
|
||||
$sFileName = (string) $aData[0];
|
||||
$bIsInline = (bool) $aData[1];
|
||||
$sCID = (string) $aData[2];
|
||||
$sContentLocation = isset($aData[3]) ? (string) $aData[3] : '';
|
||||
$sFileName = (string) $aData['name'];
|
||||
$bIsInline = (bool) $aData['inline'];
|
||||
$sCID = (string) $aData['cid'];
|
||||
$sContentLocation = (string) $aData['location'];
|
||||
$sMimeType = (string) $aData['type'];
|
||||
|
||||
$rResource = $this->FilesProvider()->GetFile($oAccount, $sTempName);
|
||||
if (\is_resource($rResource))
|
||||
@@ -1244,7 +1261,7 @@ trait Messages
|
||||
$oMessage->Attachments()->append(
|
||||
new \MailSo\Mime\Attachment($rResource, $sFileName, $iFileSize, $bIsInline,
|
||||
\in_array(trim(trim($sCID), '<>'), $aFoundCids),
|
||||
$sCID, array(), $sContentLocation
|
||||
$sCID, array(), $sContentLocation, $sMimeType
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -149,7 +149,9 @@ trait Raw
|
||||
if ('.pdf' === \substr($sFileNameIn,-4)) {
|
||||
$sContentTypeOut = 'application/pdf'; // application/octet-stream
|
||||
} else {
|
||||
$sContentTypeOut = $sContentTypeIn ?: \MailSo\Base\Utils::MimeContentType($sFileNameIn);
|
||||
$sContentTypeOut = $sContentTypeIn
|
||||
?: \SnappyMail\File\MimeType::fromFilename($sFileNameIn)
|
||||
?: 'application/octet-stream';
|
||||
}
|
||||
|
||||
$sFileNameOut = $this->MainClearFileName($sFileNameIn, $sContentTypeIn, $sMimeIndex);
|
||||
@@ -197,10 +199,12 @@ trait Raw
|
||||
if ('.pdf' === \substr($sFileName, -4)) {
|
||||
// https://github.com/the-djmaze/snappymail/issues/144
|
||||
$sContentType = 'application/pdf';
|
||||
} else if ($sContentTypeIn) {
|
||||
$sContentType = $sContentTypeIn;
|
||||
} else if (!$sContentType) {
|
||||
$sContentType = $sFileName ? \MailSo\Base\Utils::MimeContentType($sFileName) : 'text/plain';
|
||||
} else {
|
||||
$sContentType = $sContentTypeIn
|
||||
?: $sContentType
|
||||
// ?: \SnappyMail\File\MimeType::fromStream($rResource, $sFileName)
|
||||
?: \SnappyMail\File\MimeType::fromFilename($sFileName)
|
||||
?: 'application/octet-stream';
|
||||
}
|
||||
|
||||
if (!$bDownload)
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
$magic = array(
|
||||
'#^\x37\x7A.*#s' => 'application/x-7z-compressed', # unknown by magic.mime
|
||||
'#^BZh.*#s' => 'application/x-bzip2',
|
||||
'#^\x1f\x8b.*#s' => 'application/x-gzip',
|
||||
'#^.{257}ustar \x00.*#s' => 'application/x-gtar',
|
||||
'#^.{257}ustar\x00.*#s' => 'application/x-tar',
|
||||
'#^Rar!.*#s' => 'application/x-rar-compressed',
|
||||
'#^PK\x03\x04.*#s' => 'application/zip',
|
||||
|
||||
'#^%PDF-.*#s' => 'application/pdf',
|
||||
'#^[CF]WS.*#s' => 'application/x-shockwave-flash',
|
||||
|
||||
'#^(.*\n)?(INSERT|CREATE|DROP|DELETE|ALTER|UPDATE)\ .*#is' => 'text/x-sql',
|
||||
'#^BEGIN:VCARD#s' => 'text/x-vcard',
|
||||
|
||||
'#^GIF8.*#s' => 'image/gif',
|
||||
'#^.{8}heic#s' => 'image/heic', // https://nokiatech.github.io/heif/technical.html
|
||||
'#^\xFF\xD8.*#s' => 'image/jpeg',
|
||||
'#^\x89PNG.*#s' => 'image/png',
|
||||
'#^.PNG.*#s' => 'image/png',
|
||||
'#^<svg.*#s' => 'image/svg+xml', # wild guess, unknown by magic.mime
|
||||
'#^RIFF.{4}WEBP#s' => 'image/webp',
|
||||
|
||||
'#^FLV.*#s' => 'video/x-flv',
|
||||
'#^OggS.+\x80theora.*#s' => 'video/ogg',
|
||||
|
||||
'#^ID3.*#s' => 'audio/mpeg', # wild guess?
|
||||
'#^\xff\xfa.*#s' => 'audio/mpeg', # wild guess?
|
||||
'#^OggS.+\x01vorbis.*#s' => 'audio/ogg',
|
||||
|
||||
'#^\xD0\xCF\x11\xE0\xA1.*#s' => 'application/msword',
|
||||
'#^OggS.*#s' => 'application/ogg',
|
||||
);
|
||||
276
snappymail/v/0.0.0/app/libraries/snappymail/file/mimetype.php
Normal file
276
snappymail/v/0.0.0/app/libraries/snappymail/file/mimetype.php
Normal file
@@ -0,0 +1,276 @@
|
||||
<?php
|
||||
|
||||
namespace SnappyMail\File;
|
||||
|
||||
// use MailSo\Mime\Enumerations\MimeType;
|
||||
|
||||
abstract class MimeType
|
||||
{
|
||||
protected static $finfo = null;
|
||||
|
||||
protected static function initFInfo() : void
|
||||
{
|
||||
if (null === self::$finfo) {
|
||||
// if (!\getenv('MAGIC')) { \putenv('MAGIC='.__DIR__.'/magic.mime'.(WINDOWS_OS?'.win32':'')); } # /usr/share/misc/magic
|
||||
self::$finfo = \class_exists('finfo', false) ? new \finfo(FILEINFO_MIME) : false; // FILEINFO_CONTINUE
|
||||
}
|
||||
}
|
||||
|
||||
protected static function detectDeeper(string $mime, string $name = '') : string
|
||||
{
|
||||
if ('application/ogg' === $mime
|
||||
|| 'application/vnd.ms-office' === $mime
|
||||
|| 'application/octet-stream' === $mime
|
||||
|| 'application/zip' === $mime) {
|
||||
return static::fromFilename($name) ?: $mime;
|
||||
}
|
||||
return $mime;
|
||||
}
|
||||
|
||||
public static function fromFile(string $filename, string $name = '') : ?string
|
||||
{
|
||||
$mime = null;
|
||||
if (\is_file($filename)) {
|
||||
static::initFInfo();
|
||||
if (self::$finfo) {
|
||||
$mime = \preg_replace('#[,;].*#', '', self::$finfo->file($filename));
|
||||
}
|
||||
if (!$mime && $fp = \fopen($filename, 'rb')) {
|
||||
$mime = self::fromStream($fp);
|
||||
\fclose($fp);
|
||||
}
|
||||
if ('application/zip' === \str_replace('/x-', '/', $mime)) {
|
||||
$zip = new \ZipArchive($filename);
|
||||
if (false !== $zip->locateName('word/_rels/document.xml.rels')) {
|
||||
return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
|
||||
}
|
||||
if (false !== $zip->locateName('xl/_rels/workbook.xml.rels')) {
|
||||
return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
||||
}
|
||||
}
|
||||
}
|
||||
return $mime ? static::detectDeeper($mime, $name ?: $filename) : null;
|
||||
}
|
||||
|
||||
public static function fromStream($stream, string $name = '') : ?string
|
||||
{
|
||||
if (\is_resource($stream) && \stream_get_meta_data($stream)['seekable']) {
|
||||
$pos = \ftell($stream);
|
||||
// if (\is_int($pos) && \rewind($stream)) {
|
||||
if (\is_int($pos) && 0 === \fseek($stream, 0)) {
|
||||
// $str = \fread($stream, 265);
|
||||
$str = \stream_get_contents($stream, 265, 0);
|
||||
\fseek($stream, $pos);
|
||||
if ($str) {
|
||||
return static::fromString($str, $name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function fromString(string &$str, string $name = '') : ?string
|
||||
{
|
||||
static::initFInfo();
|
||||
$mime = self::$finfo
|
||||
? \preg_replace('#[,;].*#', '', self::$finfo->buffer($str))
|
||||
: self::getFromData($str);
|
||||
return $mime ? static::detectDeeper($mime, $name) : null;
|
||||
}
|
||||
|
||||
protected static function getFromData(string $str) : ?string
|
||||
{
|
||||
if (\str_contains($str, '-----BEGIN PGP SIGNATURE-----')) {
|
||||
return 'application/pgp-signature';
|
||||
}
|
||||
if (\preg_match('/-----BEGIN PGP (PUBLIC|PRIVATE) KEY BLOCK-----/', $str)) {
|
||||
return 'application/pgp-keys';
|
||||
}
|
||||
static $magic;
|
||||
if (!$magic) {
|
||||
require __DIR__ . '/magic.mime.php';
|
||||
}
|
||||
$str = \preg_replace(\array_keys($magic), \array_values($magic), $str, 1, $c);
|
||||
return $c ? $str : null;
|
||||
}
|
||||
|
||||
public static function fromFilename(string $filename) : ?string
|
||||
{
|
||||
$filename = \strtolower($filename);
|
||||
if ('winmail.dat' === $filename) {
|
||||
return 'application/ms-tnef';
|
||||
}
|
||||
$extension = \explode('.', $filename);
|
||||
$extension = \array_pop($extension);
|
||||
return isset(static::$types[$extension]) ? static::$types[$extension] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue with 'text/plain'
|
||||
*/
|
||||
public static function toExtension(string $mime, bool $include_dot = true) : ?string
|
||||
{
|
||||
$mime = \strtolower($mime);
|
||||
if ('application/pgp-signature' == $mime || 'application/pgp-keys' == $mime) {
|
||||
$ext = 'asc';
|
||||
} else {
|
||||
$mime = \str_replace('application/x-tar', 'application/gtar', $mime);
|
||||
$ext = \array_search($mime, static::$types)
|
||||
?: \array_search(\str_replace('/x-', '/', $mime), static::$types)
|
||||
?: \array_search(\str_replace('/', '/x-', $mime), static::$types)
|
||||
?: 'bin';
|
||||
}
|
||||
return ($include_dot ? '.' : '') . $ext;
|
||||
}
|
||||
|
||||
protected static $types = [
|
||||
'7z' => 'application/x-7z-compressed',
|
||||
'ai' => 'application/postscript',
|
||||
// 'asc' => 'application/pgp-signature',
|
||||
// 'asc' => 'application/pgp-keys',
|
||||
'bat' => 'application/x-msdownload',
|
||||
'bz' => 'application/x-bzip',
|
||||
'bz2' => 'application/x-bzip2',
|
||||
'cab' => 'application/vnd.ms-cab-compressed',
|
||||
'chm' => 'application/vnd.ms-htmlhelp',
|
||||
'com' => 'application/x-msdownload',
|
||||
'deb' => 'application/x-debian-package',
|
||||
'dll' => 'application/x-msdownload',
|
||||
'doc' => 'application/msword',
|
||||
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'dot' => 'application/msword',
|
||||
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
||||
'eps' => 'application/postscript',
|
||||
'epub' => 'application/epub',
|
||||
'exe' => 'application/x-msdownload',
|
||||
'gz' => 'application/gzip',
|
||||
'gz' => 'application/x-gzip',
|
||||
'hlp' => 'application/winhlp',
|
||||
'js' => 'application/javascript',
|
||||
'json' => 'application/json',
|
||||
'msi' => 'application/x-msdownload',
|
||||
'odp' => 'application/vnd.oasis.opendocument.presentation',
|
||||
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
|
||||
'odt' => 'application/vnd.oasis.opendocument.text',
|
||||
'ogx' => 'application/ogg',
|
||||
'p10' => 'application/pkcs10',
|
||||
'p7c' => 'application/pkcs7-mime',
|
||||
'p7m' => 'application/pkcs7-mime',
|
||||
'p7s' => 'application/pkcs7-signature',
|
||||
'pdf' => 'application/pdf',
|
||||
'php' => 'application/x-httpd-php',
|
||||
'php' => 'application/x-php',
|
||||
'ppt' => 'application/vnd.ms-powerpoint',
|
||||
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
'ps' => 'application/postscript',
|
||||
'psd' => 'image/vnd.adobe.photoshop',
|
||||
'rar' => 'application/rar-compressed',
|
||||
'rar' => 'application/x-rar-compressed',
|
||||
'rtf' => 'application/rtf',
|
||||
'scr' => 'application/x-msdownload',
|
||||
'sql' => 'application/sql',
|
||||
'swf' => 'application/shockwave-flash',
|
||||
'swf' => 'application/x-shockwave-flash',
|
||||
'tar' => 'application/gtar',
|
||||
// 'tar' => 'application/x-tar',
|
||||
// 'tgz' => 'application/x-gzip',
|
||||
'torrent' => 'application/x-bittorrent',
|
||||
'wgt' => 'application/widget',
|
||||
'xls' => 'application/vnd.ms-excel',
|
||||
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
|
||||
'zip' => 'application/zip',
|
||||
|
||||
'aac' => 'audio/aac',
|
||||
'aif' => 'audio/aiff',
|
||||
'aifc' => 'audio/aiff',
|
||||
'aiff' => 'audio/aiff',
|
||||
'flac' => 'audio/flac',
|
||||
'm3u' => 'audio/x-mpegurl',
|
||||
'midi' => 'audio/midi',
|
||||
'mp3' => 'audio/mpeg',
|
||||
'mp4a' => 'audio/mp4',
|
||||
'ogg' => 'audio/ogg',
|
||||
'wav' => 'audio/wav',
|
||||
'weba' => 'audio/webm',
|
||||
|
||||
'ttf' => 'font/ttf',
|
||||
'woff' => 'font/woff',
|
||||
'woff2' => 'font/woff2',
|
||||
|
||||
'bmp' => 'image/bmp',
|
||||
'cgm' => 'image/cgm',
|
||||
'djv' => 'image/vnd.djvu',
|
||||
'djvu' => 'image/vnd.djvu',
|
||||
'gif' => 'image/gif',
|
||||
// 'heic' => 'image/heic',
|
||||
'ico' => 'image/vnd.microsoft.icon',
|
||||
// 'ico' => 'image/x-icon',
|
||||
'ief' => 'image/ief',
|
||||
'jpeg' => 'image/jpeg',
|
||||
'jfif' => 'image/jpeg',
|
||||
'jpe' => 'image/jpeg',
|
||||
'jpg' => 'image/jpeg',
|
||||
'png' => 'image/png',
|
||||
'svg' => 'image/svg+xml',
|
||||
'svgz' => 'image/svg+xml',
|
||||
'tiff' => 'image/tiff',
|
||||
'tif' => 'image/tiff',
|
||||
'webp' => 'image/webp',
|
||||
|
||||
'eml' => 'message/rfc822',
|
||||
'mime' => 'message/rfc822',
|
||||
|
||||
'txt' => 'text/plain',
|
||||
'asp' => 'text/asp',
|
||||
'cfg' => 'text/plain',
|
||||
'conf' => 'text/plain',
|
||||
'css' => 'text/css',
|
||||
'csv' => 'text/csv',
|
||||
'def' => 'text/plain',
|
||||
'html' => 'text/html',
|
||||
'htm' => 'text/html',
|
||||
'ics' => 'text/calendar',
|
||||
'ifb' => 'text/calendar',
|
||||
'in' => 'text/plain',
|
||||
'ini' => 'text/plain',
|
||||
'list' => 'text/plain',
|
||||
'log' => 'text/plain',
|
||||
'pl' => 'text/perl',
|
||||
'rtx' => 'text/richtext',
|
||||
'text' => 'text/plain',
|
||||
'vcf' => 'text/vcard',
|
||||
'vcard' => 'text/vcard',
|
||||
'xml' => 'text/xml',
|
||||
|
||||
'3g2' => 'video/3gpp2',
|
||||
'3gp' => 'video/3gpp',
|
||||
'asf' => 'video/x-ms-asf',
|
||||
'asx' => 'video/x-ms-asf',
|
||||
'avi' => 'video/x-msvideo',
|
||||
'flv' => 'video/flv',
|
||||
'h261' => 'video/h261',
|
||||
'h263' => 'video/h263',
|
||||
'h264' => 'video/h264',
|
||||
'jpgv' => 'video/jpgv',
|
||||
'm4v' => 'video/x-m4v',
|
||||
'mov' => 'video/quicktime',
|
||||
'movie' => 'video/x-sgi-movie',
|
||||
'mp4' => 'video/mp4',
|
||||
'mp4v' => 'video/mp4',
|
||||
'mpeg' => 'video/mpeg',
|
||||
'm1v' => 'video/mpeg',
|
||||
'm2v' => 'video/mpeg',
|
||||
'mpe' => 'video/mpeg',
|
||||
'mpg' => 'video/mpeg',
|
||||
'mpg4' => 'video/mp4',
|
||||
'ogv' => 'video/ogg',
|
||||
'qt' => 'video/quicktime',
|
||||
'webm' => 'video/webm',
|
||||
'wm' => 'video/x-ms-wm',
|
||||
'wmv' => 'video/x-ms-wmv',
|
||||
'wmx' => 'video/x-ms-wmx',
|
||||
'wvx' => 'video/x-ms-wvx',
|
||||
];
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user