File indexing completed on 2025-01-05 04:50:03
0001 /* 0002 SPDX-FileCopyrightText: 2015-2017 Krzysztof Nowicki <krissn@op.pl> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "ewspoxautodiscoverrequest.h" 0008 0009 #include <QTemporaryFile> 0010 #include <QXmlStreamReader> 0011 #include <QXmlStreamWriter> 0012 0013 #include <KIO/TransferJob> 0014 0015 #include "ewsclient_debug.h" 0016 0017 static const QString poxAdOuReqNsUri = QStringLiteral("http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006"); 0018 static const QString poxAdRespNsUri = QStringLiteral("http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006"); 0019 static const QString poxAdOuRespNsUri = QStringLiteral("http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a"); 0020 0021 EwsPoxAutodiscoverRequest::EwsPoxAutodiscoverRequest(const QUrl &url, const QString &email, const QString &userAgent, bool useNTLMv2, QObject *parent) 0022 : EwsJob(parent) 0023 , mUrl(url) 0024 , mEmail(email) 0025 , mUserAgent(userAgent) 0026 , mUseNTLMv2(useNTLMv2) 0027 , mServerVersion(EwsServerVersion::ewsVersion2007Sp1) 0028 , mAction(Settings) 0029 { 0030 } 0031 0032 EwsPoxAutodiscoverRequest::~EwsPoxAutodiscoverRequest() = default; 0033 0034 void EwsPoxAutodiscoverRequest::doSend() 0035 { 0036 const auto jobs{subjobs()}; 0037 for (KJob *job : jobs) { 0038 job->start(); 0039 } 0040 } 0041 0042 void EwsPoxAutodiscoverRequest::prepare(const QString &body) 0043 { 0044 mBody = body; 0045 mLastUrl = mUrl; 0046 KIO::TransferJob *job = KIO::http_post(mUrl, body.toUtf8(), KIO::HideProgressInfo); 0047 job->addMetaData(QStringLiteral("content-type"), QStringLiteral("text/xml")); 0048 job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true")); 0049 if (mUseNTLMv2) { 0050 job->addMetaData(QStringLiteral("EnableNTLMv2Auth"), QStringLiteral("true")); 0051 } 0052 if (!mUserAgent.isEmpty()) { 0053 job->addMetaData(QStringLiteral("UserAgent"), mUserAgent); 0054 } 0055 // config->readEntry("no-spoof-check", false) 0056 0057 connect(job, &KIO::TransferJob::result, this, &EwsPoxAutodiscoverRequest::requestResult); 0058 connect(job, &KIO::TransferJob::data, this, &EwsPoxAutodiscoverRequest::requestData); 0059 connect(job, &KIO::TransferJob::redirection, this, &EwsPoxAutodiscoverRequest::requestRedirect); 0060 0061 addSubjob(job); 0062 } 0063 0064 void EwsPoxAutodiscoverRequest::start() 0065 { 0066 QString reqString; 0067 QXmlStreamWriter writer(&reqString); 0068 0069 writer.writeStartDocument(); 0070 0071 writer.writeDefaultNamespace(poxAdOuReqNsUri); 0072 0073 writer.writeStartElement(poxAdOuReqNsUri, QStringLiteral("Autodiscover")); 0074 0075 writer.writeStartElement(poxAdOuReqNsUri, QStringLiteral("Request")); 0076 0077 writer.writeTextElement(poxAdOuReqNsUri, QStringLiteral("EMailAddress"), mEmail); 0078 writer.writeTextElement(poxAdOuReqNsUri, QStringLiteral("AcceptableResponseSchema"), poxAdOuRespNsUri); 0079 0080 writer.writeEndElement(); // Request 0081 0082 writer.writeEndElement(); // Autodiscover 0083 0084 writer.writeEndDocument(); 0085 0086 qCDebug(EWSCLI_PROTO_LOG) << reqString; 0087 0088 qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Starting POX Autodiscovery request (url: ") << mUrl << QStringLiteral(", email: ") << mEmail; 0089 prepare(reqString); 0090 0091 doSend(); 0092 } 0093 0094 void EwsPoxAutodiscoverRequest::requestData(KIO::Job *job, const QByteArray &data) 0095 { 0096 Q_UNUSED(job) 0097 0098 qCDebug(EWSCLI_PROTO_LOG) << "data" << job << data; 0099 mResponseData += QString::fromUtf8(data); 0100 } 0101 0102 void EwsPoxAutodiscoverRequest::requestResult(KJob *job) 0103 { 0104 if (EWSCLI_PROTO_LOG().isDebugEnabled()) { 0105 ewsLogDir.setAutoRemove(false); 0106 if (ewsLogDir.isValid()) { 0107 QTemporaryFile dumpFile(ewsLogDir.path() + QStringLiteral("/ews_xmldump_XXXXXXX.xml")); 0108 dumpFile.open(); 0109 dumpFile.setAutoRemove(false); 0110 dumpFile.write(mResponseData.toUtf8()); 0111 qCDebug(EWSCLI_PROTO_LOG) << "response dumped to" << dumpFile.fileName(); 0112 dumpFile.close(); 0113 } 0114 } 0115 0116 auto trJob = qobject_cast<KIO::TransferJob *>(job); 0117 int resp = trJob->metaData()[QStringLiteral("responsecode")].toUInt(); 0118 0119 if (job->error() != 0) { 0120 setErrorMsg(QStringLiteral("Failed to process EWS request: ") + job->errorString()); 0121 setError(job->error()); 0122 } else if (resp >= 300) { 0123 setErrorMsg(QStringLiteral("Failed to process EWS request - HTTP code %1").arg(resp)); 0124 setError(resp); 0125 } else { 0126 QXmlStreamReader reader(mResponseData); 0127 readResponse(reader); 0128 } 0129 0130 emitResult(); 0131 } 0132 0133 bool EwsPoxAutodiscoverRequest::readResponse(QXmlStreamReader &reader) 0134 { 0135 if (!reader.readNextStartElement()) { 0136 return setErrorMsg(QStringLiteral("Failed to read POX response XML")); 0137 } 0138 0139 if ((reader.name() != QLatin1StringView("Autodiscover")) || (reader.namespaceUri() != poxAdRespNsUri)) { 0140 return setErrorMsg(QStringLiteral("Failed to read POX response - not an Autodiscover response")); 0141 } 0142 0143 if (!reader.readNextStartElement()) { 0144 return setErrorMsg(QStringLiteral("Failed to read POX response - expected %1 element").arg(QStringLiteral("Response"))); 0145 } 0146 0147 if ((reader.name() != QLatin1StringView("Response")) || (reader.namespaceUri() != poxAdOuRespNsUri)) { 0148 return setErrorMsg( 0149 QStringLiteral("Failed to read POX response - expected %1 element, found %2").arg(QStringLiteral("Response").arg(reader.name().toString()))); 0150 } 0151 0152 while (reader.readNextStartElement()) { 0153 if (reader.namespaceUri() != poxAdOuRespNsUri) { 0154 return setErrorMsg(QStringLiteral("Failed to read POX response - invalid namespace")); 0155 } 0156 0157 if (reader.name() == QLatin1StringView("User")) { 0158 reader.skipCurrentElement(); 0159 } else if (reader.name() == QLatin1StringView("Account")) { 0160 if (!readAccount(reader)) { 0161 return false; 0162 } 0163 } else { 0164 return setErrorMsg( 0165 QStringLiteral("Failed to read POX response - unknown element '%1' inside '%2'").arg(reader.name().toString(), QStringLiteral("Response"))); 0166 } 0167 } 0168 return true; 0169 } 0170 0171 bool EwsPoxAutodiscoverRequest::readAccount(QXmlStreamReader &reader) 0172 { 0173 while (reader.readNextStartElement()) { 0174 if (reader.namespaceUri() != poxAdOuRespNsUri) { 0175 return setErrorMsg(QStringLiteral("Failed to read POX response - invalid namespace")); 0176 } 0177 const QStringView readerName = reader.name(); 0178 if (readerName == QLatin1StringView("Action")) { 0179 QString action = reader.readElementText(); 0180 if (action == QLatin1StringView("settings")) { 0181 mAction = Settings; 0182 } else if (action == QLatin1StringView("redirectUrl")) { 0183 mAction = RedirectUrl; 0184 } else if (action == QLatin1StringView("redirectAddr")) { 0185 mAction = RedirectAddr; 0186 } else { 0187 return setErrorMsg(QStringLiteral("Failed to read POX response - unknown action '%1'").arg(action)); 0188 } 0189 } else if (readerName == QLatin1StringView("RedirectUrl")) { 0190 mRedirectUrl = reader.readElementText(); 0191 } else if (readerName == QLatin1StringView("RedirectAddr")) { 0192 mRedirectAddr = reader.readElementText(); 0193 } else if (readerName == QLatin1StringView("RedirectAddr")) { 0194 mRedirectAddr = reader.readElementText(); 0195 } else if (readerName == QLatin1StringView("Protocol")) { 0196 if (!readProtocol(reader)) { 0197 return false; 0198 } 0199 } else { 0200 reader.skipCurrentElement(); 0201 } 0202 } 0203 return true; 0204 } 0205 0206 bool EwsPoxAutodiscoverRequest::readProtocol(QXmlStreamReader &reader) 0207 { 0208 Protocol proto; 0209 0210 while (reader.readNextStartElement()) { 0211 if (reader.namespaceUri() != poxAdOuRespNsUri) { 0212 return setErrorMsg(QStringLiteral("Failed to read POX response - invalid namespace")); 0213 } 0214 const QStringView readerName = reader.name(); 0215 if (readerName == QLatin1StringView("Type")) { 0216 QString type = reader.readElementText(); 0217 if (type == QLatin1StringView("EXCH")) { 0218 proto.mType = ExchangeProto; 0219 } else if (type == QLatin1StringView("EXPR")) { 0220 proto.mType = ExchangeProxyProto; 0221 } else if (type == QLatin1StringView("WEB")) { 0222 proto.mType = ExchangeWebProto; 0223 } else { 0224 return setErrorMsg(QStringLiteral("Failed to read POX response - unknown protocol '%1'").arg(type)); 0225 } 0226 } else if (readerName == QLatin1StringView("EwsUrl")) { 0227 proto.mEwsUrl = reader.readElementText(); 0228 } else if (readerName == QLatin1StringView("OabUrl")) { 0229 proto.mOabUrl = reader.readElementText(); 0230 } else { 0231 reader.skipCurrentElement(); 0232 } 0233 } 0234 0235 qCDebug(EWSCLI_LOG) << "Adding proto type" << proto.mType << proto.isValid(); 0236 mProtocols[proto.mType] = proto; 0237 0238 return true; 0239 } 0240 0241 void EwsPoxAutodiscoverRequest::requestRedirect(KIO::Job *job, const QUrl &url) 0242 { 0243 Q_UNUSED(job) 0244 0245 qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got HTTP redirect to: ") << mUrl; 0246 0247 mLastUrl = url; 0248 } 0249 0250 void EwsPoxAutodiscoverRequest::dump() const 0251 { 0252 ewsLogDir.setAutoRemove(false); 0253 if (ewsLogDir.isValid()) { 0254 QTemporaryFile reqDumpFile(ewsLogDir.path() + QStringLiteral("/ews_xmlreqdump_XXXXXXX.xml")); 0255 reqDumpFile.open(); 0256 reqDumpFile.setAutoRemove(false); 0257 reqDumpFile.write(mBody.toUtf8()); 0258 reqDumpFile.close(); 0259 QTemporaryFile resDumpFile(ewsLogDir.path() + QStringLiteral("/ews_xmlresdump_XXXXXXX.xml")); 0260 resDumpFile.open(); 0261 resDumpFile.setAutoRemove(false); 0262 resDumpFile.write(mResponseData.toUtf8()); 0263 resDumpFile.close(); 0264 qCDebug(EWSCLI_LOG) << "request dumped to" << reqDumpFile.fileName(); 0265 qCDebug(EWSCLI_LOG) << "response dumped to" << resDumpFile.fileName(); 0266 } else { 0267 qCWarning(EWSCLI_LOG) << "failed to dump request and response"; 0268 } 0269 } 0270 0271 #include "moc_ewspoxautodiscoverrequest.cpp"