File indexing completed on 2025-03-09 04:54:12

0001 /*
0002   SPDX-FileCopyrightText: 2009 Constantin Berzan <exit3219@gmail.com>
0003 
0004   Based on KMail code by various authors (kmmsgpartdlg).
0005 
0006   SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "attachmentpropertiesdialog.h"
0010 
0011 #include "attachmentfrommimecontentjob.h"
0012 #include "ui_attachmentpropertiesdialog.h"
0013 #include "ui_attachmentpropertiesdialog_readonly.h"
0014 
0015 #include "messagecore_debug.h"
0016 #include <KAboutData>
0017 
0018 #include <KMime/Content>
0019 #include <KMime/Headers>
0020 
0021 #include <KIO/Global>
0022 #include <KLocalizedString>
0023 #include <QDesktopServices>
0024 #include <QDialogButtonBox>
0025 #include <QMimeDatabase>
0026 #include <QMimeType>
0027 #include <QPushButton>
0028 #include <QUrlQuery>
0029 #include <QVBoxLayout>
0030 
0031 using namespace MessageCore;
0032 
0033 class Q_DECL_HIDDEN MessageCore::AttachmentPropertiesDialog::AttachmentPropertiesDialogPrivate
0034 {
0035 public:
0036     AttachmentPropertiesDialogPrivate(AttachmentPropertiesDialog *qq)
0037         : q(qq)
0038     {
0039     }
0040 
0041     ~AttachmentPropertiesDialogPrivate()
0042     {
0043         delete ui;
0044         delete uiReadOnly;
0045     }
0046 
0047     void init(const AttachmentPart::Ptr &part, bool readOnly);
0048     void polishUi();
0049     void mimeTypeChanged(const QString &type); // slot
0050     void populateEncodings();
0051     void populateMimeTypes();
0052     void populateWhatsThis();
0053     void loadFromPart();
0054     void saveToPart();
0055 
0056     AttachmentPropertiesDialog *const q;
0057     AttachmentPart::Ptr mPart;
0058 
0059     Ui::AttachmentPropertiesDialog *ui = nullptr;
0060     Ui::AttachmentPropertiesDialogReadOnly *uiReadOnly = nullptr;
0061     QVBoxLayout *mainLayout = nullptr;
0062     bool mReadOnly = false;
0063 };
0064 
0065 void AttachmentPropertiesDialog::AttachmentPropertiesDialogPrivate::init(const AttachmentPart::Ptr &part, bool readOnly)
0066 {
0067     mReadOnly = readOnly;
0068     mPart = part;
0069 
0070     auto widget = new QWidget(q);
0071     mainLayout = new QVBoxLayout;
0072     q->setLayout(mainLayout);
0073 
0074     mainLayout->addWidget(widget);
0075     if (mReadOnly) {
0076         uiReadOnly = new Ui::AttachmentPropertiesDialogReadOnly;
0077         uiReadOnly->setupUi(widget);
0078     } else {
0079         ui = new Ui::AttachmentPropertiesDialog;
0080         ui->setupUi(widget);
0081     }
0082     polishUi();
0083     q->setModal(true);
0084 
0085     loadFromPart();
0086 }
0087 
0088 void AttachmentPropertiesDialog::AttachmentPropertiesDialogPrivate::polishUi()
0089 {
0090     // Tweak the dialog, depending on whether it is read-only or not.
0091     QDialogButtonBox *buttonBox = nullptr;
0092 
0093     if (mReadOnly) {
0094         buttonBox = new QDialogButtonBox(QDialogButtonBox::Help | QDialogButtonBox::Close, q);
0095     } else {
0096         // Update the icon when the selected mime type changes.
0097 
0098         connect(ui->mimeType, &QComboBox::currentTextChanged, q, [this](const QString &str) {
0099             mimeTypeChanged(str);
0100         });
0101         populateMimeTypes();
0102         populateEncodings();
0103         buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help, q);
0104         QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
0105         okButton->setDefault(true);
0106         okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
0107     }
0108     q->connect(buttonBox->button(QDialogButtonBox::Help), &QAbstractButton::clicked, q, &AttachmentPropertiesDialog::slotHelp);
0109     q->connect(buttonBox, &QDialogButtonBox::accepted, q, &QDialog::accept);
0110     q->connect(buttonBox, &QDialogButtonBox::rejected, q, &QDialog::reject);
0111     mainLayout->addWidget(buttonBox);
0112     populateWhatsThis();
0113 }
0114 
0115 void AttachmentPropertiesDialog::AttachmentPropertiesDialogPrivate::mimeTypeChanged(const QString &type)
0116 {
0117     QMimeDatabase db;
0118     const QMimeType mimeType = db.mimeTypeForName(type);
0119     QPixmap pix =
0120         QIcon::fromTheme(mimeType.iconName(), QIcon::fromTheme(QStringLiteral("unknown"))).pixmap(q->style()->pixelMetric(QStyle::PM_MessageBoxIconSize));
0121 
0122     if (mReadOnly) {
0123         uiReadOnly->mimeIcon->setPixmap(pix);
0124     } else {
0125         ui->mimeIcon->setPixmap(pix);
0126     }
0127 }
0128 
0129 void AttachmentPropertiesDialog::AttachmentPropertiesDialogPrivate::populateWhatsThis()
0130 {
0131     // FIXME These are such a mess... Make them straightforward and pretty.
0132 
0133     const QString msgMimeType = i18n(
0134         "<p>The <em>MIME type</em> of the file:</p>"
0135         "<p>Normally, you do not need to touch this setting, since the "
0136         "type of the file is automatically checked; but, sometimes, %1 "
0137         "may not detect the type correctly -- here is where you can fix "
0138         "that.</p>",
0139         KAboutData::applicationData().componentName());
0140 
0141     const QString msgSize = i18n(
0142         "<p>The estimated size of the attachment:</p>"
0143         "<p>Note that, in an email message, a binary file encoded with "
0144         "base64 will take up four thirds the actual size of the file.</p>");
0145 
0146     const QString msgName = i18n(
0147         "<p>The file name of the part:</p>"
0148         "<p>Although this defaults to the name of the attached file, "
0149         "it does not specify the file to be attached; rather, it "
0150         "suggests a file name to be used by the recipient's mail agent "
0151         "when saving the part to disk.</p>");
0152 
0153     const QString msgDescription = i18n(
0154         "<p>A description of the part:</p>"
0155         "<p>This is just an informational description of the part, "
0156         "much like the Subject is for the whole message; most "
0157         "mail agents will show this information in their message "
0158         "previews alongside the attachment's icon.</p>");
0159 
0160     const QString msgEncoding = i18n(
0161         "<p>The transport encoding of this part:</p>"
0162         "<p>Normally, you do not need to change this, since %1 will use "
0163         "a decent default encoding, depending on the MIME type; yet, "
0164         "sometimes, you can significantly reduce the size of the "
0165         "resulting message, e.g. if a PostScript file does not contain "
0166         "binary data, but consists of pure text -- in this case, choosing "
0167         "\"quoted-printable\" over the default \"base64\" will save up "
0168         "to 25% in resulting message size.</p>",
0169         KAboutData::applicationData().componentName());
0170 
0171     const QString msgAutoDisplay = i18n(
0172         "<p>Check this option if you want to suggest to the "
0173         "recipient the automatic (inline) display of this part in the "
0174         "message preview, instead of the default icon view;</p>"
0175         "<p>Technically, this is carried out by setting this part's "
0176         "<em>Content-Disposition</em> header field to \"inline\" "
0177         "instead of the default \"attachment\".</p>");
0178 
0179     const QString msgSign = i18n(
0180         "<p>Check this option if you want this message part to be "
0181         "signed.</p>"
0182         "<p>The signature will be made with the key that you associated "
0183         "with the currently-selected identity.</p>");
0184 
0185     const QString msgEncrypt = i18n(
0186         "<p>Check this option if you want this message part to be "
0187         "encrypted.</p>"
0188         "<p>The part will be encrypted for the recipients of this "
0189         "message.</p>");
0190 
0191     if (mReadOnly) {
0192         uiReadOnly->size->setWhatsThis(msgSize);
0193         uiReadOnly->name->setWhatsThis(msgName);
0194         uiReadOnly->encoding->setWhatsThis(msgEncoding);
0195     } else {
0196         ui->mimeType->setWhatsThis(msgMimeType);
0197         ui->size->setWhatsThis(msgSize);
0198         ui->name->setWhatsThis(msgName);
0199         ui->encrypt->setWhatsThis(msgEncrypt);
0200         ui->sign->setWhatsThis(msgSign);
0201         ui->autoDisplay->setWhatsThis(msgAutoDisplay);
0202         ui->encoding->setWhatsThis(msgEncoding);
0203         ui->description->setWhatsThis(msgDescription);
0204     }
0205 }
0206 
0207 void AttachmentPropertiesDialog::AttachmentPropertiesDialogPrivate::populateEncodings()
0208 {
0209     using namespace KMime;
0210     using namespace KMime::Headers;
0211 
0212     ui->encoding->clear();
0213     ui->encoding->addItem(nameForEncoding(CE7Bit), int(CE7Bit));
0214     ui->encoding->addItem(nameForEncoding(CE8Bit), int(CE8Bit));
0215     ui->encoding->addItem(nameForEncoding(CEquPr), int(CEquPr));
0216     ui->encoding->addItem(nameForEncoding(CEbase64), int(CEbase64));
0217 
0218     // TODO 8bit should be disabled if it is disabled in Settings.
0219     // Also, if it's a message/* part, base64 and qp should be disabled.
0220     // But since this is a dialog for power users anyway, let them shoot
0221     // themselves in the foot.  (The AttachmentJob will fail when they
0222     // try to compose the message.)
0223 }
0224 
0225 void AttachmentPropertiesDialog::AttachmentPropertiesDialogPrivate::populateMimeTypes()
0226 {
0227     const QStringList list = QStringList() << QStringLiteral("text/html") << QStringLiteral("text/plain") << QStringLiteral("image/gif")
0228                                            << QStringLiteral("image/jpeg") << QStringLiteral("image/png") << QStringLiteral("application/octet-stream")
0229                                            << QStringLiteral("application/x-gunzip") << QStringLiteral("application/zip");
0230 
0231     ui->mimeType->addItems(list);
0232 }
0233 
0234 void AttachmentPropertiesDialog::AttachmentPropertiesDialogPrivate::loadFromPart()
0235 {
0236     Q_ASSERT(mPart);
0237 
0238     if (mReadOnly) {
0239         uiReadOnly->mimeType->setText(QString::fromLatin1(mPart->mimeType()));
0240         mimeTypeChanged(QString::fromLatin1(mPart->mimeType()));
0241         uiReadOnly->size->setText(KIO::convertSize(mPart->size()));
0242         uiReadOnly->name->setText(mPart->name().isEmpty() ? mPart->fileName() : mPart->name());
0243         if (mPart->description().isEmpty()) {
0244             uiReadOnly->description->hide();
0245             uiReadOnly->descriptionLabel->hide();
0246         } else {
0247             uiReadOnly->description->setText(mPart->description());
0248         }
0249         uiReadOnly->encoding->setText(KMime::nameForEncoding(mPart->encoding()));
0250     } else {
0251         const QString mimeType = QString::fromLatin1(mPart->mimeType());
0252         const int index = ui->mimeType->findText(mimeType);
0253         if (index == -1) {
0254             ui->mimeType->insertItem(0, mimeType);
0255             ui->mimeType->setCurrentIndex(0);
0256         } else {
0257             ui->mimeType->setCurrentIndex(index);
0258         }
0259         ui->size->setText(KIO::convertSize(mPart->size()));
0260         ui->name->setText(mPart->name().isEmpty() ? mPart->fileName() : mPart->name());
0261         ui->description->setText(mPart->description());
0262         ui->encoding->setCurrentIndex(int(mPart->encoding()));
0263         ui->autoDisplay->setChecked(mPart->isInline());
0264         ui->encrypt->setChecked(mPart->isEncrypted());
0265         ui->sign->setChecked(mPart->isSigned());
0266     }
0267 }
0268 
0269 static QString removeNewlines(const QString &input)
0270 {
0271     QString ret(input);
0272     ret.replace(QLatin1Char('\n'), QLatin1Char(' '));
0273     return ret;
0274 }
0275 
0276 void AttachmentPropertiesDialog::AttachmentPropertiesDialogPrivate::saveToPart()
0277 {
0278     Q_ASSERT(mPart);
0279     Q_ASSERT(!mReadOnly);
0280 
0281     if (mReadOnly) {
0282         return;
0283     }
0284     mPart->setMimeType(ui->mimeType->currentText().toLatin1());
0285     const QString name = removeNewlines(ui->name->text());
0286     mPart->setName(name);
0287     mPart->setFileName(name);
0288     mPart->setDescription(removeNewlines(ui->description->text()));
0289     mPart->setInline(ui->autoDisplay->isChecked());
0290     mPart->setSigned(ui->sign->isChecked());
0291     mPart->setEncrypted(ui->encrypt->isChecked());
0292     mPart->setInline(ui->autoDisplay->isChecked());
0293 
0294     if (ui->mimeType->currentText().startsWith(QLatin1StringView("message")) && ui->encoding->itemData(ui->encoding->currentIndex()) != KMime::Headers::CE7Bit
0295         && ui->encoding->itemData(ui->encoding->currentIndex()) != KMime::Headers::CE8Bit) {
0296         qCWarning(MESSAGECORE_LOG) << R"(Encoding on message/rfc822 must be "7bit" or "8bit".)";
0297     }
0298 
0299     mPart->setEncoding(KMime::Headers::contentEncoding(ui->encoding->itemData(ui->encoding->currentIndex()).toInt()));
0300 }
0301 
0302 AttachmentPropertiesDialog::AttachmentPropertiesDialog(const AttachmentPart::Ptr &part, bool readOnly, QWidget *parent)
0303     : QDialog(parent)
0304     , d(new AttachmentPropertiesDialogPrivate(this))
0305 {
0306     d->init(part, readOnly);
0307     setWindowTitle(i18nc("@title:window", "Attachment Properties"));
0308 }
0309 
0310 AttachmentPropertiesDialog::AttachmentPropertiesDialog(const KMime::Content *content, QWidget *parent)
0311     : QDialog(parent)
0312     , d(new AttachmentPropertiesDialogPrivate(this))
0313 {
0314     auto job = new AttachmentFromMimeContentJob(content, this);
0315     job->exec();
0316     if (job->error()) {
0317         qCCritical(MESSAGECORE_LOG) << "AttachmentFromMimeContentJob failed." << job->errorString();
0318     }
0319 
0320     const AttachmentPart::Ptr part = job->attachmentPart();
0321     d->init(part, true);
0322     setWindowTitle(i18nc("@title:window", "Attachment Properties"));
0323 }
0324 
0325 AttachmentPropertiesDialog::~AttachmentPropertiesDialog() = default;
0326 
0327 AttachmentPart::Ptr AttachmentPropertiesDialog::attachmentPart() const
0328 {
0329     return d->mPart;
0330 }
0331 
0332 bool AttachmentPropertiesDialog::isEncryptEnabled() const
0333 {
0334     if (d->ui) {
0335         return d->ui->encrypt->isEnabled();
0336     }
0337     return false;
0338 }
0339 
0340 void AttachmentPropertiesDialog::setEncryptEnabled(bool enabled)
0341 {
0342     if (d->ui) {
0343         d->ui->encrypt->setEnabled(enabled);
0344     }
0345 }
0346 
0347 bool AttachmentPropertiesDialog::isSignEnabled() const
0348 {
0349     if (d->ui) {
0350         return d->ui->sign->isEnabled();
0351     }
0352     return false;
0353 }
0354 
0355 void AttachmentPropertiesDialog::setSignEnabled(bool enabled)
0356 {
0357     if (d->ui) {
0358         d->ui->sign->setEnabled(enabled);
0359     }
0360 }
0361 
0362 void AttachmentPropertiesDialog::accept()
0363 {
0364     if (!d->mReadOnly) {
0365         d->saveToPart();
0366     }
0367 
0368     QDialog::accept();
0369 }
0370 
0371 // Copy from PimCommon::Util::invokeHelp
0372 // Avoid to add PimCommon dep for this method
0373 void invokeHelp(const QString &docfile, const QString &anchor)
0374 {
0375     if (!docfile.isEmpty()) {
0376         QUrl url;
0377         url = QUrl(QStringLiteral("help:/")).resolved(QUrl(docfile));
0378         if (!anchor.isEmpty()) {
0379             QUrlQuery query(url);
0380             query.addQueryItem(QStringLiteral("anchor"), anchor);
0381             url.setQuery(query);
0382         }
0383         // launch khelpcenter, or a browser for URIs not handled by khelpcenter
0384         QDesktopServices::openUrl(url);
0385     }
0386 }
0387 
0388 void AttachmentPropertiesDialog::slotHelp()
0389 {
0390     invokeHelp(QStringLiteral("kmail2/the-composer-window.html"), QStringLiteral("attachments"));
0391 }
0392 
0393 #include "moc_attachmentpropertiesdialog.cpp"