45 #include "messagecore/settings/globalsettings.h"
46 #include "messagecore/helpers/nodehelper.h"
47 #include "messagecore/utils/stringutil.h"
49 #include "pimcommon/widgets/renamefiledialog.h"
51 #include <akonadi/item.h>
54 #include <kmbox/mbox.h>
56 #include <KMime/Message>
58 #include <kcharsets.h>
59 #include <KFileDialog>
62 #include <kmessagebox.h>
63 #include <kio/netaccess.h>
66 #include <KTemporaryFile>
67 #include <ktoolinvocation.h>
72 #include <QDBusInterface>
73 #include <QDBusConnectionInterface>
74 #include <QActionGroup>
77 using namespace MessageViewer;
81 if ( KIO::NetAccess::exists( url, KIO::NetAccess::DestinationSide, w ) ) {
82 if ( KMessageBox::Cancel == KMessageBox::warningContinueCancel(
84 i18n(
"A file named \"%1\" already exists. "
85 "Are you sure you want to overwrite it?", url.prettyUrl() ),
86 i18n(
"Overwrite File?" ),
87 KStandardGuiItem::overwrite() ) )
94 const QString &fallbackFileName1,
95 const QString &fallbackFileName2 )
98 QString tMimeType = mimeType;
101 if ( mimeType == QLatin1String(
"application/x-vnd.kolab.contact" ) ) {
102 tMimeType = QLatin1String(
"text/x-vcard" );
103 }
else if ( mimeType == QLatin1String(
"application/x-vnd.kolab.event" ) ) {
104 tMimeType = QLatin1String(
"application/x-vnd.akonadi.calendar.event" );
105 }
else if ( mimeType == QLatin1String(
"application/x-vnd.kolab.task" ) ) {
106 tMimeType = QLatin1String(
"application/x-vnd.akonadi.calendar.todo" );
107 }
else if ( mimeType == QLatin1String(
"application/x-vnd.kolab.journal" ) ) {
108 tMimeType = QLatin1String(
"application/x-vnd.akonadi.calendar.journal" );
109 }
else if ( mimeType == QLatin1String(
"application/x-vnd.kolab.note" ) ) {
110 tMimeType = QLatin1String(
"application/x-vnd.akonadi.note" );
112 KMimeType::Ptr mime = KMimeType::mimeType( tMimeType, KMimeType::ResolveAliases );
114 fileName = mime->iconName();
116 fileName = QLatin1String(
"unknown" );
117 if ( !tMimeType.isEmpty() ) {
118 kWarning() <<
"unknown mimetype" << tMimeType;
122 if(fileName == QLatin1String(
"text-vcard")) {
123 fileName = QLatin1String(
"text-x-vcard");
125 if ( fileName.isEmpty() ) {
126 fileName = fallbackFileName1;
127 if ( fileName.isEmpty() ) {
128 fileName = fallbackFileName2;
130 if ( !fileName.isEmpty() ) {
131 fileName = KMimeType::findByPath( QLatin1String(
"/tmp/") + fileName, 0,
true )->iconName();
138 #if defined Q_WS_WIN || defined Q_WS_MACX
139 #include <QDesktopServices>
144 #if defined Q_WS_WIN || defined Q_WS_MACX
145 QDesktopServices::openUrl( url );
155 KMime::Content::List result;
156 KMime::Content *child = MessageCore::NodeHelper::firstChild( message );
161 KMime::Content *next = MessageCore::NodeHelper::nextSibling( message );
172 const KMime::Content::List contents =
allContents( message );
173 KMime::Content::List result;
174 for ( KMime::Content::List::const_iterator it = contents.constBegin();
175 it != contents.constEnd(); ) {
176 KMime::Content* content = *it;
177 if ( content->contentDisposition()->filename().trimmed().isEmpty() &&
178 ( content->contentType()->name().trimmed().isEmpty() ||
179 content == message ) ) {
193 const bool multiple = (contents.count() > 1);
196 dirUrl = KFileDialog::getExistingDirectoryUrl( KUrl( QLatin1String(
"kfiledialog:///saveAttachment") ),
198 i18n(
"Save Attachments To" ) );
199 if ( !dirUrl.isValid() ) {
204 dirUrl.adjustPath( KUrl::AddTrailingSlash );
207 KMime::Content *content = contents.first();
209 fileName = MessageCore::StringUtil::cleanFileName( fileName );
210 if ( fileName.isEmpty() ) {
211 fileName = i18nc(
"filename for an unnamed attachment",
"attachment.1" );
213 KUrl pathUrl = KUrl( QLatin1String(
"kfiledialog:///saveAttachment/"));
214 pathUrl.addPath(fileName);
215 url = KFileDialog::getSaveUrl( pathUrl ,
218 i18n(
"Save Attachment" ) );
219 if ( url.isEmpty() ) {
224 QMap< QString, int > renameNumbering;
226 bool globalResult =
true;
227 int unnamedAtmCount = 0;
228 PimCommon::RenameFileDialog::RenameFileDialogResult result = PimCommon::RenameFileDialog::RENAMEFILE_IGNORE;
229 foreach( KMime::Content *content, contents ) {
231 if ( !dirUrl.isEmpty() ) {
234 fileName = MessageCore::StringUtil::cleanFileName( fileName );
235 if ( fileName.isEmpty() ) {
237 fileName = i18nc(
"filename for the %1-th unnamed attachment",
238 "attachment.%1", unnamedAtmCount );
240 curUrl.setFileName( fileName );
244 if ( !curUrl.isEmpty() ) {
246 if (multiple && (curUrl.fileName() == QLatin1String(
"smime.p7s")) ) {
251 QString origFile = curUrl.fileName();
252 QString file = origFile;
254 while ( renameNumbering.contains(file) ) {
256 int num = renameNumbering[file] + 1;
257 int dotIdx = file.lastIndexOf(QLatin1Char(
'.'));
258 file = file.insert( (dotIdx>=0) ? dotIdx : file.length(), QLatin1String(
"_") + QString::number(num) );
260 curUrl.setFileName(file);
263 if ( !renameNumbering.contains(origFile))
264 renameNumbering[origFile] = 1;
266 renameNumbering[origFile]++;
268 if ( file != origFile ) {
269 if ( !renameNumbering.contains(file))
270 renameNumbering[file] = 1;
272 renameNumbering[file]++;
276 if( !(result == PimCommon::RenameFileDialog::RENAMEFILE_OVERWRITEALL ||
277 result == PimCommon::RenameFileDialog::RENAMEFILE_IGNOREALL )) {
278 if ( KIO::NetAccess::exists( curUrl, KIO::NetAccess::DestinationSide, parent ) ) {
279 PimCommon::RenameFileDialog *dlg =
new PimCommon::RenameFileDialog(curUrl, multiple, parent);
280 result =
static_cast<PimCommon::RenameFileDialog::RenameFileDialogResult
>(dlg->exec());
281 if ( result == PimCommon::RenameFileDialog::RENAMEFILE_IGNORE ||
282 result == PimCommon::RenameFileDialog::RENAMEFILE_IGNOREALL)
287 else if ( result == PimCommon::RenameFileDialog::RENAMEFILE_RENAME )
289 curUrl = dlg->newName();
295 if( result != PimCommon::RenameFileDialog::RENAMEFILE_IGNOREALL ) {
296 const bool result =
saveContent( parent, content, curUrl );
298 globalResult = result;
314 #if 0 // totally broken
315 KMime::Content *topContent = content->topLevel();
317 bool bSaveEncrypted =
false;
319 if( bEncryptedParts )
320 if( KMessageBox::questionYesNo( parent,
321 i18n(
"The part %1 of the message is encrypted. Do you want to keep the encryption when saving?",
323 i18n(
"KMail Question" ), KGuiItem(i18n(
"Keep Encryption")), KGuiItem(i18n(
"Do Not Keep")) ) ==
325 bSaveEncrypted =
true;
327 bool bSaveWithSig =
true;
329 if( KMessageBox::questionYesNo( parent,
330 i18n(
"The part %1 of the message is signed. Do you want to keep the signature when saving?",
332 i18n(
"KMail Question" ), KGuiItem(i18n(
"Keep Signature")), KGuiItem(i18n(
"Do Not Keep")) ) !=
334 bSaveWithSig =
false;
337 if( bSaveEncrypted || !bEncryptedParts) {
338 KMime::Content *dataNode = content;
339 QByteArray rawDecryptedBody;
340 bool gotRawDecryptedBody =
false;
341 if ( !bSaveWithSig ) {
342 if ( topContent->contentType()->mimeType() ==
"multipart/signed" ) {
360 gotRawDecryptedBody =
true;
363 QByteArray cstr = gotRawDecryptedBody
365 : dataNode->decodedContent();
366 data = KMime::CRLFtoLF( cstr );
369 const QByteArray data = content->decodedContent();
370 kWarning() <<
"Port the encryption/signature handling when saving a KMime::Content.";
375 if ( url.isLocalFile() )
378 file.setFileName( url.toLocalFile() );
379 if ( !file.open( QIODevice::WriteOnly ) )
381 KMessageBox::error( parent,
382 i18nc(
"1 = file name, 2 = error string",
383 "<qt>Could not write to the file<br><filename>%1</filename><br><br>%2",
385 file.errorString() ),
386 i18n(
"Error saving attachment" ) );
391 if ( permissions >= 0 )
392 fchmod( file.handle(), permissions );
394 ds.setDevice( &file );
402 const int bytesWritten = ds.writeRawData( data.data(), data.size() );
403 if ( bytesWritten != data.size() ) {
404 QFile *f =
static_cast<QFile *
>( ds.device() );
405 KMessageBox::error( parent,
406 i18nc(
"1 = file name, 2 = error string",
407 "<qt>Could not write to the file<br><filename>%1</filename><br><br>%2",
410 i18n(
"Error saving attachment" ) );
416 if ( !url.isLocalFile() )
419 QString tfName = tf.fileName();
421 if ( !KIO::NetAccess::upload( tfName, url, parent ) )
423 KMessageBox::error( parent,
424 i18nc(
"1 = file name, 2 = error string",
425 "<qt>Could not write to the file<br><filename>%1</filename><br><br>%2",
427 KIO::NetAccess::lastErrorString() ),
428 i18n(
"Error saving attachment" ) );
446 if ( MessageCore::GlobalSettings::self()->disregardUmask() ) {
447 return S_IRUSR | S_IWUSR;
455 if ( contents.isEmpty() ) {
456 KMessageBox::information( parent, i18n(
"Found no attachments to save." ) );
467 if ( retrievedMsgs.isEmpty() )
469 const Akonadi::Item msgBase = retrievedMsgs.first();
471 if( msgBase.hasPayload<KMime::Message::Ptr>() )
474 fileName = i18n(
"message");
476 if ( !fileName.endsWith( QLatin1String(
".mbox" ) ) )
477 fileName += QLatin1String(
".mbox");
479 const QString filter = i18n(
"*.mbox|email messages (*.mbox)\n*|all files (*)" );
480 QPointer<KFileDialog> dlg =
new KFileDialog(KUrl::fromPath( fileName ), filter, parent);
481 dlg->setCaption(i18np(
"Save Message",
"Save Messages", retrievedMsgs.count()));
482 dlg->setMode(KFile::File|KFile::LocalOnly);
483 dlg->setOperationMode(KFileDialog::Saving);
484 if( !appendMessages )
485 dlg->setConfirmOverwrite(
true);
487 KUrl url = dlg->selectedUrl();
488 if ( url.isEmpty() ) {
493 const QString localFileName = url.toLocalFile();
494 if ( localFileName.isEmpty() ) {
499 if( !appendMessages ) {
500 QFile::remove(localFileName);
504 if ( !mbox.load( localFileName ) ) {
505 if( appendMessages ) {
506 KMessageBox::error( parent, i18n(
"File %1 could not be loaded.",localFileName) , i18n(
"Error loading message" ) );
508 KMessageBox::error( parent, i18n(
"File %1 could not be created.",localFileName) , i18n(
"Error saving message" ) );
513 foreach (
const Akonadi::Item &item, retrievedMsgs ) {
514 if ( item.hasPayload<KMime::Message::Ptr>() ) {
515 mbox.appendMessage( item.payload<KMime::Message::Ptr>() );
519 if ( !mbox.save() ) {
520 KMessageBox::error( parent, i18n(
"We cannot save message.") , i18n(
"Error saving message" ) );
536 if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(QLatin1String(
"org.kde.kttsd")))
539 if (KToolInvocation::startServiceByDesktopName(QLatin1String(
"kttsd"), QStringList(), &error))
541 KMessageBox::error(parent, i18n(
"Starting Jovie Text-to-Speech Service Failed"), error );
545 QDBusInterface ktts(QLatin1String(
"org.kde.kttsd"), QLatin1String(
"/KSpeech"), QLatin1String(
"org.kde.KSpeech"));
546 ktts.asyncCall(QLatin1String(
"say"), text, 0);
552 QString actionName(service->name().replace(QLatin1Char(
'&'), QLatin1String(
"&&")));
554 actionName = i18n(
"Open &with %1", actionName);
556 actionName = i18nc(
"@item:inmenu Open With, %1 is application name",
"%1", actionName);
559 KAction *act =
new KAction(parent);
560 act->setIcon(KIcon(service->icon()));
561 act->setText(actionName);
562 actionGroup->addAction( act );
563 act->setData(QVariant::fromValue(service));
571 KMimeType::Ptr mimeType = KMimeType::findByPath( name, 0,
true );
572 if ( mimeType->name() == QLatin1String(
"application/octet-stream") ) {
575 mimeType = KMimeType::findByFileContent( name );
QString MESSAGEVIEWER_EXPORT fileNameForMimetype(const QString &mimeType, int iconSize, const QString &fallbackFileName1=QString(), const QString &fallbackFileName2=QString())
Finds the filename of an icon based on the given mimetype or filenames.
bool MESSAGEVIEWER_EXPORT saveAttachments(const KMime::Content::List &contents, QWidget *parent)
bool MESSAGEVIEWER_EXPORT speakSelectedText(const QString &text, QWidget *parent)
bool MESSAGEVIEWER_EXPORT saveMessageInMbox(const QList< Akonadi::Item > &retrievedMsgs, QWidget *parent, bool appendMessages=false)
An ObjectTreeSource that does not work on anything.
static KMime::Content * findTypeNot(KMime::Content *content, const QByteArray &mediaType, const QByteArray &subType, bool deep=true, bool wide=true)
bool MESSAGEVIEWER_EXPORT saveContents(QWidget *parent, const QList< KMime::Content * > &contents)
static IconNameCache * instance()
void parseObjectTree(KMime::Content *node)
Parse beginning at a given node and recursively parsing the children of that node and it's next sibli...
MESSAGEVIEWER_EXPORT KAction * createAppAction(const KService::Ptr &service, bool singleOffer, QActionGroup *actionGroup, QObject *parent)
KDE_DEPRECATED QByteArray rawDecryptedBody() const
The origin and purpose of this function is unknown, the ancient wisdom about it got lost during the c...
KMMsgEncryptionState encryptionState(KMime::Content *node) const
QString iconPath(const QString &name, int size) const
MESSAGEVIEWER_EXPORT KMimeType::Ptr mimetype(const QString &name)
Search mimetype from filename when mimetype is empty or application/octet-stream. ...
static QString cleanSubject(KMime::Message *message)
Return this mails subject, with all "forward" and "reply" prefixes removed.
bool MESSAGEVIEWER_EXPORT saveContent(QWidget *parent, KMime::Content *content, const KUrl &url)
static KMime::Content * findType(KMime::Content *content, const QByteArray &mimeType, bool deep, bool wide)
bool MESSAGEVIEWER_EXPORT checkOverwrite(const KUrl &url, QWidget *w)
void setNodeUnprocessed(KMime::Content *node, bool recurse)
void removeTempFiles()
Cleanup the attachment temp files.
KMMsgSignatureState signatureState(KMime::Content *node) const
Parses messages and generates HTML display code out of them.
QList< KMime::Content * > MESSAGEVIEWER_EXPORT allContents(const KMime::Content *message)
bool MESSAGEVIEWER_EXPORT handleUrlWithQDesktopServices(const KUrl &url)
Delegates opening a URL to the QDesktopServices mechanisms for that on Windows and MacOSX...
int MESSAGEVIEWER_EXPORT getWritePermissions()
evaluates GlobalSettings->disregardUmask() and returns resulting permissions for storing files ...
QList< KMime::Content * > MESSAGEVIEWER_EXPORT extractAttachments(const KMime::Message *message)
static QString fileName(const KMime::Content *node)
Returns a usable filename for a node, that can be the filename from the content disposition header...