File indexing completed on 2024-05-12 05:20:37

0001 /*
0002    SPDX-FileCopyrightText: 2023-2024 Laurent Montel <montel.org>
0003 
0004    SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "commandlineinfo.h"
0008 #include "kmail_debug.h"
0009 #include "kmail_options.h"
0010 #include "messagecore/stringutil.h"
0011 #include <QCommandLineParser>
0012 
0013 CommandLineInfo::CommandLineInfo() = default;
0014 
0015 CommandLineInfo::~CommandLineInfo() = default;
0016 
0017 QDebug operator<<(QDebug d, const CommandLineInfo &t)
0018 {
0019     d << "mCustomHeaders " << t.customHeaders();
0020     d << "mAttachURLs " << t.attachURLs();
0021     d << "mTo " << t.to();
0022     d << "mCc " << t.cc();
0023     d << "mBcc " << t.bcc();
0024     d << "mSubject " << t.subject();
0025     d << "mBody " << t.body();
0026     d << "mInReplyTo " << t.inReplyTo();
0027     d << "mReplyTo " << t.replyTo();
0028     d << "mIdentity " << t.identity();
0029     d << "mMessageFile " << t.messageFile();
0030     d << "mStartInTray " << t.startInTray();
0031     d << "mMailto " << t.mailto();
0032     d << "mCheckMail " << t.checkMail();
0033     d << "mViewOnly " << t.viewOnly();
0034     d << "mCalledWithSession " << t.calledWithSession();
0035     return d;
0036 }
0037 
0038 static QUrl makeAbsoluteUrl(const QString &str, const QString &cwd)
0039 {
0040     return QUrl::fromUserInput(str, cwd, QUrl::AssumeLocalFile);
0041 }
0042 
0043 void CommandLineInfo::parseCommandLine(const QStringList &args, const QString &workingDir)
0044 {
0045     // process args:
0046     QCommandLineParser parser;
0047     kmail_options(&parser);
0048     QStringList newargs;
0049     bool addAttachmentAttribute = false;
0050     for (const QString &argument : std::as_const(args)) {
0051         if (argument == QLatin1StringView("--attach")) {
0052             addAttachmentAttribute = true;
0053         } else {
0054             if (argument.startsWith(QLatin1StringView("--"))) {
0055                 addAttachmentAttribute = false;
0056             }
0057             if (argument.contains(QLatin1Char('@')) || argument.startsWith(QLatin1StringView("mailto:"))) { // address mustn't be trade as a attachment
0058                 addAttachmentAttribute = false;
0059             }
0060             if (addAttachmentAttribute) {
0061                 newargs.append(QStringLiteral("--attach"));
0062                 newargs.append(argument);
0063             } else {
0064                 newargs.append(argument);
0065             }
0066         }
0067     }
0068 
0069     parser.process(newargs);
0070     if (parser.isSet(QStringLiteral("subject"))) {
0071         mSubject = parser.value(QStringLiteral("subject"));
0072         // if kmail is called with 'kmail -session abc' then this doesn't mean
0073         // that the user wants to send a message with subject "ession" but
0074         // (most likely) that the user clicked on KMail's system tray applet
0075         // which results in KMKernel::raise() calling "kmail kmail newInstance"
0076         // via D-Bus which apparently executes the application with the original
0077         // command line arguments and those include "-session ..." if
0078         // kmail/kontact was restored by session management
0079         if (mSubject == QLatin1StringView("ession")) {
0080             mSubject.clear();
0081             mCalledWithSession = true;
0082         } else {
0083             mMailto = true;
0084         }
0085     }
0086 
0087     const QStringList ccList = parser.values(QStringLiteral("cc"));
0088     if (!ccList.isEmpty()) {
0089         mMailto = true;
0090         mCc = ccList.join(QStringLiteral(", "));
0091     }
0092 
0093     const QStringList bccList = parser.values(QStringLiteral("bcc"));
0094     if (!bccList.isEmpty()) {
0095         mMailto = true;
0096         mBcc = bccList.join(QStringLiteral(", "));
0097     }
0098 
0099     if (parser.isSet(QStringLiteral("replyTo"))) {
0100         mMailto = true;
0101         mReplyTo = parser.value(QStringLiteral("replyTo"));
0102     }
0103 
0104     if (parser.isSet(QStringLiteral("msg"))) {
0105         mMailto = true;
0106         const QString file = parser.value(QStringLiteral("msg"));
0107         mMessageFile = makeAbsoluteUrl(file, workingDir);
0108     }
0109 
0110     if (parser.isSet(QStringLiteral("body"))) {
0111         mMailto = true;
0112         mBody = parser.value(QStringLiteral("body"));
0113     }
0114 
0115     const QStringList attachList = parser.values(QStringLiteral("attach"));
0116     if (!attachList.isEmpty()) {
0117         mMailto = true;
0118         for (const QString &attach : attachList) {
0119             if (!attach.isEmpty()) {
0120                 mAttachURLs.append(makeAbsoluteUrl(attach, workingDir));
0121             }
0122         }
0123     }
0124 
0125     mCustomHeaders = parser.values(QStringLiteral("header"));
0126 
0127     if (parser.isSet(QStringLiteral("composer"))) {
0128         mMailto = true;
0129     }
0130 
0131     if (parser.isSet(QStringLiteral("check"))) {
0132         mCheckMail = true;
0133     }
0134 
0135     if (parser.isSet(QStringLiteral("startintray"))) {
0136         mStartInTray = true;
0137     }
0138 
0139     if (parser.isSet(QStringLiteral("identity"))) {
0140         mIdentity = parser.value(QStringLiteral("identity"));
0141     }
0142 
0143     if (parser.isSet(QStringLiteral("view"))) {
0144         mViewOnly = true;
0145         const QString filename = parser.value(QStringLiteral("view"));
0146         mMessageFile = QUrl::fromUserInput(filename, workingDir);
0147     }
0148 
0149     if (!mCalledWithSession) {
0150         // only read additional command line arguments if kmail/kontact is
0151         // not called with "-session foo"
0152         const QStringList lstPositionalArguments = parser.positionalArguments();
0153         for (const QString &arg : lstPositionalArguments) {
0154             if (arg.startsWith(QLatin1StringView("mailto:"), Qt::CaseInsensitive)) {
0155                 const QUrl urlDecoded(QUrl::fromPercentEncoding(arg.toUtf8()));
0156                 const QList<QPair<QString, QString>> values = MessageCore::StringUtil::parseMailtoUrl(urlDecoded);
0157                 QString previousKey;
0158                 for (int i = 0; i < values.count(); ++i) {
0159                     const QPair<QString, QString> element = values.at(i);
0160                     const QString key = element.first.toLower();
0161                     if (key == QLatin1StringView("to")) {
0162                         if (!element.second.isEmpty()) {
0163                             mTo += element.second + QStringLiteral(", ");
0164                         }
0165                         previousKey.clear();
0166                     } else if (key == QLatin1StringView("cc")) {
0167                         if (!element.second.isEmpty()) {
0168                             mCc += element.second + QStringLiteral(", ");
0169                         }
0170                         previousKey.clear();
0171                     } else if (key == QLatin1StringView("bcc")) {
0172                         if (!element.second.isEmpty()) {
0173                             mBcc += element.second + QStringLiteral(", ");
0174                         }
0175                         previousKey.clear();
0176                     } else if (key == QLatin1StringView("subject")) {
0177                         mSubject = element.second;
0178                         previousKey.clear();
0179                     } else if (key == QLatin1StringView("body")) {
0180                         mBody = element.second;
0181                         previousKey = key;
0182                     } else if (key == QLatin1StringView("in-reply-to")) {
0183                         mInReplyTo = element.second;
0184                         previousKey.clear();
0185                     } else if (key == QLatin1StringView("attachment") || key == QLatin1StringView("attach")) {
0186                         if (!element.second.isEmpty()) {
0187                             mAttachURLs << makeAbsoluteUrl(element.second, workingDir);
0188                         }
0189                         previousKey.clear();
0190                     } else {
0191                         qCWarning(KMAIL_LOG) << "unknown key" << key;
0192                         // Workaround: https://bugs.kde.org/show_bug.cgi?id=390939
0193                         // QMap<QString, QString> parseMailtoUrl(const QUrl &url) parses correctly url
0194                         // But if we have a "&" unknown key we lost it.
0195                         if (previousKey == QLatin1StringView("mBody")) {
0196                             mBody += QLatin1Char('&') + key + QLatin1Char('=') + element.second;
0197                         }
0198                         // Don't clear previous key.
0199                     }
0200                 }
0201             } else {
0202                 const QUrl url(arg);
0203                 if (url.isValid() && !url.scheme().isEmpty()) {
0204                     mAttachURLs += url;
0205                 } else {
0206                     mTo += arg + QStringLiteral(", ");
0207                 }
0208             }
0209             mMailto = true;
0210         }
0211         if (!mTo.isEmpty()) {
0212             // cut off the superfluous trailing ", "
0213             mTo.chop(2);
0214         }
0215     }
0216 }
0217 
0218 QStringList CommandLineInfo::customHeaders() const
0219 {
0220     return mCustomHeaders;
0221 }
0222 
0223 QList<QUrl> CommandLineInfo::attachURLs() const
0224 {
0225     return mAttachURLs;
0226 }
0227 
0228 QString CommandLineInfo::to() const
0229 {
0230     return mTo;
0231 }
0232 
0233 QString CommandLineInfo::cc() const
0234 {
0235     return mCc;
0236 }
0237 
0238 QString CommandLineInfo::bcc() const
0239 {
0240     return mBcc;
0241 }
0242 
0243 QString CommandLineInfo::subject() const
0244 {
0245     return mSubject;
0246 }
0247 
0248 QString CommandLineInfo::body() const
0249 {
0250     return mBody;
0251 }
0252 
0253 QString CommandLineInfo::inReplyTo() const
0254 {
0255     return mInReplyTo;
0256 }
0257 
0258 QString CommandLineInfo::replyTo() const
0259 {
0260     return mReplyTo;
0261 }
0262 
0263 QString CommandLineInfo::identity() const
0264 {
0265     return mIdentity;
0266 }
0267 
0268 QUrl CommandLineInfo::messageFile() const
0269 {
0270     return mMessageFile;
0271 }
0272 
0273 bool CommandLineInfo::startInTray() const
0274 {
0275     return mStartInTray;
0276 }
0277 
0278 bool CommandLineInfo::mailto() const
0279 {
0280     return mMailto;
0281 }
0282 
0283 bool CommandLineInfo::checkMail() const
0284 {
0285     return mCheckMail;
0286 }
0287 
0288 bool CommandLineInfo::viewOnly() const
0289 {
0290     return mViewOnly;
0291 }
0292 
0293 bool CommandLineInfo::calledWithSession() const
0294 {
0295     return mCalledWithSession;
0296 }
0297 
0298 bool CommandLineInfo::operator==(const CommandLineInfo &other) const
0299 {
0300     return mCustomHeaders == other.mCustomHeaders && mAttachURLs == other.mAttachURLs && mTo == other.mTo && mCc == other.mCc && mBcc == other.mBcc
0301         && mSubject == other.mSubject && mBody == other.mBody && mInReplyTo == other.mInReplyTo && mReplyTo == other.mReplyTo && mIdentity == other.mIdentity
0302         && mMessageFile == other.mMessageFile && mStartInTray == other.mStartInTray && mMailto == other.mMailto && mCheckMail == other.mCheckMail
0303         && mViewOnly == other.mViewOnly && mCalledWithSession == other.mCalledWithSession;
0304 }
0305 
0306 void CommandLineInfo::setCustomHeaders(const QStringList &newCustomHeaders)
0307 {
0308     mCustomHeaders = newCustomHeaders;
0309 }
0310 
0311 void CommandLineInfo::setAttachURLs(const QList<QUrl> &newAttachURLs)
0312 {
0313     mAttachURLs = newAttachURLs;
0314 }
0315 
0316 void CommandLineInfo::setTo(const QString &newTo)
0317 {
0318     mTo = newTo;
0319 }
0320 
0321 void CommandLineInfo::setCc(const QString &newCc)
0322 {
0323     mCc = newCc;
0324 }
0325 
0326 void CommandLineInfo::setBcc(const QString &newBcc)
0327 {
0328     mBcc = newBcc;
0329 }
0330 
0331 void CommandLineInfo::setSubject(const QString &newSubject)
0332 {
0333     mSubject = newSubject;
0334 }
0335 
0336 void CommandLineInfo::setBody(const QString &newBody)
0337 {
0338     mBody = newBody;
0339 }
0340 
0341 void CommandLineInfo::setInReplyTo(const QString &newInReplyTo)
0342 {
0343     mInReplyTo = newInReplyTo;
0344 }
0345 
0346 void CommandLineInfo::setReplyTo(const QString &newReplyTo)
0347 {
0348     mReplyTo = newReplyTo;
0349 }
0350 
0351 void CommandLineInfo::setIdentity(const QString &newIdentity)
0352 {
0353     mIdentity = newIdentity;
0354 }
0355 
0356 void CommandLineInfo::setMessageFile(const QUrl &newMessageFile)
0357 {
0358     mMessageFile = newMessageFile;
0359 }
0360 
0361 void CommandLineInfo::setStartInTray(bool newStartInTray)
0362 {
0363     mStartInTray = newStartInTray;
0364 }
0365 
0366 void CommandLineInfo::setMailto(bool newMailto)
0367 {
0368     mMailto = newMailto;
0369 }
0370 
0371 void CommandLineInfo::setCheckMail(bool newCheckMail)
0372 {
0373     mCheckMail = newCheckMail;
0374 }
0375 
0376 void CommandLineInfo::setViewOnly(bool newViewOnly)
0377 {
0378     mViewOnly = newViewOnly;
0379 }
0380 
0381 void CommandLineInfo::setCalledWithSession(bool newCalledWithSession)
0382 {
0383     mCalledWithSession = newCalledWithSession;
0384 }