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 }