diff --git a/src/contacts/contactsservice.cpp b/src/contacts/contactsservice.cpp index 8a18c35..21a5650 100644 --- a/src/contacts/contactsservice.cpp +++ b/src/contacts/contactsservice.cpp @@ -1,1218 +1,1218 @@ /* Copyright (C) 2012 - 2018 Daniel Vrátil This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "contactsservice.h" #include "contact.h" #include "contactsgroup.h" +#include "utils.h" #include "../debug.h" #include #include #include #include /* Qt::escape() */ #include namespace KGAPI2 { namespace ContactsService { namespace Private { QString stringFromXMLMap(const QVariantMap &map, const QString &key) { const QVariantMap t = map.value(key).toMap(); return t.value(QStringLiteral("$t")).toString(); } ObjectPtr JSONToContactsGroup(const QVariantMap &map); ObjectPtr JSONToContact(const QVariantMap& map); static const QUrl GoogleApisUrl(QStringLiteral("https://www.google.com")); static const QString ContactsBasePath(QStringLiteral("/m8/feeds/contacts")); static const QString ContactsGroupBasePath(QStringLiteral("/m8/feeds/groups")); static const QString PhotoBasePath(QStringLiteral("/m8/feeds/photos/media")); QByteArray addRelSchema(const QByteArray &rel) { if (!rel.startsWith("http://schemas.google.com/g/2005#")) { return "http://schemas.google.com/g/2005#" + rel; } else { return rel; } } } ObjectsList parseJSONFeed(const QByteArray& jsonFeed, FeedData& feedData) { ObjectsList output; QJsonDocument document = QJsonDocument::fromJson(jsonFeed); const QVariantMap head = document.toVariant().toMap(); const QVariantMap feed = head.value(QStringLiteral("feed")).toMap(); const QVariantList categories = feed.value(QStringLiteral("category")).toList(); for (const QVariant &c : categories) { const QVariantMap category = c.toMap(); bool groups = false; if (category.value(QStringLiteral("term")).toString() == QLatin1String("http://schemas.google.com/contact/2008#group")) { groups = true; } const QVariantList entries = feed.value(QStringLiteral("entry")).toList(); for (const QVariant &e : entries) { if (groups) { output << Private::JSONToContactsGroup(e.toMap()); } else { output << Private::JSONToContact(e.toMap()); } } } const QVariantList links = feed.value(QStringLiteral("link")).toList(); for (const QVariant &l : links) { const QVariantMap link = l.toMap(); if (link.value(QStringLiteral("rel")).toString() == QLatin1String("next")) { feedData.nextPageUrl = QUrl(link.value(QStringLiteral("href")).toString()); break; } } feedData.totalResults = Private::stringFromXMLMap(feed, QStringLiteral("openSearch$totalResults")).toInt(); feedData.startIndex = Private::stringFromXMLMap(feed, QStringLiteral("openSearch$startIndex")).toInt(); feedData.itemsPerPage = Private::stringFromXMLMap(feed, QStringLiteral("openSearch$itemsPerPage")).toInt(); return output; } QUrl fetchAllContactsUrl(const QString& user, bool showDeleted) { QUrl url(Private::GoogleApisUrl); url.setPath(Private::ContactsBasePath % QLatin1Char('/') % user % QLatin1String("/full")); QUrlQuery query(url); query.addQueryItem(QStringLiteral("alt"), QStringLiteral("json")); if (showDeleted) { query.addQueryItem(QStringLiteral("showdeleted"), QStringLiteral("true")); } url.setQuery(query); return url; } QUrl fetchContactUrl(const QString& user, const QString& contactID) { QString id; if (contactID.contains(QLatin1Char('/'))) { id = contactID.mid(contactID.lastIndexOf(QLatin1Char('/')) + 1); } else { id = contactID; } QUrl url(Private::GoogleApisUrl); url.setPath(Private::ContactsBasePath % QLatin1Char('/') % user % QLatin1String("/full/") % id); QUrlQuery query(url); query.addQueryItem(QStringLiteral("alt"), QStringLiteral("json")); url.setQuery(query); return url; } QUrl createContactUrl(const QString& user) { QUrl url(Private::GoogleApisUrl); url.setPath(Private::ContactsBasePath % QLatin1Char('/') % user % QLatin1String("/full")); return url; } QUrl updateContactUrl(const QString& user, const QString& contactID) { QString id; if (contactID.contains(QLatin1Char('/'))) { id = contactID.mid(contactID.lastIndexOf(QLatin1Char('/')) + 1); } else { id = contactID; } QUrl url(Private::GoogleApisUrl); url.setPath(Private::ContactsBasePath % QLatin1Char('/') % user % QLatin1String("/full/") % id); return url; } QUrl removeContactUrl(const QString& user, const QString& contactID) { QString id; if (contactID.contains(QLatin1Char('/'))) { id = contactID.mid(contactID.lastIndexOf(QLatin1Char('/')) + 1); } else { id = contactID; } QUrl url(Private::GoogleApisUrl); url.setPath(Private::ContactsBasePath % QLatin1Char('/') % user % QLatin1String("/full/") % id); return url; } QUrl fetchAllGroupsUrl(const QString &user) { QUrl url(Private::GoogleApisUrl); url.setPath(Private::ContactsGroupBasePath % QLatin1Char('/') % user % QLatin1String("/full")); QUrlQuery query(url); query.addQueryItem(QStringLiteral("alt"), QStringLiteral("json")); url.setQuery(query); return url; } QUrl fetchGroupUrl(const QString &user, const QString &groupId) { QString id; if (groupId.contains(QLatin1Char('/'))) { id = groupId.mid(groupId.lastIndexOf(QLatin1Char('/')) + 1); } else { id = groupId; } QUrl url(Private::GoogleApisUrl); url.setPath(Private::ContactsGroupBasePath % QLatin1Char('/') % user % QLatin1String("/base/") % id); QUrlQuery query(url); query.addQueryItem(QStringLiteral("alt"), QStringLiteral("json")); url.setQuery(query); return url; } QUrl createGroupUrl(const QString &user) { QUrl url(Private::GoogleApisUrl); url.setPath(Private::ContactsGroupBasePath % QLatin1Char('/') % user % QLatin1String("/full")); return url; } QUrl updateGroupUrl(const QString &user, const QString &groupId) { QString id; if (groupId.contains(QLatin1Char('/'))) { id = groupId.mid(groupId.lastIndexOf(QLatin1Char('/')) + 1); } else { id = groupId; } QUrl url(Private::GoogleApisUrl); url.setPath(Private::ContactsGroupBasePath % QLatin1Char('/') % user % QLatin1String("/full/") % id); return url; } QUrl removeGroupUrl(const QString &user, const QString &groupId) { QString id; if (groupId.contains(QLatin1Char('/'))) { id = groupId.mid(groupId.lastIndexOf(QLatin1Char('/')) + 1); } else { id = groupId; } QUrl url(Private::GoogleApisUrl); url.setPath(Private::ContactsGroupBasePath % QLatin1Char('/') % user % QLatin1String("/full/") % id); return url; } QUrl photoUrl(const QString& user, const QString& contactID) { QString id; if (contactID.contains(QLatin1Char('/'))) { id = contactID.mid(contactID.lastIndexOf(QLatin1Char('/')) + 1); } else { id = contactID; } QUrl url(Private::GoogleApisUrl); url.setPath(Private::PhotoBasePath % QLatin1Char('/') % user % QLatin1Char('/') % id); return url; } QString APIVersion() { return QStringLiteral("3.0"); } /*********************************** PRIVATE *************************************/ ObjectPtr Private::JSONToContactsGroup(const QVariantMap& data) { ContactsGroupPtr group(new ContactsGroup); group->setId(Private::stringFromXMLMap(data, QStringLiteral("id"))); group->setEtag(data.value(QStringLiteral("gd$etag")).toString()); group->setTitle(Private::stringFromXMLMap(data, QStringLiteral("title"))); group->setContent(Private::stringFromXMLMap(data, QStringLiteral("content"))); group->setUpdated(QDateTime::fromString(Private::stringFromXMLMap(data, QStringLiteral("updated")), Qt::ISODate)); if (data.contains(QStringLiteral("gContact$systemGroup"))) { group->setIsSystemGroup(true); } else { group->setIsSystemGroup(false); } return group; } ContactsGroupPtr JSONToContactsGroup(const QByteArray& jsonData) { QJsonDocument document = QJsonDocument::fromJson(jsonData); const QVariantMap data = document.toVariant().toMap(); const QVariantMap entry = data.value(QStringLiteral("entry")).toMap(); const QVariantList categories = entry.value(QStringLiteral("category")).toList(); bool isGroup = false; for (const QVariant &c : categories) { const QVariantMap category = c.toMap(); if (category.value(QStringLiteral("term")).toString() == QLatin1String("http://schemas.google.com/contact/2008#group")) { isGroup = true; break; } } if (!isGroup) { return ContactsGroupPtr(); } return Private::JSONToContactsGroup(entry).staticCast(); } ObjectPtr Private::JSONToContact(const QVariantMap& data) { ContactPtr contact(new Contact); /* Google contact ID */ contact->setUid(Private::stringFromXMLMap(data, QStringLiteral("id"))); /* Google ETAG. This can be used to identify if the item was changed remotely */ contact->setEtag(data.value(QStringLiteral("gd$etag")).toString()); /* Date and time when contact was updated on the remote server */ contact->setUpdated(QDateTime::fromString(Private::stringFromXMLMap(data, QStringLiteral("updated")), Qt::ISODate)); /* If the contact was deleted, we don't need more info about it. * Just store our own flag, which will be then parsed by the resource * itself. */ contact->setDeleted(data.value(QStringLiteral("gd$deleted")).isValid()); /* Store URL of the picture. The URL will be used later by PhotoJob to fetch the picture * itself. */ const QVariantList links = data.value(QStringLiteral("link")).toList(); for (const QVariant &link : links) { const QVariantMap linkMap = link.toMap(); if (linkMap.value(QStringLiteral("rel")).toString() == QLatin1String("http://schemas.google.com/contacts/2008/rel#photo")) { contact->setPhotoUrl(linkMap.value(QStringLiteral("href")).toString()); } } /* Name */ if (data.contains(QStringLiteral("title"))) { contact->setName(Private::stringFromXMLMap(data, QStringLiteral("title"))); } /* Formatted name */ if (data.contains(QStringLiteral("gd$name"))) { const QVariantMap name = data.value(QStringLiteral("gd$name")).toMap(); if (name.contains(QStringLiteral("gd$fullName"))) { contact->setFormattedName(Private::stringFromXMLMap(name, QStringLiteral("gd$fullName"))); } if (name.contains(QStringLiteral("gd$givenName"))) { contact->setGivenName(Private::stringFromXMLMap(name, QStringLiteral("gd$givenName"))); } if (name.contains(QStringLiteral("gd$familyName"))) { contact->setFamilyName(Private::stringFromXMLMap(name, QStringLiteral("gd$familyName"))); } if (name.contains(QStringLiteral("gd$additionalName"))) { contact->setAdditionalName(Private::stringFromXMLMap(name, QStringLiteral("gd$additionalName"))); } if (name.contains(QStringLiteral("gd$namePrefix"))) { contact->setPrefix(Private::stringFromXMLMap(name, QStringLiteral("gd$namePrefix"))); } if (name.contains(QStringLiteral("gd$nameSuffix"))) { contact->setSuffix(Private::stringFromXMLMap(name, QStringLiteral("gd$nameSuffix"))); } } /* Note */ if (data.contains(QStringLiteral("content"))) { contact->setNote(Private::stringFromXMLMap(data, QStringLiteral("content"))); } /* Organization (work) - KABC supports only one organization */ if (data.contains(QStringLiteral("gd$organization"))) { const QVariantList organizations = data.value(QStringLiteral("gd$organization")).toList(); const QVariantMap organization = organizations.first().toMap(); if (organization.contains(QStringLiteral("gd$orgName"))) { contact->setOrganization(Private::stringFromXMLMap(organization, QStringLiteral("gd$orgName"))); } if (organization.contains(QStringLiteral("gd$orgDepartment"))) { contact->setDepartment(Private::stringFromXMLMap(organization, QStringLiteral("gd$orgDepartment"))); } if (organization.contains(QStringLiteral("gd$orgTitle"))) { contact->setTitle(Private::stringFromXMLMap(organization, QStringLiteral("gd$orgTitle"))); } if (organization.contains(QStringLiteral("gd$where"))) { contact->setOffice(Private::stringFromXMLMap(organization, QStringLiteral("gd$where"))); } } /* Nickname */ if (data.contains(QStringLiteral("gContact$nickname"))) { contact->setNickName(Private::stringFromXMLMap(data, QStringLiteral("gContact$nickname"))); } /* Occupation (= organization/title) */ if (data.contains(QStringLiteral("gContact$occupation"))) { contact->setProfession(Private::stringFromXMLMap(data, QStringLiteral("gContact$occupation"))); } /* Relationships */ if (data.contains(QStringLiteral("gContact$relation"))) { const QVariantList relations = data.value(QStringLiteral("gContact$relation")).toList(); for (const QVariant &r : relations) { const QVariantMap relation = r.toMap(); if (relation.value(QStringLiteral("rel")).toString() == QLatin1String("spouse")) { contact->setSpousesName(relation.value(QStringLiteral("$t")).toString()); continue; } if (relation.value(QStringLiteral("rel")).toString() == QLatin1String("manager")) { contact->setManagersName(relation.value(QStringLiteral("$t")).toString()); continue; } if (relation.value(QStringLiteral("rel")).toString() == QLatin1String("assistant")) { contact->setAssistantsName(relation.value(QStringLiteral("$t")).toString()); continue; } } } /* Anniversary */ if (data.contains(QStringLiteral("gContact$event"))) { const QVariantList events = data.value(QStringLiteral("gContact$event")).toList(); for (const QVariant &e : events) { const QVariantMap event = e.toMap(); if (event.value(QStringLiteral("rel")).toString() == QLatin1String("anniversary")) { QVariantMap when = event.value(QStringLiteral("gd$when")).toMap(); contact->setAnniversary(QDate::fromString(when.value(QStringLiteral("startTime")).toString(), Qt::ISODate)); } } } /* Websites */ if (data.contains(QStringLiteral("gContact$website"))) { const QVariantList websites = data.value(QStringLiteral("gContact$website")).toList(); for (const QVariant &w : websites) { const QVariantMap web = w.toMap(); const auto rel = web.value(QStringLiteral("rel")).toString(); const QUrl url(web.value(QStringLiteral("href")).toString()); if (rel == QLatin1String("home-page")) { KContacts::ResourceLocatorUrl locator; locator.setUrl(url); locator.setParameters({ { QStringLiteral("TYPE"), { QStringLiteral("HOME") } } }); contact->insertExtraUrl(locator); } else if (rel == QLatin1String("work")) { KContacts::ResourceLocatorUrl locator; locator.setUrl(url); locator.setParameters({ { QStringLiteral("TYPE"), { QStringLiteral("WORK") } } }); contact->insertExtraUrl(locator); } else if (rel == QLatin1String("profile")) { KContacts::ResourceLocatorUrl locator; locator.setUrl(url); locator.setParameters({ { QStringLiteral("TYPE"), { QStringLiteral("PROFILE") } } }); contact->insertExtraUrl(locator); } else if (rel == QLatin1String("blog")) { contact->setBlogFeed(url); } else { KContacts::ResourceLocatorUrl locator; locator.setUrl(url); locator.setParameters({ { QStringLiteral("TYPE"), { rel } } }); contact->insertExtraUrl(locator); } } } /* Emails */ const QVariantList emails = data.value(QStringLiteral("gd$email")).toList(); for (const QVariant & em : emails) { const QVariantMap email = em.toMap(); const auto emailType = Contact::emailSchemeToProtocolName(email.value(QStringLiteral("rel")).toString()); const QMap params({ { QStringLiteral("TYPE"), { emailType } } }); contact->insertEmail(email.value(QStringLiteral("address")).toString(), email.value(QStringLiteral("primary")).toBool(), params); } /* IMs */ const QVariantList ims = data.value(QStringLiteral("gd$im")).toList(); for (const QVariant & i : ims) { const QVariantMap im = i.toMap(); const QString protocol = Contact::IMSchemeToProtocolName(im.value(QStringLiteral("protocol")).toString()); contact->insertCustom(QLatin1String("messaging/") + protocol, QStringLiteral("All"), im.value(QStringLiteral("address")).toString()); } /* Phone numbers */ const QVariantList phones = data.value(QStringLiteral("gd$phoneNumber")).toList(); for (const QVariant & p : phones) { const QVariantMap phone = p.toMap(); KContacts::PhoneNumber phoneNumber( phone.value(QStringLiteral("$t")).toString(), Contact::phoneSchemeToType(phone.value(QStringLiteral("rel")).toString())); phoneNumber.setId(phoneNumber.number()); contact->insertPhoneNumber(phoneNumber); } /* Addresses */ const QVariantList addresses = data.value(QStringLiteral("gd$structuredPostalAddress")).toList(); for (const QVariant &a : addresses) { const QVariantMap address = a.toMap(); KContacts::Address addr; addr.setId(QString::number(contact->addresses().count())); if (!address.contains(QStringLiteral("gd$city")) && !address.contains(QStringLiteral("gd$country")) && !address.contains(QStringLiteral("gd$postcode")) && !address.contains(QStringLiteral("gd$region")) && !address.contains(QStringLiteral("gd$pobox"))) { addr.setExtended(Private::stringFromXMLMap(address, QStringLiteral("gd$street"))); } else { if (address.contains(QStringLiteral("gd$street"))) { addr.setStreet(Private::stringFromXMLMap(address, QStringLiteral("gd$street"))); } if (address.contains(QStringLiteral("gd$country"))) { addr.setCountry(Private::stringFromXMLMap(address, QStringLiteral("gd$country"))); } if (address.contains(QStringLiteral("gd$city"))) { addr.setLocality(Private::stringFromXMLMap(address, QStringLiteral("gd$city"))); } if (address.contains(QStringLiteral("gd$postcode"))) { addr.setPostalCode(Private::stringFromXMLMap(address, QStringLiteral("gd$postcode"))); } if (address.contains(QStringLiteral("gdregion"))) { addr.setRegion(Private::stringFromXMLMap(address, QStringLiteral("gd$region"))); } if (address.contains(QStringLiteral("gd$pobox"))) { addr.setPostOfficeBox(Private::stringFromXMLMap(address, QStringLiteral("gd$pobox"))); } } addr.setType(Contact::addressSchemeToType(address.value(QStringLiteral("rel")).toString())); contact->insertAddress(addr); } /* Birthday */ const QVariantMap bDay = data.value(QStringLiteral("gContact$birthday")).toMap(); if (!bDay.isEmpty()) { QString birthday = bDay.value(QStringLiteral("when")).toString(); /* Birthdays in format "--MM-DD" are valid and mean that no year has * been specified. Since KABC does not support birthdays without year, * we simulate that by specifying a fake year - 1900 */ if (birthday.startsWith(QLatin1String("--"))) { birthday = QLatin1String("1900") + birthday.mid(1); } contact->setBirthday(QDateTime::fromString(birthday, QStringLiteral("yyyy-MM-dd"))); } /* User-defined fields */ const QVariantList userDefined = data.value(QStringLiteral("gContact$userDefinedField")).toList(); for (const QVariant & u : userDefined) { const QVariantMap field = u.toMap(); contact->insertCustom(QStringLiteral("KADDRESSBOOK"), field.value(QStringLiteral("key")).toString(), field.value(QStringLiteral("value")).toString()); } /* Groups */ const QVariantList groups = data.value(QStringLiteral("gContact$groupMembershipInfo")).toList(); QStringList groupsList; for (const QVariant & g : groups) { const QVariantMap group = g.toMap(); if (group.value(QStringLiteral("deleted")).toBool() == false) { groupsList.append(group.value(QStringLiteral("href")).toString()); } } contact->insertCustom(QStringLiteral("GCALENDAR"), QStringLiteral("groupMembershipInfo"), groupsList.join(QStringLiteral(","))); return contact; } ContactPtr JSONToContact(const QByteArray& jsonData) { QJsonDocument document = QJsonDocument::fromJson(jsonData); const QVariantMap data = document.toVariant().toMap(); const QVariantMap entry = data.value(QStringLiteral("entry")).toMap(); const QVariantList categories = entry.value(QStringLiteral("category")).toList(); bool isContact = false; for (const QVariant &c : categories) { const QVariantMap category = c.toMap(); if (category.value(QStringLiteral("term")).toString() == QLatin1String("http://schemas.google.com/contact/2008#contact")) { isContact = true; break; } } if (!isContact) { return ContactPtr(); } return Private::JSONToContact(entry).staticCast(); } QByteArray contactToXML(const ContactPtr& contact) { QByteArray output; QStringList parsedCustoms; /* Name */ output.append(""); if (!contact->givenName().isEmpty()) { output.append("").append(contact->givenName().toHtmlEscaped().toUtf8()).append(""); } if (!contact->familyName().isEmpty()) { output.append("").append(contact->familyName().toHtmlEscaped().toUtf8()).append(""); } if (!contact->assembledName().isEmpty()) { output.append("").append(contact->assembledName().toHtmlEscaped().toUtf8()).append(""); } if (!contact->additionalName().isEmpty()) { output.append("").append(contact->additionalName().toHtmlEscaped().toUtf8()).append(""); } if (!contact->prefix().isEmpty()) { output.append("").append(contact->prefix().toHtmlEscaped().toUtf8()).append(""); } if (!contact->suffix().isEmpty()) { output.append("").append(contact->suffix().toHtmlEscaped().toUtf8()).append(""); } output.append(""); /* Notes */ if (!contact->note().isEmpty()) { output.append("").append(contact->note().toHtmlEscaped().toUtf8()).append(""); } /* Organization (work) */ QByteArray org; const QString office = contact->office(); if (!contact->organization().isEmpty()) { org.append("").append(contact->organization().toHtmlEscaped().toUtf8()).append(""); } if (!contact->department().isEmpty()) { org.append("").append(contact->department().toHtmlEscaped().toUtf8()).append(""); } if (!contact->title().isEmpty()) { org.append("").append(contact->title().toHtmlEscaped().toUtf8()).append(""); } if (!office.isEmpty()) { org.append("").append(office.toHtmlEscaped().toUtf8()).append(""); parsedCustoms << QStringLiteral("KADDRESSBOOK-X-Office"); } if (!org.isEmpty()) { output.append("").append(org).append(""); } /* Nickname */ if (!contact->nickName().isEmpty()) { output.append("").append(contact->nickName().toHtmlEscaped().toUtf8()).append(""); } /* Occupation */ if (!contact->profession().isEmpty()) { output.append("").append(contact->profession().toHtmlEscaped().toUtf8()).append(""); parsedCustoms << QStringLiteral("KADDRESSBOOK-X-Profession"); } /* Spouse */ const QString spouse = contact->spousesName(); if (!spouse.isEmpty()) { output.append("").append(spouse.toHtmlEscaped().toUtf8()).append(""); parsedCustoms << QStringLiteral("KADDRESSBOOK-X-SpousesName"); } /* Manager */ const QString manager = contact->managersName(); if (!manager.isEmpty()) { output.append("").append(manager.toHtmlEscaped().toUtf8()).append(""); parsedCustoms << QStringLiteral("KADDRESSBOOK-X-ManagersName"); } /* Assistant */ const QString assistant = contact->assistantsName(); if (!assistant.isEmpty()) { output.append("").append(assistant.toHtmlEscaped().toUtf8()).append(""); parsedCustoms << QStringLiteral("KADDRESSBOOK-X-AssistantsName"); } /* Anniversary */ const QString anniversary = contact->anniversary().toString(Qt::ISODate); if (!anniversary.isEmpty()) { output.append(""); parsedCustoms << QStringLiteral("KADDRESSBOOK-X-Anniversary"); } /* Blog */ const QString blog = contact->blogFeed().url(); if (!blog.isEmpty()) { output.append(""); parsedCustoms << QStringLiteral("KADDRESSBOOK-BlogFeed"); } /* URLs */ const auto extraUrls = contact->extraUrlList(); for (const auto &extraUrl : extraUrls) { const auto rels = extraUrl.parameters().value(QStringLiteral("TYPE")); auto rel = rels.isEmpty() ? "other" : rels.at(0).toLower().toUtf8(); if (rel == "home") { rel = "home-page"; } output.append(""); } /* Emails */ const auto preferredEmail = contact->preferredEmail(); Q_FOREACH(const auto &email, contact->emailList()) { const auto rels = email.parameters().value(QStringLiteral("TYPE"), { QStringLiteral("home") }); const auto rel = Private::addRelSchema(rels.isEmpty() ? "home" : rels.at(0).toLower().toUtf8()); output.append(""); } /* IMs */ const QString im_str = QStringLiteral(""); Q_FOREACH(const QString &im, contact->customs()) { if (im.startsWith(QLatin1String("messaging/"))) { QString key = im.left(im.indexOf(QLatin1Char(':'))); QString value = im.mid(im.indexOf(QLatin1Char(':')) + 1); QString proto = key.mid(10); proto.chop(4); bool primary = (contact->custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-IMAddress")) == value); - output.append(im_str.arg(value, Contact::IMProtocolNameToScheme(proto), - (primary ? QStringLiteral("true") : QStringLiteral("false"))).toUtf8()); + output.append(im_str.arg(value, Contact::IMProtocolNameToScheme(proto), Utils::bool2Str(primary)).toUtf8()); parsedCustoms << key; /* X-messaging is probably a new key (?) used by KAddressbook when importing * contacts from vCard. */ } else if (im.startsWith(QLatin1String("X-messaging"))) { const QString key = im.left(im.indexOf(QLatin1Char(':'))); const QString value = im.mid(im.indexOf(QLatin1Char(':')) + 1); QString proto = key.mid(12); /* strlen("X-messaging/") */ if (proto.endsWith(QLatin1String("-All"))) { proto.chop(4); } output.append(im_str.arg(value, proto, QStringLiteral("false")).toUtf8()); parsedCustoms << key; } } parsedCustoms << QStringLiteral("KADDRESSBOOK-X-IMAddress"); /* Phone numbers */ const QString phone_str = QStringLiteral("%2"); Q_FOREACH(const KContacts::PhoneNumber &number, contact->phoneNumbers()) { output.append(phone_str.arg(Contact::phoneTypeToScheme(number.type()), number.number()).toUtf8()); } /* Address */ Q_FOREACH(const KContacts::Address &address, contact->addresses()) { output.append(""); if (!address.locality().isEmpty()) output.append("").append(address.locality().toHtmlEscaped().toUtf8()).append(""); if (!address.street().isEmpty()) output.append("").append(address.street().toHtmlEscaped().toUtf8()).append(""); if (!address.region().isEmpty()) output.append("").append(address.region().toHtmlEscaped().toUtf8()).append(""); if (!address.postalCode().isEmpty()) output.append("").append(address.postalCode().toHtmlEscaped().toUtf8()).append(""); if (!address.country().isEmpty()) output.append("").append(address.country().toHtmlEscaped().toUtf8()).append(""); if (!address.formattedAddress().isEmpty()) output.append("").append(address.formattedAddress().toHtmlEscaped().toUtf8()).append(""); output.append(""); } /* Birthday */ const QDate birthday = contact->birthday().date(); if (birthday.isValid()) { QString birthdayStr; /* We use year 1900 as a fake year for birthdays without a year specified. * Here we assume that nobody actually has a contact born in 1900 and so * we replace 1900 by "-", so that we get "--MM-dd" date, which is a valid * birthday date according to RFC6350 */ if (birthday.year() == 1900) { birthdayStr = birthday.toString(QStringLiteral("--MM-dd")); } else { birthdayStr = birthday.toString(QStringLiteral("yyyy-MM-dd")); } output.append(""); } const QStringList groups = contact->custom(QStringLiteral("GCALENDAR"), QStringLiteral("groupMembershipInfo")).split(QLatin1Char(',')); qCDebug(KGAPIDebug) << groups; if ((!groups.isEmpty()) && !groups.at(0).isEmpty()) { for (const QString & group :groups) { bool removed = contact->groupIsDeleted(group); if (!removed) output.append(QStringLiteral("").arg(group).toUtf8()); } } parsedCustoms << QStringLiteral("GCALENDAR-groupMembershipInfo"); /* User-defined fields */ const QStringList customs = contact->customs(); const QString defined_str = QStringLiteral(""); for (const QString &customStr : customs) { QString key = customStr.left(customStr.indexOf(QLatin1Char(':'))); if (!parsedCustoms.contains(key)) { if (key.startsWith(QLatin1String("KADDRESSBOOK-"))) { key = key.remove(QStringLiteral("KADDRESSBOOK-")); } const QString value = customStr.mid(customStr.indexOf(QLatin1Char(':')) + 1); output.append(defined_str.arg(key.toHtmlEscaped(), value).toHtmlEscaped().toUtf8()); } } return output; } QByteArray contactsGroupToXML(const ContactsGroupPtr& group) { QByteArray output; output.append("").append(group->title().toHtmlEscaped().toUtf8()).append(""); output.append("").append(group->content().toHtmlEscaped().toUtf8()).append(""); return output; } ContactPtr XMLToContact(const QByteArray& xmlData) { QByteArray xmlDoc; /* Document without header is not valid and Qt won't parse it */ if (!xmlData.contains(""); } xmlDoc.append(xmlData); QDomDocument doc; doc.setContent(xmlDoc); const QDomNodeList entry = doc.elementsByTagName(QStringLiteral("entry")); QDomNodeList data; if (!entry.isEmpty()) { data = entry.at(0).childNodes(); } else { return ContactPtr(); } bool isGroup = false; for (int i = 0; i < data.count(); ++i) { const QDomNode n = data.at(i); const QDomElement e = n.toElement(); if (((e.tagName() == QLatin1String("category")) && (e.attribute(QStringLiteral("term")) == QLatin1String("http://schemas.google.com/contact/2008#group"))) || ((e.tagName() == QLatin1String("atom:category")) && (e.attribute(QStringLiteral("term")) == QLatin1String("http://schemas.google.com/g/2005#group")))) { isGroup = true; break; } } if (isGroup) { return ContactPtr(); } QStringList groups; ContactPtr contact(new Contact); contact->setEtag(entry.at(0).toElement().attribute(QStringLiteral("gd:etag"))); for (int i = 0; i < data.count(); ++i) { const QDomNode n = data.at(i); const QDomElement e = n.toElement(); if (e.tagName() == QLatin1String("id")) { contact->setUid(e.text()); continue; } /* ETag */ if (e.tagName() == QLatin1String("etag")) { contact->setEtag(e.text()); continue; } if (e.tagName() == QLatin1String("gd:name")) { QDomNodeList l = e.childNodes(); for (int i = 0; i < l.length(); ++i) { const QDomElement el = l.at(i).toElement(); if (el.tagName() == QLatin1String("gd:fullName")) { contact->setFormattedName(el.text()); continue; } if (el.tagName() == QLatin1String("gd:givenName")) { contact->setGivenName(el.text()); continue; } if (el.tagName() == QLatin1String("gd:familyName")) { contact->setFamilyName(el.text()); continue; } if (el.tagName() == QLatin1String("gd:additionalName")) { contact->setAdditionalName(el.text()); continue; } if (el.tagName() == QLatin1String("gd:namePrefix")) { contact->setPrefix(el.text()); continue; } if (el.tagName() == QLatin1String("gd:nameSuffix")) { contact->setSuffix(el.text()); continue; } } continue; } /* If the contact was deleted, we don't need more info about it. * Just store our own flag, which will be then parsed by the resource * itself. */ contact->setDeleted(e.tagName() == QLatin1String("gd:deleted")); if (e.tagName() == QLatin1String("updated")) { contact->setUpdated(QDateTime::fromString(e.text(), Qt::ISODate)); } /* Store URL of the picture. The URL will be used later by PhotoJob to fetch the picture * itself. */ if ((e.tagName() == QLatin1String("link")) && (e.attribute(QStringLiteral("rel")) == QLatin1String("http://schemas.google.com/contacts/2008/rel#photo"))) { contact->setPhotoUrl(e.attribute(QStringLiteral("href"))); /* URL */ continue; } /* Name */ if (e.tagName() == QLatin1String("title")) { contact->setName(e.text()); continue; } /* Note */ if (e.tagName() == QLatin1String("content")) { contact->setNote(e.text()); continue; } /* Organization (work) - KABC supports only organization */ if (e.tagName() == QLatin1String("gd:organization")) { const QDomNodeList l = e.childNodes(); for (int i = 0; i < l.length(); ++i) { const QDomElement el = l.at(i).toElement(); if (el.tagName() == QLatin1String("gd:orgName")) { contact->setOrganization(el.text()); continue; } if (el.tagName() == QLatin1String("gd:orgDepartment")) { contact->setDepartment(el.text()); continue; } if (el.tagName() == QLatin1String("gd:orgTitle")) { contact->setTitle(el.text()); continue; } if (el.tagName() == QLatin1String("gd:where")) { contact->setOffice(el.text()); continue; } } continue; } /* Nickname */ if (e.tagName() == QLatin1String("gContact:nickname")) { contact->setNickName(e.text()); continue; } /* Occupation (= organization/title) */ if (e.tagName() == QLatin1String("gContact:occupation")) { contact->setProfession(e.text()); continue; } /* Relationships */ if (e.tagName() == QLatin1String("gContact:relation")) { if (e.attribute(QStringLiteral("rel"), QString()) == QLatin1String("spouse")) { contact->setSpousesName(e.text()); continue; } if (e.attribute(QStringLiteral("rel"), QString()) == QLatin1String("manager")) { contact->setManagersName(e.text()); continue; } if (e.attribute(QStringLiteral("rel"), QString()) == QLatin1String("assistant")) { contact->setAssistantsName(e.text()); continue; } continue; } /* Anniversary */ if (e.tagName() == QLatin1String("gContact:event")) { if (e.attribute(QStringLiteral("rel"), QString()) == QLatin1String("anniversary")) { QDomElement w = e.firstChildElement(QStringLiteral("gd:when")); contact->setAnniversary(QDate::fromString(w.attribute(QStringLiteral("startTime"), QString()), Qt::ISODate)); } continue; } /* Websites */ if (e.tagName() == QLatin1String("gContact:website")) { if (e.attribute(QStringLiteral("rel"), QString()) == QLatin1String("blog")) { contact->setBlogFeed(QUrl(e.attribute(QStringLiteral("href"), QString()))); continue; } KContacts::ResourceLocatorUrl url; QString rel = e.attribute(QStringLiteral("rel")).toUpper(); if (rel == QLatin1String("home-page")) { rel = QStringLiteral("HOME"); } url.setParameters({ { QStringLiteral("TYPE"), { rel } } }); url.setUrl(QUrl(e.attribute(QStringLiteral("href"), {}))); contact->insertExtraUrl(url); continue; } /* Emails */ if (e.tagName() == QLatin1String("gd:email")) { const auto emailType = Contact::emailSchemeToProtocolName(e.attribute(QStringLiteral("rel"), {})); const QMap params({ { QStringLiteral("TYPE"), { emailType } } }); contact->insertEmail(e.attribute(QStringLiteral("address")), (e.attribute(QStringLiteral("primary")).toLower() == QLatin1String("true")), params); continue; } /* IMs */ if (e.tagName() == QLatin1String("gd:im")) { contact->insertCustom(QLatin1String("messaging/") + Contact::IMSchemeToProtocolName(e.attribute(QStringLiteral("protocol"))), QStringLiteral("All"), e.attribute(QStringLiteral("address"))); continue; } /* Phone numbers */ if (e.tagName() == QLatin1String("gd:phoneNumber")) { KContacts::PhoneNumber number(e.text(), Contact::phoneSchemeToType(e.attribute(QStringLiteral("rel")))); number.setId(e.text()); contact->insertPhoneNumber(number); continue; } /* Addresses */ if (e.tagName() == QLatin1String("gd:structuredPostalAddress")) { KContacts::Address address; address.setId(QString::number(contact->addresses().count())); const QDomNodeList l = e.childNodes(); for (int i = 0; i < l.length(); ++i) { const QDomElement el = l.at(i).toElement(); if (el.tagName() == QLatin1String("gd:street")) { address.setStreet(el.text()); continue; } if (el.tagName() == QLatin1String("gd:country")) { address.setCountry(el.text()); continue; } if (el.tagName() == QLatin1String("gd:city")) { address.setLocality(el.text()); continue; } if (el.tagName() == QLatin1String("gd:postcode")) { address.setPostalCode(el.text()); continue; } if (el.tagName() == QLatin1String("gd:region")) { address.setRegion(el.text()); continue; } if (el.tagName() == QLatin1String("gd:pobox")) { address.setPostOfficeBox(el.text()); continue; } } address.setType(Contact::addressSchemeToType(e.attribute(QStringLiteral("rel")), (e.attribute(QStringLiteral("primary")) == QLatin1String("true")))); contact->insertAddress(address); continue; } /* Birthday */ if (e.tagName() == QLatin1String("gContact:birthday")) { QString birthday = e.attribute(QStringLiteral("when")); /* Birthdays in format "--MM-DD" are valid and mean that no year has * been specified. Since KABC does not support birthdays without year, * we simulate that by specifying a fake year - 1900 */ if (birthday.startsWith(QLatin1String("--"))) { birthday = QLatin1String("1900") + birthday.mid(1); } contact->setBirthday(QDateTime::fromString(birthday, QStringLiteral("yyyy-MM-dd"))); continue; } /* User-defined tags */ if (e.tagName() == QLatin1String("gContact:userDefinedField")) { contact->insertCustom(QStringLiteral("KADDRESSBOOK"), e.attribute(QStringLiteral("key"), QString()), e.attribute(QStringLiteral("value"), QString())); continue; } if (e.tagName() == QLatin1String("gContact:groupMembershipInfo")) { if (e.hasAttribute(QStringLiteral("deleted")) || e.attribute(QStringLiteral("deleted")).toInt() == false) { groups.append(e.attribute(QStringLiteral("href"))); } } } contact->insertCustom(QStringLiteral("GCALENDAR"), QStringLiteral("groupMembershipInfo"), groups.join(QStringLiteral(","))); return contact; } ContactsGroupPtr XMLToContactsGroup(const QByteArray& xmlData) { QByteArray xmlDoc; /* Document without header is not valid and Qt won't parse it */ if (!xmlData.contains(""); } xmlDoc.append(xmlData); QDomDocument doc; doc.setContent(xmlDoc); const QDomNodeList entry = doc.elementsByTagName(QStringLiteral("entry")); QDomNodeList data; if (!entry.isEmpty()) { data = entry.at(0).childNodes(); } else { return ContactsGroupPtr(); } bool isGroup = false; for (int i = 0; i < data.count(); ++i) { const QDomNode n = data.at(i); const QDomElement e = n.toElement(); if (((e.tagName() == QLatin1String("category")) && (e.attribute(QStringLiteral("term")) == QLatin1String("http://schemas.google.com/contact/2008#group"))) || ((e.tagName() == QLatin1String("atom:category")) && (e.attribute(QStringLiteral("term")) == QLatin1String("http://schemas.google.com/g/2005#group")))) { isGroup = true; break; } } if (!isGroup) { return ContactsGroupPtr(); } ContactsGroupPtr group(new ContactsGroup); QStringList groups; for (int i = 0; i < data.count(); ++i) { const QDomNode n = data.at(i); const QDomElement e = n.toElement(); if (e.tagName() == QLatin1String("id")) { group->setId(e.text()); continue; } if (e.tagName() == QLatin1String("updated")) { group->setUpdated(QDateTime::fromString(e.text(), Qt::ISODate)); continue; } if ((e.tagName() == QLatin1String("title")) || (e.tagName() == QLatin1String("atom:title"))) { group->setTitle(e.text()); continue; } if ((e.tagName() == QLatin1String("content")) || (e.tagName() == QLatin1String("atom:content"))) { group->setContent(e.text()); continue; } if (e.tagName() == QLatin1String("gContact:systemGroup")) { group->setIsSystemGroup(true); continue; } } return group; } } // namespace ContactsService } // namespace KGAPI2 diff --git a/src/core/job.cpp b/src/core/job.cpp index e4f1a40..79b2b9d 100644 --- a/src/core/job.cpp +++ b/src/core/job.cpp @@ -1,539 +1,540 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "job.h" #include "job_p.h" #include "account.h" #include "networkaccessmanagerfactory_p.h" #include "../debug.h" #include "authjob.h" +#include "utils.h" #include #include #include #include #include using namespace KGAPI2; FileLogger *FileLogger::sInstance = nullptr; FileLogger::FileLogger() { if (!qEnvironmentVariableIsSet("KGAPI_SESSION_LOGFILE")) { return; } QString filename = QString::fromLocal8Bit(qgetenv("KGAPI_SESSION_LOGFILE")) + QLatin1Char('.') + QString::number(QCoreApplication::applicationPid()); mFile.reset(new QFile(filename)); if (!mFile->open(QIODevice::WriteOnly | QIODevice::Truncate)) { qCWarning(KGAPIDebug) << "Failed to open logging file" << filename << ":" << mFile->errorString(); mFile.reset(); } } FileLogger::~FileLogger() {} FileLogger *FileLogger::self() { if (!sInstance) { sInstance = new FileLogger(); } return sInstance; } void FileLogger::logRequest(const QNetworkRequest &request, const QByteArray &rawData) { if (!mFile) { return; } QTextStream stream(mFile.data()); stream << "C: " << request.url().toDisplayString() << "\n"; const auto headers = request.rawHeaderList(); for (const auto &header : headers) { stream << " " << header << ": " << request.rawHeader(header) << "\n"; } stream << " " << rawData << "\n\n"; mFile->flush(); } void FileLogger::logReply(const QNetworkReply *reply, const QByteArray &rawData) { if (!mFile) { return; } QTextStream stream(mFile.data()); stream << "S: " << reply->url().toDisplayString() << "\n"; const auto headers = reply->rawHeaderList(); for (const auto &header : headers) { stream << " " << header << ": " << reply->rawHeader(header) << "\n"; } stream << " " << rawData << "\n\n"; mFile->flush(); } Job::Private::Private(Job *parent): isRunning(false), error(KGAPI2::NoError), accessManager(nullptr), maxTimeout(0), prettyPrint(false), q(parent) { } void Job::Private::init() { QTimer::singleShot(0, q, [this]() { _k_doStart(); }); accessManager = NetworkAccessManagerFactory::instance()->networkAccessManager(q); connect(accessManager, &QNetworkAccessManager::finished, q, [this](QNetworkReply *reply) { _k_replyReceived(reply); }); dispatchTimer = new QTimer(q); connect(dispatchTimer, &QTimer::timeout, q, [this]() { _k_dispatchTimeout(); }); } QString Job::Private::parseErrorMessage(const QByteArray &json) { QJsonDocument document = QJsonDocument::fromJson(json); if (!document.isNull()) { QVariantMap map = document.toVariant().toMap(); QString message; if (map.contains(QStringLiteral("error"))) { map = map.value(QStringLiteral("error")).toMap(); } if (map.contains(QStringLiteral("message"))) { message.append(map.value(QStringLiteral("message")).toString()); } else { message = QLatin1String(json); } return message; } else { return QLatin1String(json); } } void Job::Private::_k_doStart() { isRunning = true; q->aboutToStart(); q->start(); } void Job::Private::_k_doEmitFinished() { Q_EMIT q->finished(q); } void Job::Private::_k_replyReceived(QNetworkReply* reply) { int replyCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (replyCode == 0) { /* Workaround for a bug (??), when QNetworkReply does not report HTTP/1.1 401 Unauthorized * as an error. */ if (!reply->rawHeaderList().isEmpty()) { QString status = QLatin1String(reply->rawHeaderList().first()); if (status.startsWith(QLatin1String("HTTP/1.1 401"))) replyCode = KGAPI2::Unauthorized; } } const QByteArray rawData = reply->readAll(); qCDebug(KGAPIDebug) << "Received reply from" << reply->url(); qCDebug(KGAPIDebug) << "Status code: " << replyCode; FileLogger::self()->logReply(reply, rawData); switch (replyCode) { case KGAPI2::NoError: case KGAPI2::OK: /** << OK status (fetched, updated, removed) */ case KGAPI2::Created: /** << OK status (created) */ case KGAPI2::NoContent: /** << OK status (removed task using Tasks API) */ break; case KGAPI2::TemporarilyMoved: { /** << Temporarily moved - Google provides a new URL where to send the request */ qCDebug(KGAPIDebug) << "Google says: Temporarily moved to " << reply->header(QNetworkRequest::LocationHeader).toUrl(); QNetworkRequest request = currentRequest.request; request.setUrl(reply->header(QNetworkRequest::LocationHeader).toUrl()); q->enqueueRequest(request, currentRequest.rawData, currentRequest.contentType); return; } case KGAPI2::BadRequest: /** << Bad request - malformed data, API changed, something went wrong... */ qCWarning(KGAPIDebug) << "Bad request, Google replied '" << rawData << "'"; q->setError(KGAPI2::BadRequest); q->setErrorString(tr("Bad request.")); q->emitFinished(); return; case KGAPI2::Unauthorized: /** << Unauthorized - Access token has expired, request a new token */ qCWarning(KGAPIDebug) << "Unauthorized. Access token has expired or is invalid."; q->setError(KGAPI2::Unauthorized); q->setErrorString(tr("Invalid authentication.")); q->emitFinished(); return; case KGAPI2::Forbidden: { qCWarning(KGAPIDebug) << "Requested resource is forbidden."; const QString msg = parseErrorMessage(rawData); q->setError(KGAPI2::Forbidden); q->setErrorString(tr("Requested resource is forbidden.\n\nGoogle replied '%1'").arg(msg)); q->emitFinished(); return; } case KGAPI2::NotFound: { qCWarning(KGAPIDebug) << "Requested resource does not exist"; const QString msg = parseErrorMessage(rawData); q->setError(KGAPI2::NotFound); q->setErrorString(tr("Requested resource does not exist.\n\nGoogle replied '%1'").arg(msg)); // don't emit finished() here, we can get 404 when fetching contact photos or so, // in that case 404 is not fatal. Let subclass decide whether to terminate or not. q->handleReply(reply, rawData); if (requestQueue.isEmpty()) { q->emitFinished(); } return; } case KGAPI2::Conflict: { qCWarning(KGAPIDebug) << "Conflict. Remote resource is newer then local."; const QString msg = parseErrorMessage(rawData); q->setError(KGAPI2::Conflict); q->setErrorString(tr("Conflict. Remote resource is newer than local.\n\nGoogle replied '%1'").arg(msg)); q->emitFinished(); return; } case KGAPI2::Gone: { qCWarning(KGAPIDebug) << "Requested resource does not exist anymore."; const QString msg = parseErrorMessage(rawData); q->setError(KGAPI2::Gone); q->setErrorString(tr("Requested resource does not exist anymore.\n\nGoogle replied '%1'").arg(msg)); q->emitFinished(); return; } case KGAPI2::InternalError: { qCWarning(KGAPIDebug) << "Internal server error."; const QString msg = parseErrorMessage(rawData); q->setError(KGAPI2::InternalError); q->setErrorString(tr("Internal server error. Try again later.\n\nGoogle replied '%1'").arg(msg)); q->emitFinished(); return; } case KGAPI2::QuotaExceeded: { qCWarning(KGAPIDebug) << "User quota exceeded."; // Extend the interval (if possible) and enqueue the request again int interval = dispatchTimer->interval() / 1000; if (interval == 0) { interval = 1; } else if (interval == 1) { interval = 2; } else if ((interval > maxTimeout) && (maxTimeout > 0)) { const QString msg = parseErrorMessage(rawData); q->setError(KGAPI2::QuotaExceeded); q->setErrorString(tr("Maximum quota exceeded. Try again later.\n\nGoogle replied '%1'").arg(msg)); q->emitFinished(); return; } else { interval = interval ^ 2; } qCDebug(KGAPIDebug) << "Increasing dispatch interval to" << interval * 1000 << "msecs"; dispatchTimer->setInterval(interval * 1000); const QNetworkRequest request = reply->request(); q->enqueueRequest(request); if (!dispatchTimer->isActive()) { dispatchTimer->start(); } return; } default:{ /** Something went wrong, there's nothing we can do about it */ qCWarning(KGAPIDebug) << "Unknown error" << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); const QString msg = parseErrorMessage(rawData); q->setError(KGAPI2::UnknownError); q->setErrorString(tr("Unknown error.\n\nGoogle replied '%1'").arg(msg)); q->emitFinished(); return; } } q->handleReply(reply, rawData); // handleReply has terminated the job, don't continue if (!q->isRunning()) { return; } qCDebug(KGAPIDebug) << requestQueue.length() << "requests in requestQueue."; if (requestQueue.isEmpty()) { q->emitFinished(); return; } if (!dispatchTimer->isActive()) { dispatchTimer->start(); } } void Job::Private::_k_dispatchTimeout() { if (requestQueue.isEmpty()) { dispatchTimer->stop(); return; } const Request r = requestQueue.dequeue(); currentRequest = r; QNetworkRequest authorizedRequest = r.request; if (account) { authorizedRequest.setRawHeader("Authorization", "Bearer " + account->accessToken().toLatin1()); } QUrl url = authorizedRequest.url(); QUrlQuery standardParamQuery(url); if (!fields.isEmpty()) { standardParamQuery.addQueryItem(Job::StandardParams::Fields, fields.join(QStringLiteral(","))); } if (!standardParamQuery.hasQueryItem(Job::StandardParams::PrettyPrint)) { - standardParamQuery.addQueryItem(Job::StandardParams::PrettyPrint, prettyPrint ? QStringLiteral("true") : QStringLiteral("false")); + standardParamQuery.addQueryItem(Job::StandardParams::PrettyPrint, Utils::bool2Str(prettyPrint)); } url.setQuery(standardParamQuery); authorizedRequest.setUrl(url); qCDebug(KGAPIDebug) << q << "Dispatching request to" << r.request.url(); FileLogger::self()->logRequest(authorizedRequest, r.rawData); q->dispatchRequest(accessManager, authorizedRequest, r.rawData, r.contentType); if (requestQueue.isEmpty()) { dispatchTimer->stop(); } } /************************* PUBLIC **********************/ const QString Job::StandardParams::PrettyPrint = QStringLiteral("prettyPrint"); const QString Job::StandardParams::Fields = QStringLiteral("fields"); Job::Job(QObject* parent): QObject(parent), d(new Private(this)) { d->init(); } Job::Job(const AccountPtr& account, QObject* parent): QObject(parent), d(new Private(this)) { d->account = account; d->init(); } Job::~Job() { delete d; } void Job::setError(Error error) { d->error = error; } Error Job::error() const { if (isRunning()) { qCWarning(KGAPIDebug) << "Called error() on running job, returning nothing"; return KGAPI2::NoError; } return d->error; } void Job::setErrorString(const QString& errorString) { d->errorString = errorString; } QString Job::errorString() const { if (isRunning()) { qCWarning(KGAPIDebug) << "Called errorString() on running job, returning nothing"; return QString(); } return d->errorString; } bool Job::isRunning() const { return d->isRunning; } int Job::maxTimeout() const { return d->maxTimeout; } void Job::setMaxTimeout(int maxTimeout) { if (isRunning()) { qCWarning(KGAPIDebug) << "Called setMaxTimeout() on running job. Ignoring."; return; } d->maxTimeout = maxTimeout; } AccountPtr Job::account() const { return d->account; } void Job::setAccount(const AccountPtr& account) { if (d->isRunning) { qCWarning(KGAPIDebug) << "Called setAccount() on running job. Ignoring."; return; } d->account = account; } bool Job::prettyPrint() const { return d->prettyPrint; } void Job::setPrettyPrint(bool prettyPrint) { if (d->isRunning) { qCWarning(KGAPIDebug) << "Called setPrettyPrint() on running job. Ignoring."; return; } d->prettyPrint = prettyPrint; } QStringList Job::fields() const { return d->fields; } void Job::setFields(const QStringList &fields) { d->fields = fields; } QString Job::buildSubfields(const QString &field, const QStringList &fields) { return QStringLiteral("%1(%2)").arg(field).arg(fields.join(QStringLiteral(","))); } void Job::restart() { if (d->isRunning) { qCWarning(KGAPIDebug) << "Running job cannot be restarted."; return; } QTimer::singleShot(0, this, [this]() { d->_k_doStart();}); } void Job::emitFinished() { aboutToFinish(); d->isRunning = false; d->dispatchTimer->stop(); d->requestQueue.clear(); // Emit in next event loop iteration so that the method caller can finish // before user is notified QTimer::singleShot(0, this, [this]() { d->_k_doEmitFinished(); }); } void Job::emitProgress(int processed, int total) { Q_EMIT progress(this, processed, total); } void Job::enqueueRequest(const QNetworkRequest& request, const QByteArray& data, const QString& contentType) { if (!isRunning()) { qCDebug(KGAPIDebug) << "Can't enqueue requests when job is not running."; qCDebug(KGAPIDebug) << "Not enqueueing" << request.url(); return; } qCDebug(KGAPIDebug) << "Queued" << request.url(); Request r_; r_.request = request; r_.rawData = data; r_.contentType = contentType; d->requestQueue.enqueue(r_); if (!d->dispatchTimer->isActive()) { d->dispatchTimer->start(); } } void Job::aboutToFinish() { } void Job::aboutToStart() { d->error = KGAPI2::NoError; d->errorString.clear(); d->currentRequest.contentType.clear(); d->currentRequest.rawData.clear(); d->currentRequest.request = QNetworkRequest(); d->dispatchTimer->setInterval(0); } #include "moc_job.cpp" diff --git a/src/drive/changefetchjob.cpp b/src/drive/changefetchjob.cpp index 8c2bf2a..982a770 100644 --- a/src/drive/changefetchjob.cpp +++ b/src/drive/changefetchjob.cpp @@ -1,226 +1,226 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "changefetchjob.h" #include "account.h" #include "change.h" #include "../debug.h" #include "driveservice.h" #include "utils.h" #include #include #include using namespace KGAPI2; using namespace KGAPI2::Drive; class Q_DECL_HIDDEN ChangeFetchJob::Private { public: Private(ChangeFetchJob *parent); QString changeId; bool includeDeleted; bool includeSubscribed; int maxResults; qlonglong startChangeId; bool includeItemsFromAllDrives; bool supportsAllDrives; private: ChangeFetchJob *q; }; ChangeFetchJob::Private::Private(ChangeFetchJob *parent): includeDeleted(true), includeSubscribed(true), maxResults(0), startChangeId(0), includeItemsFromAllDrives(true), supportsAllDrives(true), q(parent) { } ChangeFetchJob::ChangeFetchJob(const QString &changeId, const AccountPtr &account, QObject *parent): FetchJob(account, parent), d(new Private(this)) { d->changeId = changeId; } ChangeFetchJob::ChangeFetchJob(const AccountPtr &account, QObject *parent): FetchJob(account, parent), d(new Private(this)) { } ChangeFetchJob::~ChangeFetchJob() { delete d; } void ChangeFetchJob::setIncludeDeleted(bool includeDeleted) { if (isRunning()) { qCWarning(KGAPIDebug) << "Can't modify includeDeleted property when job is running"; return; } d->includeDeleted = includeDeleted; } bool ChangeFetchJob::includeDeleted() const { return d->includeDeleted; } void ChangeFetchJob::setIncludeSubscribed(bool includeSubscribed) { if (isRunning()) { qCWarning(KGAPIDebug) << "Can't modify includeSubscribed property when job is running"; return; } d->includeSubscribed = includeSubscribed; } bool ChangeFetchJob::includeSubscribed() const { return d->includeSubscribed; } void ChangeFetchJob::setMaxResults(int maxResults) { if (isRunning()) { qCWarning(KGAPIDebug) << "Can't modify maxResults property when job is running"; return; } d->maxResults = maxResults; } int ChangeFetchJob::maxResults() const { return d->maxResults; } void ChangeFetchJob::setStartChangeId(qlonglong startChangeId) { if (isRunning()) { qCWarning(KGAPIDebug) << "Can't modify startChangeId property when job is running"; } d->startChangeId = startChangeId; } qlonglong ChangeFetchJob::startChangeId() const { return d->startChangeId; } bool ChangeFetchJob::includeItemsFromAllDrives() const { return d->includeItemsFromAllDrives; } void ChangeFetchJob::setIncludeItemsFromAllDrives(bool includeItemsFromAllDrives) { d->includeItemsFromAllDrives = includeItemsFromAllDrives; } bool ChangeFetchJob::supportsAllDrives() const { return d->supportsAllDrives; } void ChangeFetchJob::setSupportsAllDrives(bool supportsAllDrives) { d->supportsAllDrives = supportsAllDrives; } void ChangeFetchJob::start() { QUrl url; if (d->changeId.isEmpty()) { url = DriveService::fetchChangesUrl(); QUrlQuery query(url); query.addQueryItem(QStringLiteral("includeDeleted"), Utils::bool2Str(d->includeDeleted)); query.addQueryItem(QStringLiteral("includeSubscribed"), Utils::bool2Str(d->includeSubscribed)); if (d->maxResults > 0) { query.addQueryItem(QStringLiteral("maxResults"), QString::number(d->maxResults)); } if (d->startChangeId > 0) { query.addQueryItem(QStringLiteral("startChangeId"), QString::number(d->startChangeId)); } - query.addQueryItem(QStringLiteral("includeItemsFromAllDrives"), d->includeItemsFromAllDrives ? QStringLiteral("true") : QStringLiteral("false")); + query.addQueryItem(QStringLiteral("includeItemsFromAllDrives"), Utils::bool2Str(d->includeItemsFromAllDrives)); url.setQuery(query); } else { url = DriveService::fetchChangeUrl(d->changeId); } QUrlQuery query(url); - query.addQueryItem(QStringLiteral("supportsAllDrives"), d->supportsAllDrives ? QStringLiteral("true") : QStringLiteral("false")); + query.addQueryItem(QStringLiteral("supportsAllDrives"), Utils::bool2Str(d->supportsAllDrives)); url.setQuery(query); QNetworkRequest request(url); enqueueRequest(request); } ObjectsList ChangeFetchJob::handleReplyWithItems(const QNetworkReply *reply, const QByteArray &rawData) { FeedData feedData; feedData.requestUrl = reply->url(); ObjectsList items; QString itemId; const QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString(); ContentType ct = Utils::stringToContentType(contentType); if (ct == KGAPI2::JSON) { if (d->changeId.isEmpty()) { items << Change::fromJSONFeed(rawData, feedData); } else { items << Change::fromJSON(rawData); } } else { setError(KGAPI2::InvalidResponse); setErrorString(tr("Invalid response content type")); emitFinished(); return items; } if (feedData.nextPageUrl.isValid()) { QNetworkRequest request(feedData.nextPageUrl); enqueueRequest(request); } return items; } diff --git a/src/drive/childreferencecreatejob.cpp b/src/drive/childreferencecreatejob.cpp index b3cd3ea..6e7407f 100644 --- a/src/drive/childreferencecreatejob.cpp +++ b/src/drive/childreferencecreatejob.cpp @@ -1,163 +1,163 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "childreferencecreatejob.h" #include "account.h" #include "childreference.h" #include "driveservice.h" #include "utils.h" #include #include #include using namespace KGAPI2; using namespace KGAPI2::Drive; class Q_DECL_HIDDEN ChildReferenceCreateJob::Private { public: Private(ChildReferenceCreateJob *parent); void processNext(); QString folderId; ChildReferencesList references; bool supportsAllDrives; private: ChildReferenceCreateJob *q; }; ChildReferenceCreateJob::Private::Private(ChildReferenceCreateJob *parent): supportsAllDrives(true), q(parent) { } void ChildReferenceCreateJob::Private::processNext() { if (references.isEmpty()) { q->emitFinished(); return; } const ChildReferencePtr reference = references.takeFirst(); QUrl url = DriveService::createChildReference(folderId); QUrlQuery withDriveSupportQuery(url); - withDriveSupportQuery.addQueryItem(QStringLiteral("supportsAllDrives"), supportsAllDrives ? QStringLiteral("true") : QStringLiteral("false")); + withDriveSupportQuery.addQueryItem(QStringLiteral("supportsAllDrives"), Utils::bool2Str(supportsAllDrives)); url.setQuery(withDriveSupportQuery); QNetworkRequest request(url); const QByteArray rawData = ChildReference::toJSON(reference); q->enqueueRequest(request, rawData, QStringLiteral("application/json")); } ChildReferenceCreateJob::ChildReferenceCreateJob(const QString &folderId, const QString &childId, const AccountPtr &account, QObject *parent): CreateJob(account, parent), d(new Private(this)) { d->folderId = folderId; d->references << ChildReferencePtr(new ChildReference(childId)); } ChildReferenceCreateJob::ChildReferenceCreateJob(const QString &folderId, const QStringList &childrenIds, const AccountPtr &account, QObject *parent): CreateJob(account, parent), d(new Private(this)) { d->folderId = folderId; for (const QString & childId : qAsConst(childrenIds)) { d->references << ChildReferencePtr(new ChildReference(childId)); } } ChildReferenceCreateJob::ChildReferenceCreateJob(const QString &folderId, const ChildReferencePtr &reference, const AccountPtr &account, QObject *parent): CreateJob(account, parent), d(new Private(this)) { d->folderId = folderId; d->references << reference; } ChildReferenceCreateJob::ChildReferenceCreateJob(const QString &folderId, const ChildReferencesList &references, const AccountPtr &account, QObject *parent): CreateJob(account, parent), d(new Private(this)) { d->folderId = folderId; d->references << references; } ChildReferenceCreateJob::~ChildReferenceCreateJob() { delete d; } bool ChildReferenceCreateJob::supportsAllDrives() const { return d->supportsAllDrives; } void ChildReferenceCreateJob::setSupportsAllDrives(bool supportsAllDrives) { d->supportsAllDrives = supportsAllDrives; } void ChildReferenceCreateJob::start() { d->processNext(); } ObjectsList ChildReferenceCreateJob::handleReplyWithItems(const QNetworkReply *reply, const QByteArray &rawData) { const QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString(); ContentType ct = Utils::stringToContentType(contentType); ObjectsList items; if (ct == KGAPI2::JSON) { items << ChildReference::fromJSON(rawData); } else { setError(KGAPI2::InvalidResponse); setErrorString(tr("Invalid response content type")); emitFinished(); } // Enqueue next item or finish d->processNext(); return items; } diff --git a/src/drive/drivesfetchjob.cpp b/src/drive/drivesfetchjob.cpp index 04af9d3..ef44a81 100644 --- a/src/drive/drivesfetchjob.cpp +++ b/src/drive/drivesfetchjob.cpp @@ -1,216 +1,214 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2019 David Barchiesi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "drivesfetchjob.h" #include "account.h" #include "drives.h" #include "../debug.h" #include "driveservice.h" #include "utils.h" #include #include #include namespace { static const QString MaxResultsAttr = QStringLiteral("maxResults"); static const QString UseDomainAdminAccessAttr = QStringLiteral("useDomainAdminAccess"); - static const QString True = QStringLiteral("true"); - static const QString False = QStringLiteral("false"); } using namespace KGAPI2; using namespace KGAPI2::Drive; class Q_DECL_HIDDEN DrivesFetchJob::Private { public: Private(DrivesFetchJob *parent); DrivesSearchQuery searchQuery; QString drivesId; int maxResults = 0; QVariant useDomainAdminAccess; QStringList fields; private: DrivesFetchJob *const q; }; DrivesFetchJob::Private::Private(DrivesFetchJob *parent): q(parent) { } DrivesFetchJob::DrivesFetchJob(const QString &drivesId, const AccountPtr &account, QObject *parent): FetchJob(account, parent), d(new Private(this)) { d->drivesId = drivesId; } DrivesFetchJob::DrivesFetchJob(const DrivesSearchQuery &query, const AccountPtr &account, QObject *parent): FetchJob(account, parent), d(new Private(this)) { d->searchQuery = query; } DrivesFetchJob::DrivesFetchJob(const AccountPtr &account, QObject *parent): FetchJob(account, parent), d(new Private(this)) { } DrivesFetchJob::~DrivesFetchJob() = default; void DrivesFetchJob::setMaxResults(int maxResults) { if (isRunning()) { qCWarning(KGAPIDebug) << "Can't modify maxResults property when job is running"; return; } d->maxResults = maxResults; } int DrivesFetchJob::maxResults() const { return d->maxResults; } void DrivesFetchJob::setUseDomainAdminAccess(bool useDomainAdminAccess) { if (isRunning()) { qCWarning(KGAPIDebug) << "Can't modify useDomainAdminAccess property when job is running"; return; } d->useDomainAdminAccess = useDomainAdminAccess; } bool DrivesFetchJob::useDomainAdminAccess() const { return d->useDomainAdminAccess.toBool(); } void DrivesFetchJob::setFields(const QStringList &fields) { if (isRunning()) { qCWarning(KGAPIDebug) << "Called setFields() on running job. Ignoring."; return; } d->fields = fields; } QStringList DrivesFetchJob::fields() const { return d->fields; } void DrivesFetchJob::start() { QUrl url; if (d->drivesId.isEmpty()) { url = DriveService::fetchDrivesUrl(); applyRequestParameters(url); } else { url = DriveService::fetchDrivesUrl(d->drivesId); if (!d->fields.isEmpty()) { // Deserializing requires kind attribute, always force add it if (!d->fields.contains(Drives::Fields::Kind)) { d->fields << Drives::Fields::Kind; } Job::setFields(d->fields); } } QNetworkRequest request(url); enqueueRequest(request); } ObjectsList DrivesFetchJob::handleReplyWithItems(const QNetworkReply *reply, const QByteArray &rawData) { FeedData feedData; feedData.requestUrl = reply->url(); ObjectsList items; QString itemId; const QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString(); ContentType ct = Utils::stringToContentType(contentType); if (ct == KGAPI2::JSON) { if (d->drivesId.isEmpty()) { items << Drives::fromJSONFeed(rawData, feedData); } else { items << Drives::fromJSON(rawData); } } else { setError(KGAPI2::InvalidResponse); setErrorString(tr("Invalid response content type")); emitFinished(); return items; } if (feedData.nextPageUrl.isValid()) { // Reapply query options applyRequestParameters(feedData.nextPageUrl); QNetworkRequest request(feedData.nextPageUrl); enqueueRequest(request); } return items; } void DrivesFetchJob::applyRequestParameters(QUrl &url) { QUrlQuery query(url); if (d->maxResults != 0) { query.addQueryItem(MaxResultsAttr, QString::number(d->maxResults)); } if (!d->useDomainAdminAccess.isNull()) { - query.addQueryItem(UseDomainAdminAccessAttr, d->useDomainAdminAccess.toBool() ? True : False); + query.addQueryItem(UseDomainAdminAccessAttr, Utils::bool2Str(d->useDomainAdminAccess.toBool())); } if (!d->searchQuery.isEmpty()) { query.addQueryItem(QStringLiteral("q"), d->searchQuery.serialize()); } if (!d->fields.isEmpty()) { // Deserializing requires kind attribute, always force add it if (!d->fields.contains(Drives::Fields::Kind)) { d->fields << Drives::Fields::Kind; } QString itemFields = Job::buildSubfields(Drives::Fields::Items, d->fields); Job::setFields({ Drives::Fields::Kind, Drives::Fields::NextPageToken, itemFields }); } url.setQuery(query); } diff --git a/src/drive/drivesmodifyjob.cpp b/src/drive/drivesmodifyjob.cpp index 11d4ae4..5c9717c 100644 --- a/src/drive/drivesmodifyjob.cpp +++ b/src/drive/drivesmodifyjob.cpp @@ -1,146 +1,144 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2019 David Barchiesi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "drivesmodifyjob.h" #include "account.h" #include "driveservice.h" #include "drives.h" #include "utils.h" #include "../debug.h" #include #include #include namespace { static const QString UseDomainAdminAccessAttr = QStringLiteral("useDomainAdminAccess"); - static const QString True = QStringLiteral("true"); - static const QString False = QStringLiteral("false"); } using namespace KGAPI2; using namespace KGAPI2::Drive; class Q_DECL_HIDDEN DrivesModifyJob::Private { public: Private(DrivesModifyJob *parent); void processNext(); bool useDomainAdminAccess = false; DrivesList drives; private: DrivesModifyJob *const q; }; DrivesModifyJob::Private::Private(DrivesModifyJob *parent): q(parent) { } void DrivesModifyJob::setUseDomainAdminAccess(bool useDomainAdminAccess) { if (isRunning()) { qCWarning(KGAPIDebug) << "Can't modify useDomainAdminAccess property when job is running"; return; } d->useDomainAdminAccess = useDomainAdminAccess; } bool DrivesModifyJob::useDomainAdminAccess() const { return d->useDomainAdminAccess; } void DrivesModifyJob::Private::processNext() { if (drives.isEmpty()) { q->emitFinished(); return; } const DrivesPtr drive = drives.takeFirst(); QUrl url = DriveService::fetchDrivesUrl(drive->id()); QUrlQuery query(url); if (useDomainAdminAccess != false) { - query.addQueryItem(UseDomainAdminAccessAttr, useDomainAdminAccess ? True : False); + query.addQueryItem(UseDomainAdminAccessAttr, Utils::bool2Str(useDomainAdminAccess)); } url.setQuery(query); QNetworkRequest request(url); const QByteArray rawData = Drives::toJSON(drive); q->enqueueRequest(request, rawData, QStringLiteral("application/json")); } DrivesModifyJob::DrivesModifyJob(const DrivesPtr &drive, const AccountPtr &account, QObject *parent): ModifyJob(account, parent), d(new Private(this)) { d->drives << drive; } DrivesModifyJob::DrivesModifyJob(const DrivesList &drives, const AccountPtr &account, QObject *parent): ModifyJob(account, parent), d(new Private(this)) { d->drives << drives; } DrivesModifyJob::~DrivesModifyJob() = default; void DrivesModifyJob::start() { d->processNext(); } ObjectsList DrivesModifyJob::handleReplyWithItems(const QNetworkReply *reply, const QByteArray &rawData) { const QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString(); ContentType ct = Utils::stringToContentType(contentType); ObjectsList items; if (ct == KGAPI2::JSON) { items << Drives::fromJSON(rawData); } else { setError(KGAPI2::InvalidResponse); setErrorString(tr("Invalid response content type")); emitFinished(); return items; } // Enqueue next item or finish d->processNext(); return items; } diff --git a/src/drive/drivessearchquery.cpp b/src/drive/drivessearchquery.cpp index 71330fb..3e7772a 100644 --- a/src/drive/drivessearchquery.cpp +++ b/src/drive/drivessearchquery.cpp @@ -1,90 +1,91 @@ /* * Copyright (C) 2019 David Barchiesi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "drivessearchquery.h" +#include "utils.h" #include #include using namespace KGAPI2; using namespace KGAPI2::Drive; QString DrivesSearchQuery::fieldToString(Field field) { switch (field) { case Name: return QStringLiteral("name"); case Hidden: return QStringLiteral("hidden"); case CreatedDate: return QStringLiteral("createdDate"); case MemberCount: return QStringLiteral("memberCount"); case OrganizerCount: return QStringLiteral("organizerCount"); } Q_ASSERT(false); return QString(); } QString DrivesSearchQuery::valueToString(DrivesSearchQuery::Field field, const QVariant &var) { switch (field) { case Name: return QStringLiteral("'%1'").arg(var.toString().replace(QLatin1Char('\''), QLatin1String("\\\'"))); case Hidden: - return (var.toBool() == true ? QStringLiteral("true") : QStringLiteral("false")); + return Utils::bool2Str(var.toBool()); case MemberCount: case OrganizerCount: return var.toString(); case CreatedDate: return QStringLiteral("'%1'").arg(var.toDateTime().toUTC().toString(QStringLiteral("yyyy-MM-ddThh:mm:ss"))); } Q_ASSERT(false); return QString(); } void DrivesSearchQuery::addQuery(DrivesSearchQuery::Field field, DrivesSearchQuery::CompareOperator op, const QVariant &value) { switch (field) { case Name: Q_ASSERT(op == Contains || op == Equals || op == NotEquals); Q_ASSERT(value.canConvert()); break; case Hidden: Q_ASSERT(op == Equals || op == NotEquals); Q_ASSERT(value.canConvert()); break; case MemberCount: case OrganizerCount: Q_ASSERT(op == LessOrEqual || op == Less || op == Equals || op == NotEquals || op == Greater || op == GreaterOrEqual); Q_ASSERT(value.canConvert()); break; case CreatedDate: Q_ASSERT(op == LessOrEqual || op == Less || op == Equals || op == NotEquals || op == Greater || op == GreaterOrEqual); Q_ASSERT(value.canConvert()); break; } SearchQuery::addQuery(fieldToString(field), op, valueToString(field, value)); } diff --git a/src/drive/fileabstractmodifyjob.cpp b/src/drive/fileabstractmodifyjob.cpp index 49e7153..2a26d2a 100644 --- a/src/drive/fileabstractmodifyjob.cpp +++ b/src/drive/fileabstractmodifyjob.cpp @@ -1,154 +1,154 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "fileabstractmodifyjob.h" #include "account.h" #include "driveservice.h" #include "file.h" #include "utils.h" #include #include #include using namespace KGAPI2; using namespace KGAPI2::Drive; class Q_DECL_HIDDEN FileAbstractModifyJob::Private { public: Private(FileAbstractModifyJob *parent); void processNext(); QStringList filesIds; bool supportsAllDrives; private: FileAbstractModifyJob *q; }; FileAbstractModifyJob::Private::Private(FileAbstractModifyJob *parent): supportsAllDrives(true), q(parent) { } void FileAbstractModifyJob::Private::processNext() { if (filesIds.isEmpty()) { q->emitFinished(); return; } const QString fileId = filesIds.takeFirst(); QUrl url = q->url(fileId); QUrlQuery query(url); - query.addQueryItem(QStringLiteral("supportsAllDrives"), supportsAllDrives ? QStringLiteral("true") : QStringLiteral("false")); + query.addQueryItem(QStringLiteral("supportsAllDrives"), Utils::bool2Str(supportsAllDrives)); url.setQuery(query); QNetworkRequest request(url); request.setHeader(QNetworkRequest::ContentLengthHeader, 0); q->enqueueRequest(request); } FileAbstractModifyJob::FileAbstractModifyJob(const QString &fileId, const AccountPtr &account, QObject *parent): ModifyJob(account, parent), d(new Private(this)) { d->filesIds << fileId; } FileAbstractModifyJob::FileAbstractModifyJob(const QStringList &filesIds, const AccountPtr &account, QObject *parent): ModifyJob(account, parent), d(new Private(this)) { d->filesIds << filesIds; } FileAbstractModifyJob::FileAbstractModifyJob(const FilePtr &file, const AccountPtr &account, QObject *parent): ModifyJob(account, parent), d(new Private(this)) { d->filesIds << file->id(); } FileAbstractModifyJob::FileAbstractModifyJob(const FilesList &files, const AccountPtr &account, QObject *parent): ModifyJob(account, parent), d(new Private(this)) { for (const FilePtr & file : qAsConst(files)) { d->filesIds << file->id(); } } FileAbstractModifyJob::~FileAbstractModifyJob() { delete d; } void FileAbstractModifyJob::start() { d->processNext(); } bool FileAbstractModifyJob::supportsAllDrives() const { return d->supportsAllDrives; } void FileAbstractModifyJob::setSupportsAllDrives(bool supportsAllDrives) { d->supportsAllDrives = supportsAllDrives; } ObjectsList FileAbstractModifyJob::handleReplyWithItems(const QNetworkReply *reply, const QByteArray &rawData) { const QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString(); ContentType ct = Utils::stringToContentType(contentType); ObjectsList items; if (ct == KGAPI2::JSON) { items << File::fromJSON(rawData); } else { setError(KGAPI2::InvalidResponse); setErrorString(tr("Invalid response content type")); emitFinished(); } d->processNext(); return items; } #include "moc_fileabstractmodifyjob.cpp" diff --git a/src/drive/filefetchjob.cpp b/src/drive/filefetchjob.cpp index 06e1911..c5b64ed 100644 --- a/src/drive/filefetchjob.cpp +++ b/src/drive/filefetchjob.cpp @@ -1,280 +1,280 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "filefetchjob.h" #include "filesearchquery.h" #include "account.h" #include "../debug.h" #include "driveservice.h" #include "file.h" #include "utils.h" #include #include #include using namespace KGAPI2; using namespace KGAPI2::Drive; class Q_DECL_HIDDEN FileFetchJob::Private { public: Private(FileFetchJob *parent); void processNext(); FileSearchQuery searchQuery; QStringList filesIDs; bool isFeed; bool includeItemsFromAllDrives; bool supportsAllDrives; bool updateViewedDate; QStringList fields; private: FileFetchJob *const q; }; FileFetchJob::Private::Private(FileFetchJob *parent): isFeed(false), includeItemsFromAllDrives(true), supportsAllDrives(true), updateViewedDate(false), q(parent) { } void FileFetchJob::Private::processNext() { QUrl url; if (isFeed) { url = DriveService::fetchFilesUrl(); QUrlQuery query(url); if (!searchQuery.isEmpty()) { query.addQueryItem(QStringLiteral("q"), searchQuery.serialize()); } - query.addQueryItem(QStringLiteral("includeItemsFromAllDrives"), includeItemsFromAllDrives ? QStringLiteral("true") : QStringLiteral("false")); + query.addQueryItem(QStringLiteral("includeItemsFromAllDrives"), Utils::bool2Str(includeItemsFromAllDrives)); url.setQuery(query); if (!fields.isEmpty()) { // Deserializing requires kind attribute, always force add it if (!fields.contains(File::Fields::Kind)) { fields << File::Fields::Kind; } Job *baseJob = dynamic_cast(q); baseJob->setFields({ File::Fields::Etag, File::Fields::Kind, File::Fields::NextLink, File::Fields::NextPageToken, File::Fields::SelfLink, Job::buildSubfields(File::Fields::Items, fields) }); } } else { if (filesIDs.isEmpty()) { q->emitFinished(); return; } const QString fileId = filesIDs.takeFirst(); url = DriveService::fetchFileUrl(fileId); if (!fields.isEmpty()) { // Deserializing requires kind attribute, always force add it if (!fields.contains(File::Fields::Kind)) { fields << File::Fields::Kind; } Job *baseJob = dynamic_cast(q); baseJob->setFields(fields); } } QUrlQuery withDriveSupportQuery(url); - withDriveSupportQuery.addQueryItem(QStringLiteral("supportsAllDrives"), supportsAllDrives ? QStringLiteral("true") : QStringLiteral("false")); + withDriveSupportQuery.addQueryItem(QStringLiteral("supportsAllDrives"), Utils::bool2Str(supportsAllDrives)); url.setQuery(withDriveSupportQuery); QNetworkRequest request(url); q->enqueueRequest(request); } FileFetchJob::FileFetchJob(const QString &fileId, const AccountPtr &account, QObject *parent): FetchJob(account, parent), d(new Private(this)) { d->filesIDs << fileId; } FileFetchJob::FileFetchJob(const QStringList &filesIds, const AccountPtr &account, QObject *parent): FetchJob(account, parent), d(new Private(this)) { d->filesIDs << filesIds; } FileFetchJob::FileFetchJob(const AccountPtr &account, QObject *parent): FetchJob(account, parent), d(new Private(this)) { d->isFeed = true; } FileFetchJob::FileFetchJob(const FileSearchQuery &query, const AccountPtr &account, QObject *parent): FetchJob(account, parent), d(new Private(this)) { d->isFeed = true; d->searchQuery = query; } FileFetchJob::~FileFetchJob() { delete d; } bool FileFetchJob::updateViewedDate() const { return d->updateViewedDate; } void FileFetchJob::setUpdateViewedDate(bool updateViewedDate) { if (isRunning()) { qCWarning(KGAPIDebug) << "Can't modify updateViewedDate property when job is running."; return; } d->updateViewedDate = updateViewedDate; } void FileFetchJob::start() { d->processNext(); } void FileFetchJob::setFields(const QStringList &fields) { if (isRunning()) { qCWarning(KGAPIDebug) << "Called setFields() on running job. Ignoring."; return; } d->fields = fields; } QStringList FileFetchJob::fields() const { return d->fields; } bool FileFetchJob::includeItemsFromAllDrives() const { return d->includeItemsFromAllDrives; } void FileFetchJob::setIncludeItemsFromAllDrives(bool includeItemsFromAllDrives) { d->includeItemsFromAllDrives = includeItemsFromAllDrives; } bool FileFetchJob::supportsAllDrives() const { return d->supportsAllDrives; } void FileFetchJob::setSupportsAllDrives(bool supportsAllDrives) { d->supportsAllDrives = supportsAllDrives; } const QStringList FileFetchJob::FieldShorthands::BasicFields = { File::Fields::Id, File::Fields::Title, File::Fields::MimeType, File::Fields::CreatedDate, File::Fields::ModifiedDate, File::Fields::FileSize, File::Fields::DownloadUrl, File::Fields::Permissions }; const QStringList FileFetchJob::FieldShorthands::AccessFields = { File::Fields::CreatedDate, File::Fields::ModifiedDate, File::Fields::ModifiedByMeDate, File::Fields::LastModifiedByMeDate, File::Fields::LastViewedByMeDate, File::Fields::MarkedViewedByMeDate }; const QStringList FileFetchJob::FieldShorthands::SharingFields = { File::Fields::SharedWithMeDate, File::Fields::WritersCanShare, File::Fields::Shared, File::Fields::Owners, File::Fields::SharingUser, File::Fields::OwnerNames }; ObjectsList FileFetchJob::handleReplyWithItems(const QNetworkReply *reply, const QByteArray &rawData) { ObjectsList items; const QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString(); ContentType ct = Utils::stringToContentType(contentType); if (ct == KGAPI2::JSON) { if (d->isFeed) { FeedData feedData; items << File::fromJSONFeed(rawData, feedData); if (feedData.nextPageUrl.isValid()) { QNetworkRequest request(feedData.nextPageUrl); enqueueRequest(request); } } else { items << File::fromJSON(rawData); d->processNext(); } } else { setError(KGAPI2::InvalidResponse); setErrorString(tr("Invalid response content type")); emitFinished(); return items; } return items; } diff --git a/src/drive/filesearchquery.cpp b/src/drive/filesearchquery.cpp index e6e1677..03bb835 100644 --- a/src/drive/filesearchquery.cpp +++ b/src/drive/filesearchquery.cpp @@ -1,121 +1,122 @@ /* * Copyright (C) 2014 Daniel Vrátil * Copyright (C) 2019 David Barchiesi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "filesearchquery.h" +#include "utils.h" #include #include using namespace KGAPI2; using namespace KGAPI2::Drive; QString FileSearchQuery::fieldToString(Field field) { switch (field) { case Title: return QStringLiteral("title"); case FullText: return QStringLiteral("fullText"); case MimeType: return QStringLiteral("mimeType"); case ModifiedDate: return QStringLiteral("modifiedDate"); case LastViewedByMeDate: return QStringLiteral("lastViewedByMeDate"); case Trashed: return QStringLiteral("trashed"); case Starred: return QStringLiteral("starred"); case Parents: return QStringLiteral("parents"); case Owners: return QStringLiteral("owners"); case Writers: return QStringLiteral("writers"); case Readers: return QStringLiteral("readers"); case SharedWithMe: return QStringLiteral("sharedWithMe"); } Q_ASSERT(false); return QString(); } QString FileSearchQuery::valueToString(FileSearchQuery::Field field, const QVariant &var) { switch (field) { case Title: case FullText: case MimeType: case Parents: case Owners: case Writers: case Readers: return QStringLiteral("'%1'").arg(var.toString().replace(QLatin1Char('\''), QLatin1String("\\\'"))); case ModifiedDate: case LastViewedByMeDate: return QStringLiteral("'%1'").arg(var.toDateTime().toUTC().toString(QStringLiteral("yyyy-MM-ddThh:mm:ss"))); case Trashed: case Starred: case SharedWithMe: - return (var.toBool() == true ? QStringLiteral("true") : QStringLiteral("false")); + return Utils::bool2Str(var.toBool()); } Q_ASSERT(false); return QString(); } void FileSearchQuery::addQuery(FileSearchQuery::Field field, FileSearchQuery::CompareOperator op, const QVariant &value) { switch (field) { case Title: case MimeType: Q_ASSERT(op == Contains || op == Equals || op == NotEquals); Q_ASSERT(value.canConvert()); break; case FullText: Q_ASSERT(op == Contains); Q_ASSERT(value.canConvert()); break; case ModifiedDate: case LastViewedByMeDate: Q_ASSERT(op == LessOrEqual || op == Less || op == Equals || op == NotEquals || op == Greater || op == GreaterOrEqual); Q_ASSERT(value.canConvert()); break; case Trashed: case Starred: case SharedWithMe: Q_ASSERT(op == Equals || op == NotEquals); Q_ASSERT(value.canConvert()); break; case Parents: case Owners: case Writers: case Readers: Q_ASSERT(op == In); Q_ASSERT(value.canConvert()); break; } SearchQuery::addQuery(fieldToString(field), op, valueToString(field, value)); } diff --git a/src/drive/parentreferencecreatejob.cpp b/src/drive/parentreferencecreatejob.cpp index 425b01b..d869480 100644 --- a/src/drive/parentreferencecreatejob.cpp +++ b/src/drive/parentreferencecreatejob.cpp @@ -1,163 +1,163 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "parentreferencecreatejob.h" #include "account.h" #include "driveservice.h" #include "parentreference.h" #include "utils.h" #include #include #include using namespace KGAPI2; using namespace KGAPI2::Drive; class Q_DECL_HIDDEN ParentReferenceCreateJob::Private { public: Private(ParentReferenceCreateJob *parent); void processNext(); bool supportsAllDrives; QString fileId; ParentReferencesList references; private: ParentReferenceCreateJob *q; }; ParentReferenceCreateJob::Private::Private(ParentReferenceCreateJob *parent): supportsAllDrives(true), q(parent) { } void ParentReferenceCreateJob::Private::processNext() { if (references.isEmpty()) { q->emitFinished(); return; } const ParentReferencePtr reference = references.takeFirst(); QUrl url = DriveService::createParentReferenceUrl(fileId); QUrlQuery withDriveSupportQuery(url); - withDriveSupportQuery.addQueryItem(QStringLiteral("supportsAllDrives"), supportsAllDrives ? QStringLiteral("true") : QStringLiteral("false")); + withDriveSupportQuery.addQueryItem(QStringLiteral("supportsAllDrives"), Utils::bool2Str(supportsAllDrives)); url.setQuery(withDriveSupportQuery); QNetworkRequest request(url); const QByteArray rawData = ParentReference::toJSON(reference); q->enqueueRequest(request, rawData, QStringLiteral("application/json")); } ParentReferenceCreateJob::ParentReferenceCreateJob(const QString &fileId, const QString &parentId, const AccountPtr &account, QObject *parent): CreateJob(account, parent), d(new Private(this)) { d->fileId = fileId; d->references << ParentReferencePtr(new ParentReference(parentId)); } ParentReferenceCreateJob::ParentReferenceCreateJob(const QString &fileId, const QStringList &parentsIds, const AccountPtr &account, QObject *parent): CreateJob(account, parent), d(new Private(this)) { d->fileId = fileId; for (const QString & parentId : qAsConst(parentsIds)) { d->references << ParentReferencePtr(new ParentReference(parentId)); } } ParentReferenceCreateJob::ParentReferenceCreateJob(const QString &fileId, const ParentReferencePtr &reference, const AccountPtr &account, QObject *parent): CreateJob(account, parent), d(new Private(this)) { d->fileId = fileId; d->references << reference; } ParentReferenceCreateJob::ParentReferenceCreateJob(const QString &fileId, const ParentReferencesList &references, const AccountPtr &account, QObject *parent): CreateJob(account, parent), d(new Private(this)) { d->fileId = fileId; d->references << references; } ParentReferenceCreateJob::~ParentReferenceCreateJob() { delete d; } bool ParentReferenceCreateJob::supportsAllDrives() const { return d->supportsAllDrives; } void ParentReferenceCreateJob::setSupportsAllDrives(bool supportsAllDrives) { d->supportsAllDrives = supportsAllDrives; } void ParentReferenceCreateJob::start() { d->processNext(); } ObjectsList ParentReferenceCreateJob::handleReplyWithItems(const QNetworkReply *reply, const QByteArray &rawData) { const QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString(); ContentType ct = Utils::stringToContentType(contentType); ObjectsList items; if (ct == KGAPI2::JSON) { items << ParentReference::fromJSON(rawData); } else { setError(KGAPI2::InvalidResponse); setErrorString(tr("Invalid response content type")); emitFinished(); } // Enqueue next item or finish d->processNext(); return items; } diff --git a/src/drive/permissioncreatejob.cpp b/src/drive/permissioncreatejob.cpp index d63b8f7..c0fe439 100644 --- a/src/drive/permissioncreatejob.cpp +++ b/src/drive/permissioncreatejob.cpp @@ -1,140 +1,140 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "permissioncreatejob.h" #include "account.h" #include "driveservice.h" #include "permission.h" #include "utils.h" #include #include #include using namespace KGAPI2; using namespace KGAPI2::Drive; class Q_DECL_HIDDEN PermissionCreateJob::Private { public: Private(PermissionCreateJob *parent); void processNext(); PermissionsList permissions; QString fileId; bool supportsAllDrives; private: PermissionCreateJob *const q; }; PermissionCreateJob::Private::Private(PermissionCreateJob *parent): supportsAllDrives(true), q(parent) { } void PermissionCreateJob::Private::processNext() { if (permissions.isEmpty()) { q->emitFinished(); return; } const PermissionPtr permission = permissions.takeFirst(); QUrl url = DriveService::createPermissionUrl(fileId); QUrlQuery withDriveSupportQuery(url); - withDriveSupportQuery.addQueryItem(QStringLiteral("supportsAllDrives"), supportsAllDrives ? QStringLiteral("true") : QStringLiteral("false")); + withDriveSupportQuery.addQueryItem(QStringLiteral("supportsAllDrives"), Utils::bool2Str(supportsAllDrives)); url.setQuery(withDriveSupportQuery); QNetworkRequest request(url); const QByteArray rawData = Permission::toJSON(permission); q->enqueueRequest(request, rawData, QStringLiteral("application/json")); } PermissionCreateJob::PermissionCreateJob(const QString &fileId, const PermissionPtr &permission, const AccountPtr &account, QObject *parent): CreateJob(account, parent), d(new Private(this)) { d->fileId = fileId; d->permissions << permission; } PermissionCreateJob::PermissionCreateJob(const QString &fileId, const PermissionsList &permissions, const AccountPtr &account, QObject *parent): CreateJob(account, parent), d(new Private(this)) { d->fileId = fileId; d->permissions = permissions; } PermissionCreateJob::~PermissionCreateJob() { delete d; } bool PermissionCreateJob::supportsAllDrives() const { return d->supportsAllDrives; } void PermissionCreateJob::setSupportsAllDrives(bool supportsAllDrives) { d->supportsAllDrives = supportsAllDrives; } void PermissionCreateJob::start() { d->processNext(); } ObjectsList PermissionCreateJob::handleReplyWithItems(const QNetworkReply *reply, const QByteArray &rawData) { const QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString(); ContentType ct = Utils::stringToContentType(contentType); ObjectsList items; if (ct == KGAPI2::JSON) { items << Permission::fromJSON(rawData); } else { setError(KGAPI2::InvalidResponse); setErrorString(tr("Invalid response content type")); emitFinished(); } // Enqueue next item or finish d->processNext(); return items; } diff --git a/src/drive/permissiondeletejob.cpp b/src/drive/permissiondeletejob.cpp index 34baeca..0b80cde 100644 --- a/src/drive/permissiondeletejob.cpp +++ b/src/drive/permissiondeletejob.cpp @@ -1,125 +1,126 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "permissiondeletejob.h" #include "permission.h" #include "account.h" +#include "utils.h" #include "driveservice.h" #include #include using namespace KGAPI2; using namespace KGAPI2::Drive; class Q_DECL_HIDDEN PermissionDeleteJob::Private { public: QString fileId; QStringList permissionsIds; bool supportsAllDrives; }; PermissionDeleteJob::PermissionDeleteJob(const QString &fileId, const PermissionPtr &permission, const AccountPtr &account, QObject *parent): DeleteJob(account, parent), d(new Private) { d->supportsAllDrives = true; d->fileId = fileId; d->permissionsIds << permission->id(); } PermissionDeleteJob::PermissionDeleteJob(const QString &fileId, const QString &permissionId, const AccountPtr &account, QObject *parent): DeleteJob(account, parent), d(new Private) { d->supportsAllDrives = true; d->fileId = fileId; d->permissionsIds << permissionId; } PermissionDeleteJob::PermissionDeleteJob(const QString &fileId, const PermissionsList &permissions, const AccountPtr &account, QObject *parent): DeleteJob(account, parent), d(new Private) { d->supportsAllDrives = true; d->fileId = fileId; for (const PermissionPtr & permission : qAsConst(permissions)) { d->permissionsIds << permission->id(); } } PermissionDeleteJob::PermissionDeleteJob(const QString &fileId, const QStringList &permissionsIds, const AccountPtr &account, QObject *parent): DeleteJob(account, parent), d(new Private) { d->supportsAllDrives = true; d->fileId = fileId; d->permissionsIds << permissionsIds; } PermissionDeleteJob::~PermissionDeleteJob() { delete d; } bool PermissionDeleteJob::supportsAllDrives() const { return d->supportsAllDrives; } void PermissionDeleteJob::setSupportsAllDrives(bool supportsAllDrives) { d->supportsAllDrives = supportsAllDrives; } void PermissionDeleteJob::start() { if (d->permissionsIds.isEmpty()) { emitFinished(); return; } const QString permissionId = d->permissionsIds.takeFirst(); QUrl url = DriveService::deletePermissionUrl(d->fileId, permissionId); QUrlQuery withDriveSupportQuery(url); - withDriveSupportQuery.addQueryItem(QStringLiteral("supportsAllDrives"), d->supportsAllDrives ? QStringLiteral("true") : QStringLiteral("false")); + withDriveSupportQuery.addQueryItem(QStringLiteral("supportsAllDrives"), Utils::bool2Str(d->supportsAllDrives)); url.setQuery(withDriveSupportQuery); QNetworkRequest request(url); enqueueRequest(request); } diff --git a/src/drive/permissionfetchjob.cpp b/src/drive/permissionfetchjob.cpp index 4176153..36b6c10 100644 --- a/src/drive/permissionfetchjob.cpp +++ b/src/drive/permissionfetchjob.cpp @@ -1,144 +1,144 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "permissionfetchjob.h" #include "driveservice.h" #include "account.h" #include "file.h" #include "permission.h" #include "utils.h" #include #include #include using namespace KGAPI2; using namespace KGAPI2::Drive; class Q_DECL_HIDDEN PermissionFetchJob::Private { public: QString fileId; QString permissionId; bool supportsAllDrives; }; PermissionFetchJob::PermissionFetchJob(const QString &fileId, const AccountPtr &account, QObject *parent): FetchJob(account, parent), d(new Private) { d->supportsAllDrives = true; d->fileId = fileId; } PermissionFetchJob::PermissionFetchJob(const FilePtr &file, const AccountPtr &account, QObject *parent): FetchJob(account, parent), d(new Private) { d->supportsAllDrives = true; d->fileId = file->id(); } PermissionFetchJob::PermissionFetchJob(const QString &fileId, const QString &permissionId, const AccountPtr &account, QObject *parent): FetchJob(account, parent), d(new Private) { d->supportsAllDrives = true; d->fileId = fileId; d->permissionId = permissionId; } PermissionFetchJob::PermissionFetchJob(const FilePtr &file, const QString &permissionId, const AccountPtr &account, QObject *parent): FetchJob(account, parent), d(new Private) { d->supportsAllDrives = true; d->fileId = file->id(); d->permissionId = permissionId; } PermissionFetchJob::~PermissionFetchJob() { delete d; } bool PermissionFetchJob::supportsAllDrives() const { return d->supportsAllDrives; } void PermissionFetchJob::setSupportsAllDrives(bool supportsAllDrives) { d->supportsAllDrives = supportsAllDrives; } void PermissionFetchJob::start() { QUrl url; if (d->permissionId.isEmpty()) { url = DriveService::fetchPermissionsUrl(d->fileId); } else { url = DriveService::fetchPermissionUrl(d->fileId, d->permissionId); } QUrlQuery withDriveSupportQuery(url); - withDriveSupportQuery.addQueryItem(QStringLiteral("supportsAllDrives"), d->supportsAllDrives ? QStringLiteral("true") : QStringLiteral("false")); + withDriveSupportQuery.addQueryItem(QStringLiteral("supportsAllDrives"), Utils::bool2Str(d->supportsAllDrives)); url.setQuery(withDriveSupportQuery); QNetworkRequest request(url); enqueueRequest(request); } ObjectsList PermissionFetchJob::handleReplyWithItems(const QNetworkReply *reply, const QByteArray &rawData) { ObjectsList items; const QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString(); ContentType ct = Utils::stringToContentType(contentType); if (ct == KGAPI2::JSON) { if (d->permissionId.isEmpty()) { items << Permission::fromJSONFeed(rawData); } else { items << Permission::fromJSON(rawData); } } else { setError(KGAPI2::InvalidResponse); setErrorString(tr("Invalid response content type")); } emitFinished(); return items; } diff --git a/src/drive/permissionmodifyjob.cpp b/src/drive/permissionmodifyjob.cpp index 0c84e8d..d1ab131 100644 --- a/src/drive/permissionmodifyjob.cpp +++ b/src/drive/permissionmodifyjob.cpp @@ -1,140 +1,140 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "permissionmodifyjob.h" #include "account.h" #include "driveservice.h" #include "permission.h" #include "utils.h" #include #include #include using namespace KGAPI2; using namespace KGAPI2::Drive; class Q_DECL_HIDDEN PermissionModifyJob::Private { public: Private(PermissionModifyJob *parent); void processNext(); QString fileId; PermissionsList permissions; bool supportsAllDrives; private: PermissionModifyJob *q; }; PermissionModifyJob::Private::Private(PermissionModifyJob *parent): supportsAllDrives(true), q(parent) { } void PermissionModifyJob::Private::processNext() { if (permissions.isEmpty()) { q->emitFinished(); return; } const PermissionPtr permission = permissions.takeFirst(); QUrl url = DriveService::modifyPermissionUrl(fileId, permission->id()); QUrlQuery withDriveSupportQuery(url); - withDriveSupportQuery.addQueryItem(QStringLiteral("supportsAllDrives"), supportsAllDrives ? QStringLiteral("true") : QStringLiteral("false")); + withDriveSupportQuery.addQueryItem(QStringLiteral("supportsAllDrives"), Utils::bool2Str(supportsAllDrives)); url.setQuery(withDriveSupportQuery); QNetworkRequest request(url); const QByteArray rawData = Permission::toJSON(permission); q->enqueueRequest(request, rawData, QStringLiteral("application/json")); } PermissionModifyJob::PermissionModifyJob(const QString &fileId, const PermissionPtr &permission, const AccountPtr &account, QObject *parent): ModifyJob(account, parent), d(new Private(this)) { d->fileId = fileId; d->permissions << permission; } PermissionModifyJob::PermissionModifyJob(const QString &fileId, const PermissionsList &permissions, const AccountPtr &account, QObject *parent): ModifyJob(account, parent), d(new Private(this)) { d->fileId = fileId; d->permissions << permissions; } PermissionModifyJob::~PermissionModifyJob() { delete d; } bool PermissionModifyJob::supportsAllDrives() const { return d->supportsAllDrives; } void PermissionModifyJob::setSupportsAllDrives(bool supportsAllDrives) { d->supportsAllDrives = supportsAllDrives; } void PermissionModifyJob::start() { d->processNext(); } ObjectsList PermissionModifyJob::handleReplyWithItems(const QNetworkReply *reply, const QByteArray &rawData) { const QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString(); ContentType ct = Utils::stringToContentType(contentType); ObjectsList items; if (ct == KGAPI2::JSON) { items << Permission::fromJSON(rawData); } else { setError(KGAPI2::InvalidResponse); setErrorString(tr("Invalid response content type")); emitFinished(); } // Enqueue next item or finish d->processNext(); return items; } diff --git a/src/drive/teamdrivefetchjob.cpp b/src/drive/teamdrivefetchjob.cpp index bcb20fe..ffa8a08 100644 --- a/src/drive/teamdrivefetchjob.cpp +++ b/src/drive/teamdrivefetchjob.cpp @@ -1,217 +1,215 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2019 David Barchiesi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "teamdrivefetchjob.h" #include "account.h" #include "teamdrive.h" #include "../debug.h" #include "driveservice.h" #include "utils.h" #include #include #include namespace { static const QString MaxResultsAttr = QStringLiteral("maxResults"); static const QString UseDomainAdminAccessAttr = QStringLiteral("useDomainAdminAccess"); - static const QString True = QStringLiteral("true"); - static const QString False = QStringLiteral("false"); } using namespace KGAPI2; using namespace KGAPI2::Drive; class Q_DECL_HIDDEN TeamdriveFetchJob::Private { public: Private(TeamdriveFetchJob *parent); TeamdriveSearchQuery searchQuery; QString teamdriveId; int maxResults = 0; bool useDomainAdminAccess = false; QStringList fields; private: TeamdriveFetchJob *const q; }; TeamdriveFetchJob::Private::Private(TeamdriveFetchJob *parent): q(parent) { } TeamdriveFetchJob::TeamdriveFetchJob(const QString &teamdriveId, const AccountPtr &account, QObject *parent): FetchJob(account, parent), d(new Private(this)) { d->teamdriveId = teamdriveId; } TeamdriveFetchJob::TeamdriveFetchJob(const TeamdriveSearchQuery &query, const AccountPtr &account, QObject *parent): FetchJob(account, parent), d(new Private(this)) { d->useDomainAdminAccess = true; d->searchQuery = query; } TeamdriveFetchJob::TeamdriveFetchJob(const AccountPtr &account, QObject *parent): FetchJob(account, parent), d(new Private(this)) { } TeamdriveFetchJob::~TeamdriveFetchJob() = default; void TeamdriveFetchJob::setMaxResults(int maxResults) { if (isRunning()) { qCWarning(KGAPIDebug) << "Can't modify maxResults property when job is running"; return; } d->maxResults = maxResults; } int TeamdriveFetchJob::maxResults() const { return d->maxResults; } void TeamdriveFetchJob::setUseDomainAdminAccess(bool useDomainAdminAccess) { if (isRunning()) { qCWarning(KGAPIDebug) << "Can't modify useDomainAdminAccess property when job is running"; return; } d->useDomainAdminAccess = useDomainAdminAccess; } bool TeamdriveFetchJob::useDomainAdminAccess() const { return d->useDomainAdminAccess; } void TeamdriveFetchJob::setFields(const QStringList &fields) { if (isRunning()) { qCWarning(KGAPIDebug) << "Called setFields() on running job. Ignoring."; return; } d->fields = fields; } QStringList TeamdriveFetchJob::fields() const { return d->fields; } void TeamdriveFetchJob::start() { QUrl url; if (d->teamdriveId.isEmpty()) { url = DriveService::fetchTeamdrivesUrl(); applyRequestParameters(url); } else { url = DriveService::fetchTeamdriveUrl(d->teamdriveId); if (!d->fields.isEmpty()) { // Deserializing requires kind attribute, always force add it if (!d->fields.contains(Teamdrive::Fields::Kind)) { d->fields << Teamdrive::Fields::Kind; } Job::setFields(d->fields); } } QNetworkRequest request(url); enqueueRequest(request); } ObjectsList TeamdriveFetchJob::handleReplyWithItems(const QNetworkReply *reply, const QByteArray &rawData) { FeedData feedData; feedData.requestUrl = reply->url(); ObjectsList items; QString itemId; const QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString(); ContentType ct = Utils::stringToContentType(contentType); if (ct == KGAPI2::JSON) { if (d->teamdriveId.isEmpty()) { items << Teamdrive::fromJSONFeed(rawData, feedData); } else { items << Teamdrive::fromJSON(rawData); } } else { setError(KGAPI2::InvalidResponse); setErrorString(tr("Invalid response content type")); emitFinished(); return items; } if (feedData.nextPageUrl.isValid()) { // Reapply query options applyRequestParameters(feedData.nextPageUrl); QNetworkRequest request(feedData.nextPageUrl); enqueueRequest(request); } return items; } void TeamdriveFetchJob::applyRequestParameters(QUrl &url) { QUrlQuery query(url); if (d->maxResults != 0) { query.addQueryItem(MaxResultsAttr, QString::number(d->maxResults)); } if (d->useDomainAdminAccess != false) { - query.addQueryItem(UseDomainAdminAccessAttr, d->useDomainAdminAccess ? True : False); + query.addQueryItem(UseDomainAdminAccessAttr, Utils::bool2Str(d->useDomainAdminAccess)); } if (!d->searchQuery.isEmpty()) { query.addQueryItem(QStringLiteral("q"), d->searchQuery.serialize()); } if (!d->fields.isEmpty()) { // Deserializing requires kind attribute, always force add it if (!d->fields.contains(Teamdrive::Fields::Kind)) { d->fields << Teamdrive::Fields::Kind; } QString itemFields = Job::buildSubfields(Teamdrive::Fields::Items, d->fields); Job::setFields({ Teamdrive::Fields::Kind, Teamdrive::Fields::NextPageToken, itemFields }); } url.setQuery(query); }