File indexing completed on 2024-12-22 04:57:01

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 "ewsfinditemrequest.h"
0008 
0009 #include <memory>
0010 
0011 #include <QXmlStreamWriter>
0012 
0013 #include "ewsclient_debug.h"
0014 
0015 static const QString traversalTypeNames[] = {
0016     QStringLiteral("Shallow"),
0017     QStringLiteral("Deep"),
0018     QStringLiteral("SoftDeleted"),
0019     QStringLiteral("Associated"),
0020 };
0021 
0022 class EwsFindItemResponse : public EwsRequest::Response
0023 {
0024 public:
0025     EwsFindItemResponse(QXmlStreamReader &reader);
0026     bool parseRootFolder(QXmlStreamReader &reader);
0027     EwsItem *readItem(QXmlStreamReader &reader);
0028 
0029     QList<EwsItem> mItems;
0030     unsigned mTotalItems;
0031     int mNextOffset;
0032     int mNextNumerator;
0033     int mNextDenominator;
0034     bool mIncludesLastItem;
0035 };
0036 
0037 EwsFindItemRequest::EwsFindItemRequest(EwsClient &client, QObject *parent)
0038     : EwsRequest(client, parent)
0039     , mTraversal(EwsTraversalShallow)
0040     , mPagination(false)
0041     , mPageBasePoint(EwsBasePointBeginning)
0042     , mPageOffset(0)
0043     , mFractional(false)
0044     , mMaxItems(-1)
0045     , mFracNumerator(0)
0046     , mFracDenominator(0)
0047     , mTotalItems(0)
0048     , mNextOffset(-1)
0049     , mNextNumerator(-1)
0050     , mNextDenominator(-1)
0051     , mIncludesLastItem(false)
0052 {
0053 }
0054 
0055 EwsFindItemRequest::~EwsFindItemRequest()
0056 {
0057 }
0058 
0059 void EwsFindItemRequest::setFolderId(const EwsId &id)
0060 {
0061     mFolderId = id;
0062 }
0063 
0064 void EwsFindItemRequest::setItemShape(const EwsItemShape &shape)
0065 {
0066     mShape = shape;
0067 }
0068 
0069 void EwsFindItemRequest::start()
0070 {
0071     QString reqString;
0072     QXmlStreamWriter writer(&reqString);
0073 
0074     startSoapDocument(writer);
0075 
0076     writer.writeStartElement(ewsMsgNsUri, QStringLiteral("FindItem"));
0077     writer.writeAttribute(QStringLiteral("Traversal"), traversalTypeNames[mTraversal]);
0078 
0079     mShape.write(writer);
0080 
0081     if (mPagination) {
0082         writer.writeStartElement(ewsMsgNsUri, QStringLiteral("IndexedPageItemView"));
0083         if (mMaxItems > 0) {
0084             writer.writeAttribute(QStringLiteral("MaxEntriesReturned"), QString::number(mMaxItems));
0085         }
0086         writer.writeAttribute(QStringLiteral("Offset"), QString::number(mPageOffset));
0087         writer.writeAttribute(QStringLiteral("BasePoint"), (mPageBasePoint == EwsBasePointEnd) ? QStringLiteral("End") : QStringLiteral("Beginning"));
0088         writer.writeEndElement();
0089     } else if (mFractional) {
0090         writer.writeStartElement(ewsMsgNsUri, QStringLiteral("FractionalPageItemView"));
0091         if (mMaxItems > 0) {
0092             writer.writeAttribute(QStringLiteral("MaxEntriesReturned"), QString::number(mMaxItems));
0093         }
0094         writer.writeAttribute(QStringLiteral("Numerator"), QString::number(mFracNumerator));
0095         writer.writeAttribute(QStringLiteral("Denominator"), QString::number(mFracDenominator));
0096         writer.writeEndElement();
0097     }
0098 
0099     writer.writeStartElement(ewsMsgNsUri, QStringLiteral("ParentFolderIds"));
0100     mFolderId.writeFolderIds(writer);
0101     writer.writeEndElement();
0102 
0103     writer.writeEndElement();
0104 
0105     endSoapDocument(writer);
0106 
0107     qCDebug(EWSCLI_PROTO_LOG) << reqString;
0108 
0109     qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Starting FindItems request (folder: ") << mFolderId << QStringLiteral(")");
0110 
0111     prepare(reqString);
0112 
0113     doSend();
0114 }
0115 
0116 bool EwsFindItemRequest::parseResult(QXmlStreamReader &reader)
0117 {
0118     return parseResponseMessage(reader, QStringLiteral("FindItem"), [this](QXmlStreamReader &reader) {
0119         return parseItemsResponse(reader);
0120     });
0121 }
0122 
0123 bool EwsFindItemRequest::parseItemsResponse(QXmlStreamReader &reader)
0124 {
0125     auto resp = new EwsFindItemResponse(reader);
0126     if (resp->responseClass() == EwsResponseUnknown) {
0127         return false;
0128     }
0129 
0130     mItems = resp->mItems;
0131     mTotalItems = resp->mTotalItems;
0132     mNextOffset = resp->mNextOffset;
0133     mNextNumerator = resp->mNextNumerator;
0134     mNextDenominator = resp->mNextDenominator;
0135     mIncludesLastItem = resp->mIncludesLastItem;
0136 
0137     if (EWSCLI_REQUEST_LOG().isDebugEnabled()) {
0138         if (resp->isSuccess()) {
0139             qCDebugNC(EWSCLI_REQUEST_LOG) << QStringLiteral("Got FindItems response (%1 items, last included: %2)")
0140                                                  .arg(mItems.size())
0141                                                  .arg(mIncludesLastItem ? QStringLiteral("true") : QStringLiteral("false"));
0142         } else {
0143             qCDebug(EWSCLI_REQUEST_LOG) << QStringLiteral("Got FindItems response - %1").arg(resp->responseMessage());
0144         }
0145     }
0146 
0147     return true;
0148 }
0149 
0150 EwsFindItemResponse::EwsFindItemResponse(QXmlStreamReader &reader)
0151     : EwsRequest::Response(reader)
0152 {
0153     while (reader.readNextStartElement()) {
0154         if (reader.namespaceUri() != ewsMsgNsUri && reader.namespaceUri() != ewsTypeNsUri) {
0155             setErrorMsg(QStringLiteral("Unexpected namespace in %1 element: %2").arg(QStringLiteral("ResponseMessage"), reader.namespaceUri().toString()));
0156             return;
0157         }
0158 
0159         if (reader.name() == QLatin1StringView("RootFolder")) {
0160             if (!parseRootFolder(reader)) {
0161                 return;
0162             }
0163         } else if (!readResponseElement(reader)) {
0164             setErrorMsg(QStringLiteral("Failed to read EWS request - invalid response element."));
0165             return;
0166         }
0167     }
0168 }
0169 
0170 bool EwsFindItemResponse::parseRootFolder(QXmlStreamReader &reader)
0171 {
0172     if (reader.namespaceUri() != ewsMsgNsUri || reader.name() != QLatin1StringView("RootFolder")) {
0173         return setErrorMsg(
0174             QStringLiteral("Failed to read EWS request - expected %1 element (got %2).").arg(QStringLiteral("RootFolder"), reader.qualifiedName().toString()));
0175     }
0176 
0177     if (!reader.attributes().hasAttribute(QStringLiteral("TotalItemsInView")) || !reader.attributes().hasAttribute(QStringLiteral("TotalItemsInView"))) {
0178         return setErrorMsg(QStringLiteral("Failed to read EWS request - missing attributes of %1 element.").arg(QStringLiteral("RootFolder")));
0179     }
0180     bool ok;
0181     QXmlStreamAttributes attrs = reader.attributes();
0182     mTotalItems = attrs.value(QStringLiteral("TotalItemsInView")).toUInt(&ok);
0183     if (!ok) {
0184         return setErrorMsg(QStringLiteral("Failed to read EWS request - failed to read %1 attribute.").arg(QStringLiteral("TotalItemsInView")));
0185     }
0186     mIncludesLastItem = attrs.value(QStringLiteral("IncludesLastItemInRange")) == QLatin1StringView("true");
0187 
0188     if (attrs.hasAttribute(QStringLiteral("IndexedPagingOffset"))) {
0189         mNextOffset = attrs.value(QStringLiteral("IndexedPagingOffset")).toInt(&ok);
0190         if (!ok) {
0191             return setErrorMsg(QStringLiteral("Failed to read EWS request - failed to read %1 attribute.").arg(QStringLiteral("IndexedPagingOffset")));
0192         }
0193     }
0194 
0195     if (attrs.hasAttribute(QStringLiteral("NumeratorOffset"))) {
0196         mNextNumerator = attrs.value(QStringLiteral("NumeratorOffset")).toInt(&ok);
0197         if (!ok) {
0198             return setErrorMsg(QStringLiteral("Failed to read EWS request - failed to read %1 attribute.").arg(QStringLiteral("NumeratorOffset")));
0199         }
0200     }
0201 
0202     if (attrs.hasAttribute(QStringLiteral("AbsoluteDenominator"))) {
0203         mNextDenominator = attrs.value(QStringLiteral("AbsoluteDenominator")).toInt(&ok);
0204         if (!ok) {
0205             return setErrorMsg(QStringLiteral("Failed to read EWS request - failed to read %1 attribute.").arg(QStringLiteral("AbsoluteDenominator")));
0206         }
0207     }
0208 
0209     if (!reader.readNextStartElement()) {
0210         return setErrorMsg(QStringLiteral("Failed to read EWS request - expected a child element in %1 element.").arg(QStringLiteral("RootFolder")));
0211     }
0212 
0213     if (reader.namespaceUri() != ewsTypeNsUri || reader.name() != QLatin1StringView("Items")) {
0214         return setErrorMsg(
0215             QStringLiteral("Failed to read EWS request - expected %1 element (got %2).").arg(QStringLiteral("Items"), reader.qualifiedName().toString()));
0216     }
0217 
0218     if (!reader.readNextStartElement()) {
0219         // An empty Items element means no items.
0220         reader.skipCurrentElement();
0221         return true;
0222     }
0223 
0224     if (reader.namespaceUri() != ewsTypeNsUri) {
0225         return setErrorMsg(QStringLiteral("Failed to read EWS request - expected child element from types namespace."));
0226     }
0227 
0228     do {
0229         EwsItem *item = readItem(reader);
0230         if (item) {
0231             mItems.append(*item);
0232         }
0233     } while (reader.readNextStartElement());
0234 
0235     // Finish the Items element
0236     reader.skipCurrentElement();
0237 
0238     // Finish the RootFolder element
0239     reader.skipCurrentElement();
0240 
0241     return true;
0242 }
0243 
0244 EwsItem *EwsFindItemResponse::readItem(QXmlStreamReader &reader)
0245 {
0246     EwsItem *item = nullptr;
0247     const QStringView readerName = reader.name();
0248     if (readerName == QLatin1StringView("Item") || readerName == QLatin1StringView("Message") || readerName == QLatin1StringView("CalendarItem")
0249         || readerName == QLatin1StringView("Contact") || readerName == QLatin1StringView("DistributionList")
0250         || readerName == QLatin1StringView("MeetingMessage") || readerName == QLatin1StringView("MeetingRequest")
0251         || readerName == QLatin1StringView("MeetingResponse") || readerName == QLatin1StringView("MeetingCancellation")
0252         || readerName == QLatin1StringView("Task")) {
0253         qCDebug(EWSCLI_LOG).noquote() << QStringLiteral("Processing %1").arg(readerName.toString());
0254         item = new EwsItem(reader);
0255         if (!item->isValid()) {
0256             setErrorMsg(QStringLiteral("Failed to read EWS request - invalid %1 element.").arg(readerName.toString()));
0257             delete item;
0258             return nullptr;
0259         }
0260     } else {
0261         qCWarning(EWSCLI_LOG).noquote() << QStringLiteral("Unsupported folder type %1").arg(readerName.toString());
0262         reader.skipCurrentElement();
0263     }
0264 
0265     return item;
0266 }
0267 
0268 #include "moc_ewsfinditemrequest.cpp"