File indexing completed on 2024-04-21 14:52:20

0001 /*
0002     This file is part of KDE.
0003 
0004     SPDX-FileCopyrightText: 2008 Cornelius Schumacher <schumacher@kde.org>
0005     SPDX-FileCopyrightText: 2010 Sebastian Kügler <sebas@kde.org>
0006     SPDX-FileCopyrightText: 2011 Laszlo Papp <djszapi@archlinux.us>
0007 
0008     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0009 */
0010 
0011 #include "provider.h"
0012 
0013 #include "accountbalance.h"
0014 #include "accountbalanceparser.h"
0015 #include "achievementparser.h"
0016 #include "activity.h"
0017 #include "activityparser.h"
0018 #include "buildservice.h"
0019 #include "buildservicejob.h"
0020 #include "buildservicejoboutput.h"
0021 #include "buildservicejoboutputparser.h"
0022 #include "buildservicejobparser.h"
0023 #include "buildserviceparser.h"
0024 #include "categoryparser.h"
0025 #include "commentparser.h"
0026 #include "config.h"
0027 #include "content.h"
0028 #include "contentparser.h"
0029 #include "distributionparser.h"
0030 #include "downloaditem.h"
0031 #include "downloaditemparser.h"
0032 #include "event.h"
0033 #include "eventparser.h"
0034 #include "folder.h"
0035 #include "folderparser.h"
0036 #include "forumparser.h"
0037 #include "homepagetype.h"
0038 #include "homepagetypeparser.h"
0039 #include "knowledgebaseentry.h"
0040 #include "knowledgebaseentryparser.h"
0041 #include "licenseparser.h"
0042 #include "messageparser.h"
0043 #include "person.h"
0044 #include "personparser.h"
0045 #include "platformdependent_v2.h"
0046 #include "postfiledata.h"
0047 #include "postjob.h"
0048 #include "privatedata.h"
0049 #include "privatedataparser.h"
0050 #include "project.h"
0051 #include "projectparser.h"
0052 #include "publisher.h"
0053 #include "publisherfield.h"
0054 #include "publisherfieldparser.h"
0055 #include "publisherparser.h"
0056 #include "remoteaccount.h"
0057 #include "remoteaccountparser.h"
0058 #include "topic.h"
0059 #include "topicparser.h"
0060 #include "version.h"
0061 
0062 #include <QCoreApplication>
0063 #include <QDebug>
0064 #include <QFile>
0065 #include <QNetworkAccessManager>
0066 #include <QNetworkReply>
0067 #include <QThreadStorage>
0068 #include <QUrlQuery>
0069 
0070 using namespace Attica;
0071 
0072 class Q_DECL_HIDDEN Provider::Private : public QSharedData
0073 {
0074 public:
0075     QUrl m_baseUrl;
0076     QUrl m_icon;
0077     QString m_name;
0078     QString m_credentialsUserName;
0079     QString m_credentialsPassword;
0080     QString m_personVersion;
0081     QString m_friendVersion;
0082     QString m_messageVersion;
0083     QString m_achievementVersion;
0084     QString m_activityVersion;
0085     QString m_contentVersion;
0086     QString m_fanVersion;
0087     QString m_forumVersion;
0088     QString m_knowledgebaseVersion;
0089     QString m_eventVersion;
0090     QString m_commentVersion;
0091     QString m_registerUrl;
0092     PlatformDependent *m_internals;
0093     QString m_additionalAgentInformation;
0094 
0095     Private()
0096         : m_internals(nullptr)
0097     {
0098     }
0099 
0100     Private(const Private &other)
0101         : QSharedData(other)
0102         , m_baseUrl(other.m_baseUrl)
0103         , m_name(other.m_name)
0104         , m_credentialsUserName(other.m_credentialsUserName)
0105         , m_credentialsPassword(other.m_credentialsPassword)
0106         , m_personVersion(other.m_personVersion)
0107         , m_friendVersion(other.m_friendVersion)
0108         , m_messageVersion(other.m_messageVersion)
0109         , m_achievementVersion(other.m_achievementVersion)
0110         , m_activityVersion(other.m_activityVersion)
0111         , m_contentVersion(other.m_contentVersion)
0112         , m_fanVersion(other.m_fanVersion)
0113         , m_forumVersion(other.m_forumVersion)
0114         , m_knowledgebaseVersion(other.m_knowledgebaseVersion)
0115         , m_eventVersion(other.m_eventVersion)
0116         , m_commentVersion(other.m_commentVersion)
0117         , m_registerUrl(other.m_registerUrl)
0118         , m_internals(other.m_internals)
0119         , m_additionalAgentInformation(other.m_additionalAgentInformation)
0120     {
0121     }
0122 
0123     Private(PlatformDependent *internals,
0124             const QUrl &baseUrl,
0125             const QString &name,
0126             const QUrl &icon,
0127             const QString &person,
0128             const QString &friendV,
0129             const QString &message,
0130             const QString &achievement,
0131             const QString &activity,
0132             const QString &content,
0133             const QString &fan,
0134             const QString &forum,
0135             const QString &knowledgebase,
0136             const QString &event,
0137             const QString &comment,
0138             const QString &registerUrl,
0139             const QString &additionalAgentInformation)
0140         : m_baseUrl(baseUrl)
0141         , m_icon(icon)
0142         , m_name(name)
0143         , m_personVersion(person)
0144         , m_friendVersion(friendV)
0145         , m_messageVersion(message)
0146         , m_achievementVersion(achievement)
0147         , m_activityVersion(activity)
0148         , m_contentVersion(content)
0149         , m_fanVersion(fan)
0150         , m_forumVersion(forum)
0151         , m_knowledgebaseVersion(knowledgebase)
0152         , m_eventVersion(event)
0153         , m_commentVersion(comment)
0154         , m_registerUrl(registerUrl)
0155         , m_internals(internals)
0156         , m_additionalAgentInformation(additionalAgentInformation)
0157     {
0158         if (m_baseUrl.isEmpty()) {
0159             return;
0160         }
0161         QString user;
0162         QString pass;
0163         if (m_internals->hasCredentials(m_baseUrl) && m_internals->loadCredentials(m_baseUrl, user, pass)) {
0164             m_credentialsUserName = user;
0165             m_credentialsPassword = pass;
0166         }
0167     }
0168 
0169     ~Private()
0170     {
0171     }
0172 };
0173 
0174 Provider::Provider()
0175     : d(new Private)
0176 {
0177 }
0178 
0179 Provider::Provider(const Provider &other)
0180     : d(other.d)
0181 {
0182 }
0183 
0184 Provider::Provider(PlatformDependent *internals,
0185                    const QUrl &baseUrl,
0186                    const QString &name,
0187                    const QUrl &icon,
0188                    const QString &person,
0189                    const QString &friendV,
0190                    const QString &message,
0191                    const QString &achievement,
0192                    const QString &activity,
0193                    const QString &content,
0194                    const QString &fan,
0195                    const QString &forum,
0196                    const QString &knowledgebase,
0197                    const QString &event,
0198                    const QString &comment)
0199     : d(new Private(internals,
0200                     baseUrl,
0201                     name,
0202                     icon,
0203                     person,
0204                     friendV,
0205                     message,
0206                     achievement,
0207                     activity,
0208                     content,
0209                     fan,
0210                     forum,
0211                     knowledgebase,
0212                     event,
0213                     comment,
0214                     QString(),
0215                     QString()))
0216 {
0217 }
0218 
0219 Provider::Provider(PlatformDependent *internals,
0220                    const QUrl &baseUrl,
0221                    const QString &name,
0222                    const QUrl &icon,
0223                    const QString &person,
0224                    const QString &friendV,
0225                    const QString &message,
0226                    const QString &achievement,
0227                    const QString &activity,
0228                    const QString &content,
0229                    const QString &fan,
0230                    const QString &forum,
0231                    const QString &knowledgebase,
0232                    const QString &event,
0233                    const QString &comment,
0234                    const QString &registerUrl)
0235     : d(new Private(internals,
0236                     baseUrl,
0237                     name,
0238                     icon,
0239                     person,
0240                     friendV,
0241                     message,
0242                     achievement,
0243                     activity,
0244                     content,
0245                     fan,
0246                     forum,
0247                     knowledgebase,
0248                     event,
0249                     comment,
0250                     registerUrl,
0251                     QString()))
0252 {
0253 }
0254 
0255 Provider::Provider(PlatformDependent *internals,
0256                    const QUrl &baseUrl,
0257                    const QString &name,
0258                    const QUrl &icon,
0259                    const QString &person,
0260                    const QString &friendV,
0261                    const QString &message,
0262                    const QString &achievement,
0263                    const QString &activity,
0264                    const QString &content,
0265                    const QString &fan,
0266                    const QString &forum,
0267                    const QString &knowledgebase,
0268                    const QString &event,
0269                    const QString &comment,
0270                    const QString &registerUrl,
0271                    const QString &additionalAgentInformation)
0272     : d(new Private(internals,
0273                     baseUrl,
0274                     name,
0275                     icon,
0276                     person,
0277                     friendV,
0278                     message,
0279                     achievement,
0280                     activity,
0281                     content,
0282                     fan,
0283                     forum,
0284                     knowledgebase,
0285                     event,
0286                     comment,
0287                     registerUrl,
0288                     additionalAgentInformation))
0289 {
0290 }
0291 
0292 Provider &Provider::operator=(const Attica::Provider &other)
0293 {
0294     d = other.d;
0295     return *this;
0296 }
0297 
0298 Provider::~Provider()
0299 {
0300 }
0301 
0302 QUrl Provider::baseUrl() const
0303 {
0304     return d->m_baseUrl;
0305 }
0306 
0307 bool Provider::isValid() const
0308 {
0309     return d->m_baseUrl.isValid();
0310 }
0311 
0312 bool Provider::isEnabled() const
0313 {
0314     if (!isValid()) {
0315         return false;
0316     }
0317 
0318     return d->m_internals->isEnabled(d->m_baseUrl);
0319 }
0320 
0321 void Provider::setEnabled(bool enabled)
0322 {
0323     if (!isValid()) {
0324         return;
0325     }
0326 
0327     d->m_internals->enableProvider(d->m_baseUrl, enabled);
0328 }
0329 
0330 void Provider::setAdditionalAgentInformation(const QString &additionalInformation)
0331 {
0332     d->m_additionalAgentInformation = additionalInformation;
0333 }
0334 
0335 QString Provider::additionalAgentInformation() const
0336 {
0337     return d->m_additionalAgentInformation;
0338 }
0339 
0340 QString Provider::name() const
0341 {
0342     return d->m_name;
0343 }
0344 
0345 QUrl Attica::Provider::icon() const
0346 {
0347     return d->m_icon;
0348 }
0349 
0350 bool Provider::hasCredentials()
0351 {
0352     if (!isValid()) {
0353         return false;
0354     }
0355 
0356     return d->m_internals->hasCredentials(d->m_baseUrl);
0357 }
0358 
0359 bool Provider::hasCredentials() const
0360 {
0361     if (!isValid()) {
0362         return false;
0363     }
0364 
0365     return d->m_internals->hasCredentials(d->m_baseUrl);
0366 }
0367 
0368 bool Provider::loadCredentials(QString &user, QString &password)
0369 {
0370     if (!isValid()) {
0371         return false;
0372     }
0373 
0374     if (d->m_internals->loadCredentials(d->m_baseUrl, user, password)) {
0375         d->m_credentialsUserName = user;
0376         d->m_credentialsPassword = password;
0377         return true;
0378     }
0379     return false;
0380 }
0381 
0382 bool Provider::saveCredentials(const QString &user, const QString &password)
0383 {
0384     if (!isValid()) {
0385         return false;
0386     }
0387 
0388     d->m_credentialsUserName = user;
0389     d->m_credentialsPassword = password;
0390     return d->m_internals->saveCredentials(d->m_baseUrl, user, password);
0391 }
0392 
0393 PostJob *Provider::checkLogin(const QString &user, const QString &password)
0394 {
0395     if (!isValid()) {
0396         return nullptr;
0397     }
0398 
0399     QMap<QString, QString> postParameters;
0400 
0401     postParameters.insert(QLatin1String("login"), user);
0402     postParameters.insert(QLatin1String("password"), password);
0403 
0404     return new PostJob(d->m_internals, createRequest(QLatin1String("person/check")), postParameters);
0405 }
0406 
0407 ItemJob<Config> *Provider::requestConfig()
0408 {
0409     if (!isValid()) {
0410         return nullptr;
0411     }
0412 
0413     QUrl url = createUrl(QLatin1String("config"));
0414     return doRequestConfig(url);
0415 }
0416 
0417 PostJob *Provider::registerAccount(const QString &id, const QString &password, const QString &mail, const QString &firstName, const QString &lastName)
0418 {
0419     if (!isValid()) {
0420         return nullptr;
0421     }
0422 
0423     QMap<QString, QString> postParameters;
0424 
0425     postParameters.insert(QLatin1String("login"), id);
0426     postParameters.insert(QLatin1String("password"), password);
0427     postParameters.insert(QLatin1String("firstname"), firstName);
0428     postParameters.insert(QLatin1String("lastname"), lastName);
0429     postParameters.insert(QLatin1String("email"), mail);
0430 
0431     return new PostJob(d->m_internals, createRequest(QLatin1String("person/add")), postParameters);
0432 }
0433 
0434 const QString &Provider::getRegisterAccountUrl() const
0435 {
0436     return d->m_registerUrl;
0437 }
0438 
0439 ItemJob<Person> *Provider::requestPerson(const QString &id)
0440 {
0441     if (!isValid()) {
0442         return nullptr;
0443     }
0444 
0445     QUrl url = createUrl(QLatin1String("person/data/") + id);
0446     return doRequestPerson(url);
0447 }
0448 
0449 ItemJob<Person> *Provider::requestPersonSelf()
0450 {
0451     if (!isValid()) {
0452         return nullptr;
0453     }
0454 
0455     QUrl url = createUrl(QLatin1String("person/self"));
0456     return doRequestPerson(url);
0457 }
0458 
0459 ItemJob<AccountBalance> *Provider::requestAccountBalance()
0460 {
0461     if (!isValid()) {
0462         return nullptr;
0463     }
0464 
0465     QUrl url = createUrl(QLatin1String("person/balance"));
0466     return doRequestAccountBalance(url);
0467 }
0468 
0469 ListJob<Person> *Provider::requestPersonSearchByName(const QString &name)
0470 {
0471     if (!isValid()) {
0472         return nullptr;
0473     }
0474 
0475     QUrl url = createUrl(QStringLiteral("person/data"));
0476     QUrlQuery q(url);
0477     q.addQueryItem(QStringLiteral("name"), name);
0478     url.setQuery(q);
0479     return doRequestPersonList(url);
0480 }
0481 
0482 ListJob<Person> *Provider::requestPersonSearchByLocation(qreal latitude, qreal longitude, qreal distance, int page, int pageSize)
0483 {
0484     if (!isValid()) {
0485         return nullptr;
0486     }
0487 
0488     QUrl url = createUrl(QStringLiteral("person/data"));
0489     QUrlQuery q(url);
0490     q.addQueryItem(QStringLiteral("latitude"), QString::number(latitude));
0491     q.addQueryItem(QStringLiteral("longitude"), QString::number(longitude));
0492     if (distance > 0.0) {
0493         q.addQueryItem(QStringLiteral("distance"), QString::number(distance));
0494     }
0495     q.addQueryItem(QStringLiteral("page"), QString::number(page));
0496     q.addQueryItem(QStringLiteral("pagesize"), QString::number(pageSize));
0497     url.setQuery(q);
0498 
0499     return doRequestPersonList(url);
0500 }
0501 
0502 ListJob<Person> *Provider::requestFriends(const QString &id, int page, int pageSize)
0503 {
0504     if (!isValid()) {
0505         return nullptr;
0506     }
0507 
0508     QUrl url = createUrl(QLatin1String("friend/data/") + id);
0509     QUrlQuery q(url);
0510     q.addQueryItem(QLatin1String("page"), QString::number(page));
0511     q.addQueryItem(QLatin1String("pagesize"), QString::number(pageSize));
0512     url.setQuery(q);
0513 
0514     return doRequestPersonList(url);
0515 }
0516 
0517 ListJob<Person> *Provider::requestSentInvitations(int page, int pageSize)
0518 {
0519     if (!isValid()) {
0520         return nullptr;
0521     }
0522 
0523     QUrl url = createUrl(QStringLiteral("friend/sentinvitations"));
0524     QUrlQuery q(url);
0525     q.addQueryItem(QStringLiteral("page"), QString::number(page));
0526     q.addQueryItem(QStringLiteral("pagesize"), QString::number(pageSize));
0527     url.setQuery(q);
0528 
0529     return doRequestPersonList(url);
0530 }
0531 
0532 ListJob<Person> *Provider::requestReceivedInvitations(int page, int pageSize)
0533 {
0534     if (!isValid()) {
0535         return nullptr;
0536     }
0537 
0538     QUrl url = createUrl(QStringLiteral("friend/receivedinvitations"));
0539     QUrlQuery q(url);
0540     q.addQueryItem(QStringLiteral("page"), QString::number(page));
0541     q.addQueryItem(QStringLiteral("pagesize"), QString::number(pageSize));
0542     url.setQuery(q);
0543 
0544     return doRequestPersonList(url);
0545 }
0546 
0547 ListJob<Achievement> *Provider::requestAchievements(const QString &contentId, const QString &achievementId, const QString &userId)
0548 {
0549     if (!isValid()) {
0550         return nullptr;
0551     }
0552 
0553     QUrl url = createUrl(QLatin1String("achievements/content/") + contentId + achievementId);
0554     QUrlQuery q(url);
0555     q.addQueryItem(QStringLiteral("user_id"), userId);
0556     url.setQuery(q);
0557     return doRequestAchievementList(url);
0558 }
0559 
0560 ItemPostJob<Achievement> *Provider::addNewAchievement(const QString &contentId, const Achievement &newAchievement)
0561 {
0562     if (!isValid()) {
0563         return nullptr;
0564     }
0565 
0566     StringMap postParameters;
0567     int i = 0;
0568     int j = 0;
0569 
0570     postParameters.insert(QLatin1String("name"), newAchievement.name());
0571     postParameters.insert(QLatin1String("description"), newAchievement.description());
0572     postParameters.insert(QLatin1String("explanation"), newAchievement.explanation());
0573     postParameters.insert(QLatin1String("points"), QString::number(newAchievement.points()));
0574     postParameters.insert(QLatin1String("image"), newAchievement.image().toLocalFile());
0575     const auto dependenciesList = newAchievement.dependencies();
0576     for (const QString &dependency : dependenciesList) {
0577         postParameters.insert(QString::fromLatin1("dependencies[%1]").arg(QString::number(i++)), dependency);
0578     }
0579 
0580     postParameters.insert(QLatin1String("type"), Achievement::achievementTypeToString(newAchievement.type()));
0581     const auto optionsList = newAchievement.options();
0582     for (const QString &option : optionsList) {
0583         postParameters.insert(QString::fromLatin1("options[%1]").arg(QString::number(j++)), option);
0584     }
0585 
0586     postParameters.insert(QLatin1String("steps"), QString::number(newAchievement.steps()));
0587     postParameters.insert(QLatin1String("visibility"), Achievement::achievementVisibilityToString(newAchievement.visibility()));
0588 
0589     return new ItemPostJob<Achievement>(d->m_internals, createRequest(QLatin1String("achievements/content/") + contentId), postParameters);
0590 }
0591 
0592 PutJob *Provider::editAchievement(const QString &contentId, const QString &achievementId, const Achievement &achievement)
0593 {
0594     if (!isValid()) {
0595         return nullptr;
0596     }
0597 
0598     if (!dynamic_cast<Attica::PlatformDependentV2 *>(d->m_internals)) {
0599         return nullptr;
0600     }
0601 
0602     StringMap postParameters;
0603     int i = 0;
0604     int j = 0;
0605 
0606     postParameters.insert(QLatin1String("name"), achievement.name());
0607     postParameters.insert(QLatin1String("description"), achievement.description());
0608     postParameters.insert(QLatin1String("explanation"), achievement.explanation());
0609     postParameters.insert(QLatin1String("points"), QString::number(achievement.points()));
0610     postParameters.insert(QLatin1String("image"), achievement.image().toLocalFile());
0611     const auto dependenciesList = achievement.dependencies();
0612     for (const QString &dependency : dependenciesList) {
0613         postParameters.insert(QString::fromLatin1("dependencies[%1]").arg(QString::number(i++)), dependency);
0614     }
0615 
0616     postParameters.insert(QLatin1String("type"), Achievement::achievementTypeToString(achievement.type()));
0617     const auto optionsList = achievement.options();
0618     for (const QString &option : optionsList) {
0619         postParameters.insert(QString::fromLatin1("options[%1]").arg(QString::number(j++)), option);
0620     }
0621 
0622     postParameters.insert(QLatin1String("steps"), QString::number(achievement.steps()));
0623     postParameters.insert(QLatin1String("visibility"), Achievement::achievementVisibilityToString(achievement.visibility()));
0624 
0625     return new ItemPutJob<Achievement>(d->m_internals, createRequest(QLatin1String("achievement/content/") + contentId + achievementId), postParameters);
0626 }
0627 
0628 DeleteJob *Provider::deleteAchievement(const QString &contentId, const QString &achievementId)
0629 {
0630     if (!isValid()) {
0631         return nullptr;
0632     }
0633 
0634     if (!dynamic_cast<Attica::PlatformDependentV2 *>(d->m_internals)) {
0635         return nullptr;
0636     }
0637 
0638     return new ItemDeleteJob<Achievement>(d->m_internals, createRequest(QLatin1String("achievements/progress/") + contentId + achievementId));
0639 }
0640 
0641 PostJob *Provider::setAchievementProgress(const QString &id, const QVariant &progress, const QDateTime &timestamp)
0642 {
0643     if (!isValid()) {
0644         return nullptr;
0645     }
0646 
0647     StringMap postParameters;
0648 
0649     postParameters.insert(QLatin1String("progress"), progress.toString());
0650     postParameters.insert(QLatin1String("timestamp"), timestamp.toString());
0651 
0652     return new ItemPostJob<Achievement>(d->m_internals, createRequest(QLatin1String("achievements/progress/") + id), postParameters);
0653 }
0654 
0655 DeleteJob *Provider::resetAchievementProgress(const QString &id)
0656 {
0657     if (!isValid()) {
0658         return nullptr;
0659     }
0660 
0661     if (!dynamic_cast<Attica::PlatformDependentV2 *>(d->m_internals)) {
0662         return nullptr;
0663     }
0664 
0665     return new ItemDeleteJob<Achievement>(d->m_internals, createRequest(QLatin1String("achievements/progress/") + id));
0666 }
0667 
0668 ListJob<Activity> *Provider::requestActivities()
0669 {
0670     if (!isValid()) {
0671         return nullptr;
0672     }
0673 
0674     // qCDebug(ATTICA) << "request activity";
0675     QUrl url = createUrl(QLatin1String("activity"));
0676     return doRequestActivityList(url);
0677 }
0678 
0679 ListJob<Project> *Provider::requestProjects()
0680 {
0681     if (!isValid()) {
0682         return nullptr;
0683     }
0684 
0685     // qCDebug(ATTICA) << "request projects";
0686     QUrl url = createUrl(QLatin1String("buildservice/project/list"));
0687     return new ListJob<Project>(d->m_internals, createRequest(url));
0688 }
0689 
0690 ItemJob<Project> *Provider::requestProject(const QString &id)
0691 {
0692     if (!isValid()) {
0693         return nullptr;
0694     }
0695 
0696     QUrl url = createUrl(QLatin1String("buildservice/project/get/") + id);
0697     // qCDebug(ATTICA) << url;
0698     return new ItemJob<Project>(d->m_internals, createRequest(url));
0699 }
0700 
0701 QMap<QString, QString> projectPostParameters(const Project &project)
0702 {
0703     QMap<QString, QString> postParameters;
0704 
0705     if (!project.name().isEmpty()) {
0706         postParameters.insert(QLatin1String("name"), project.name());
0707     }
0708     if (!project.summary().isEmpty()) {
0709         postParameters.insert(QLatin1String("summary"), project.summary());
0710     }
0711     if (!project.description().isEmpty()) {
0712         postParameters.insert(QLatin1String("description"), project.description());
0713     }
0714     if (!project.url().isEmpty()) {
0715         postParameters.insert(QLatin1String("url"), project.url());
0716     }
0717     if (!project.developers().isEmpty()) {
0718         postParameters.insert(QLatin1String("developers"), project.developers().join(QLatin1Char('\n')));
0719     }
0720     if (!project.version().isEmpty()) {
0721         postParameters.insert(QLatin1String("version"), project.version());
0722     }
0723     if (!project.license().isEmpty()) {
0724         postParameters.insert(QLatin1String("license"), project.license());
0725     }
0726     if (!project.requirements().isEmpty()) {
0727         postParameters.insert(QLatin1String("requirements"), project.requirements());
0728     }
0729     // The specfile generator expects an empty string parameter if it is supposed to regenerate the spec file
0730     // So we need to check for nullity here as opposed to an empty string.
0731     if (!project.specFile().isNull()) {
0732         postParameters.insert(QLatin1String("specfile"), project.specFile());
0733     }
0734     return postParameters;
0735 }
0736 
0737 PostJob *Provider::createProject(const Project &project)
0738 {
0739     if (!isValid()) {
0740         return nullptr;
0741     }
0742 
0743     return new PostJob(d->m_internals, createRequest(QLatin1String("buildservice/project/create")), projectPostParameters(project));
0744 }
0745 
0746 PostJob *Provider::editProject(const Project &project)
0747 {
0748     if (!isValid()) {
0749         return nullptr;
0750     }
0751 
0752     return new PostJob(d->m_internals, createRequest(QLatin1String("buildservice/project/edit/") + project.id()), projectPostParameters(project));
0753 }
0754 
0755 PostJob *Provider::deleteProject(const Project &project)
0756 {
0757     if (!isValid()) {
0758         return nullptr;
0759     }
0760 
0761     return new PostJob(d->m_internals, createRequest(QLatin1String("buildservice/project/delete/") + project.id()), projectPostParameters(project));
0762 }
0763 
0764 ItemJob<BuildService> *Provider::requestBuildService(const QString &id)
0765 {
0766     if (!isValid()) {
0767         return nullptr;
0768     }
0769 
0770     QUrl url = createUrl(QLatin1String("buildservice/buildservices/get/") + id);
0771     return new ItemJob<BuildService>(d->m_internals, createRequest(url));
0772 }
0773 
0774 ItemJob<Publisher> *Provider::requestPublisher(const QString &id)
0775 {
0776     if (!isValid()) {
0777         return nullptr;
0778     }
0779 
0780     // qCDebug(ATTICA) << "request publisher" << id;
0781     QUrl url = createUrl(QLatin1String("buildservice/publishing/getpublisher/") + id);
0782     return new ItemJob<Publisher>(d->m_internals, createRequest(url));
0783 }
0784 
0785 PostJob *Provider::savePublisherField(const Project &project, const PublisherField &field)
0786 {
0787     if (!isValid()) {
0788         return nullptr;
0789     }
0790 
0791     StringMap postParameters;
0792     postParameters.insert(QLatin1String("fields[0][name]"), field.name());
0793     postParameters.insert(QLatin1String("fields[0][fieldtype]"), field.type());
0794     postParameters.insert(QLatin1String("fields[0][data]"), field.data());
0795 
0796     QString url = QLatin1String("buildservice/publishing/savefields/") + project.id();
0797     // qCDebug(ATTICA) << "saving field values...";
0798     return new PostJob(d->m_internals, createRequest(url), postParameters);
0799 }
0800 
0801 PostJob *Provider::publishBuildJob(const BuildServiceJob &buildjob, const Publisher &publisher)
0802 {
0803     if (!isValid()) {
0804         return nullptr;
0805     }
0806 
0807     StringMap postParameters;
0808     postParameters.insert(QLatin1String("dummyparameter"), QLatin1String("dummyvalue"));
0809 
0810     QString url = QLatin1String("buildservice/publishing/publishtargetresult/") + buildjob.id() + QLatin1Char('/') + publisher.id();
0811     // qCDebug(ATTICA) << "pub'ing";
0812     return new PostJob(d->m_internals, createRequest(url), postParameters);
0813 }
0814 
0815 // Buildservices and their jobs
0816 ItemJob<BuildServiceJobOutput> *Provider::requestBuildServiceJobOutput(const QString &id)
0817 {
0818     if (!isValid()) {
0819         return nullptr;
0820     }
0821 
0822     QUrl url = createUrl(QLatin1String("buildservice/jobs/getoutput/") + id);
0823     // qCDebug(ATTICA) << url;
0824     return new ItemJob<BuildServiceJobOutput>(d->m_internals, createRequest(url));
0825 }
0826 
0827 ItemJob<BuildServiceJob> *Provider::requestBuildServiceJob(const QString &id)
0828 {
0829     if (!isValid()) {
0830         return nullptr;
0831     }
0832 
0833     QUrl url = createUrl(QLatin1String("buildservice/jobs/get/") + id);
0834     // qCDebug(ATTICA) << url;
0835     return new ItemJob<BuildServiceJob>(d->m_internals, createRequest(url));
0836 }
0837 
0838 QMap<QString, QString> buildServiceJobPostParameters(const BuildServiceJob &buildjob)
0839 {
0840     QMap<QString, QString> postParameters;
0841 
0842     if (!buildjob.name().isEmpty()) {
0843         postParameters.insert(QLatin1String("name"), buildjob.name());
0844     }
0845     if (!buildjob.projectId().isEmpty()) {
0846         postParameters.insert(QLatin1String("projectid"), buildjob.projectId());
0847     }
0848     if (!buildjob.target().isEmpty()) {
0849         postParameters.insert(QLatin1String("target"), buildjob.target());
0850     }
0851     if (!buildjob.buildServiceId().isEmpty()) {
0852         postParameters.insert(QLatin1String("buildservice"), buildjob.buildServiceId());
0853     }
0854 
0855     return postParameters;
0856 }
0857 
0858 PostJob *Provider::cancelBuildServiceJob(const BuildServiceJob &job)
0859 {
0860     if (!isValid()) {
0861         return nullptr;
0862     }
0863 
0864     StringMap postParameters;
0865     postParameters.insert(QLatin1String("dummyparameter"), QLatin1String("dummyvalue"));
0866     // qCDebug(ATTICA) << "b....................b";
0867     return new PostJob(d->m_internals, createRequest(QLatin1String("buildservice/jobs/cancel/") + job.id()), postParameters);
0868 }
0869 
0870 PostJob *Provider::createBuildServiceJob(const BuildServiceJob &job)
0871 {
0872     if (!isValid()) {
0873         return nullptr;
0874     }
0875 
0876     StringMap postParameters;
0877     // A postjob won't be run without parameters.
0878     // so even while we don't need any in this case,
0879     // we add dummy data to the request
0880     postParameters.insert(QLatin1String("dummyparameter"), QLatin1String("dummyvalue"));
0881     // qCDebug(ATTICA) << "Creating new BSJ on" << job.buildServiceId();
0882     return new PostJob(
0883         d->m_internals,
0884         createRequest(QLatin1String("buildservice/jobs/create/") + job.projectId() + QLatin1Char('/') + job.buildServiceId() + QLatin1Char('/') + job.target()),
0885         postParameters);
0886 }
0887 
0888 ListJob<BuildService> *Provider::requestBuildServices()
0889 {
0890     if (!isValid()) {
0891         return nullptr;
0892     }
0893 
0894     // qCDebug(ATTICA) << "request projects";
0895     QUrl url = createUrl(QLatin1String("buildservice/buildservices/list"));
0896     return new ListJob<BuildService>(d->m_internals, createRequest(url));
0897 }
0898 
0899 ListJob<Publisher> *Provider::requestPublishers()
0900 {
0901     if (!isValid()) {
0902         return nullptr;
0903     }
0904 
0905     QUrl url = createUrl(QLatin1String("buildservice/publishing/getpublishingcapabilities"));
0906     // qCDebug(ATTICA) << "request publishers" << url;
0907     return new ListJob<Publisher>(d->m_internals, createRequest(url));
0908 }
0909 
0910 ListJob<BuildServiceJob> *Provider::requestBuildServiceJobs(const Project &project)
0911 {
0912     if (!isValid()) {
0913         return nullptr;
0914     }
0915 
0916     // qCDebug(ATTICA) << "request projects";
0917     QUrl url = createUrl(QLatin1String("buildservice/jobs/list/") + project.id());
0918     return new ListJob<BuildServiceJob>(d->m_internals, createRequest(url));
0919 }
0920 
0921 ListJob<RemoteAccount> *Provider::requestRemoteAccounts()
0922 {
0923     if (!isValid()) {
0924         return nullptr;
0925     }
0926 
0927     // qCDebug(ATTICA) << "request remoteaccounts";
0928     QUrl url = createUrl(QLatin1String("buildservice/remoteaccounts/list/"));
0929     return new ListJob<RemoteAccount>(d->m_internals, createRequest(url));
0930 }
0931 
0932 PostJob *Provider::createRemoteAccount(const RemoteAccount &account)
0933 {
0934     if (!isValid()) {
0935         return nullptr;
0936     }
0937 
0938     StringMap postParameters;
0939     // A postjob won't be run without parameters.
0940     // so even while we don't need any in this case,
0941     // we add dummy data to the request
0942     postParameters.insert(QLatin1String("login"), account.login());
0943     postParameters.insert(QLatin1String("password"), account.password());
0944     postParameters.insert(QLatin1String("type"), account.type());
0945     postParameters.insert(QLatin1String("typeid"), account.remoteServiceId()); // FIXME: remoteserviceid?
0946     postParameters.insert(QLatin1String("data"), account.data());
0947     // qCDebug(ATTICA) << "Creating new Remoteaccount" << account.id() << account.login() << account.password();
0948     return new PostJob(d->m_internals, createRequest(QLatin1String("buildservice/remoteaccounts/add")), postParameters);
0949 }
0950 
0951 PostJob *Provider::editRemoteAccount(const RemoteAccount &account)
0952 {
0953     if (!isValid()) {
0954         return nullptr;
0955     }
0956 
0957     StringMap postParameters;
0958     // A postjob won't be run without parameters.
0959     // so even while we don't need any in this case,
0960     // we add dummy data to the request
0961     postParameters.insert(QLatin1String("login"), account.login());
0962     postParameters.insert(QLatin1String("password"), account.password());
0963     postParameters.insert(QLatin1String("type"), account.type());
0964     postParameters.insert(QLatin1String("typeid"), account.remoteServiceId()); // FIXME: remoteserviceid?
0965     postParameters.insert(QLatin1String("data"), account.data());
0966     // qCDebug(ATTICA) << "Creating new Remoteaccount" << account.id() << account.login() << account.password();
0967     return new PostJob(d->m_internals, createRequest(QLatin1String("buildservice/remoteaccounts/edit/") + account.id()), postParameters);
0968 }
0969 
0970 ItemJob<RemoteAccount> *Provider::requestRemoteAccount(const QString &id)
0971 {
0972     if (!isValid()) {
0973         return nullptr;
0974     }
0975 
0976     QUrl url = createUrl(QLatin1String("buildservice/remoteaccounts/get/") + id);
0977     // qCDebug(ATTICA) << url;
0978     return new ItemJob<RemoteAccount>(d->m_internals, createRequest(url));
0979 }
0980 
0981 PostJob *Provider::deleteRemoteAccount(const QString &id)
0982 {
0983     if (!isValid()) {
0984         return nullptr;
0985     }
0986 
0987     StringMap postParameters;
0988     return new PostJob(d->m_internals, createRequest(QLatin1String("buildservice/remoteaccounts/remove/") + id), postParameters);
0989 }
0990 
0991 PostJob *Provider::uploadTarballToBuildService(const QString &projectId, const QString &fileName, const QByteArray &payload)
0992 {
0993     if (!isValid()) {
0994         return nullptr;
0995     }
0996 
0997     QUrl url = createUrl(QLatin1String("buildservice/project/uploadsource/") + projectId);
0998     // qCDebug(ATTICA) << "Up'ing tarball" << url << projectId << fileName << payload;
0999     PostFileData postRequest(url);
1000     postRequest.addFile(fileName, payload, QLatin1String("application/octet-stream"), QLatin1String("source"));
1001     return new PostJob(d->m_internals, postRequest.request(), postRequest.data());
1002 }
1003 
1004 // Activity
1005 
1006 PostJob *Provider::postActivity(const QString &message)
1007 {
1008     if (!isValid()) {
1009         return nullptr;
1010     }
1011 
1012     StringMap postParameters;
1013     postParameters.insert(QLatin1String("message"), message);
1014     return new PostJob(d->m_internals, createRequest(QLatin1String("activity")), postParameters);
1015 }
1016 
1017 PostJob *Provider::inviteFriend(const QString &to, const QString &message)
1018 {
1019     if (!isValid()) {
1020         return nullptr;
1021     }
1022 
1023     StringMap postParameters;
1024     postParameters.insert(QLatin1String("message"), message);
1025     return new PostJob(d->m_internals, createRequest(QLatin1String("friend/invite/") + to), postParameters);
1026 }
1027 
1028 PostJob *Provider::approveFriendship(const QString &to)
1029 {
1030     if (!isValid()) {
1031         return nullptr;
1032     }
1033 
1034     return new PostJob(d->m_internals, createRequest(QLatin1String("friend/approve/") + to));
1035 }
1036 
1037 PostJob *Provider::declineFriendship(const QString &to)
1038 {
1039     if (!isValid()) {
1040         return nullptr;
1041     }
1042 
1043     return new PostJob(d->m_internals, createRequest(QLatin1String("friend/decline/") + to));
1044 }
1045 
1046 PostJob *Provider::cancelFriendship(const QString &to)
1047 {
1048     if (!isValid()) {
1049         return nullptr;
1050     }
1051 
1052     return new PostJob(d->m_internals, createRequest(QLatin1String("friend/cancel/") + to));
1053 }
1054 
1055 PostJob *Provider::postLocation(qreal latitude, qreal longitude, const QString &city, const QString &country)
1056 {
1057     if (!isValid()) {
1058         return nullptr;
1059     }
1060 
1061     StringMap postParameters;
1062     postParameters.insert(QLatin1String("latitude"), QString::number(latitude));
1063     postParameters.insert(QLatin1String("longitude"), QString::number(longitude));
1064     postParameters.insert(QLatin1String("city"), city);
1065     postParameters.insert(QLatin1String("country"), country);
1066     return new PostJob(d->m_internals, createRequest(QLatin1String("person/self")), postParameters);
1067 }
1068 
1069 ListJob<Folder> *Provider::requestFolders()
1070 {
1071     if (!isValid()) {
1072         return nullptr;
1073     }
1074 
1075     return doRequestFolderList(createUrl(QLatin1String("message")));
1076 }
1077 
1078 ListJob<Message> *Provider::requestMessages(const Folder &folder)
1079 {
1080     if (!isValid()) {
1081         return nullptr;
1082     }
1083 
1084     return doRequestMessageList(createUrl(QLatin1String("message/") + folder.id()));
1085 }
1086 
1087 ListJob<Message> *Provider::requestMessages(const Folder &folder, Message::Status status)
1088 {
1089     if (!isValid()) {
1090         return nullptr;
1091     }
1092 
1093     QUrl url = createUrl(QLatin1String("message/") + folder.id());
1094     QUrlQuery q(url);
1095     q.addQueryItem(QStringLiteral("status"), QString::number(status));
1096     url.setQuery(q);
1097     return doRequestMessageList(url);
1098 }
1099 
1100 ItemJob<Message> *Provider::requestMessage(const Folder &folder, const QString &id)
1101 {
1102     if (!isValid()) {
1103         return nullptr;
1104     }
1105 
1106     return new ItemJob<Message>(d->m_internals, createRequest(QLatin1String("message/") + folder.id() + QLatin1Char('/') + id));
1107 }
1108 
1109 PostJob *Provider::postMessage(const Message &message)
1110 {
1111     if (!isValid()) {
1112         return nullptr;
1113     }
1114 
1115     StringMap postParameters;
1116     postParameters.insert(QLatin1String("message"), message.body());
1117     postParameters.insert(QLatin1String("subject"), message.subject());
1118     postParameters.insert(QLatin1String("to"), message.to());
1119     return new PostJob(d->m_internals, createRequest(QLatin1String("message/2")), postParameters);
1120 }
1121 
1122 ListJob<Category> *Provider::requestCategories()
1123 {
1124     if (!isValid()) {
1125         return nullptr;
1126     }
1127 
1128     const QUrl url = createUrl(QLatin1String("content/categories"));
1129 
1130     // Thread-local cache of categories requests. They are fairly slow and block startup
1131     static QThreadStorage<QHash<QUrl, ListJob<Category> *>> reqs;
1132     ListJob<Category> *job = reqs.localData().value(url);
1133     if (!job) {
1134         job = new ListJob<Category>(d->m_internals, createRequest(url));
1135         QObject::connect(job, &BaseJob::finished, [url] {
1136             reqs.localData().remove(url);
1137         });
1138         reqs.localData().insert(url, job);
1139     }
1140     return job;
1141 }
1142 
1143 ListJob<License> *Provider::requestLicenses()
1144 {
1145     if (!isValid()) {
1146         return nullptr;
1147     }
1148 
1149     QUrl url = createUrl(QLatin1String("content/licenses"));
1150     ListJob<License> *job = new ListJob<License>(d->m_internals, createRequest(url));
1151     return job;
1152 }
1153 
1154 ListJob<Distribution> *Provider::requestDistributions()
1155 {
1156     if (!isValid()) {
1157         return nullptr;
1158     }
1159 
1160     QUrl url = createUrl(QLatin1String("content/distributions"));
1161     ListJob<Distribution> *job = new ListJob<Distribution>(d->m_internals, createRequest(url));
1162     return job;
1163 }
1164 
1165 ListJob<HomePageType> *Provider::requestHomePageTypes()
1166 {
1167     if (!isValid()) {
1168         return nullptr;
1169     }
1170 
1171     QUrl url = createUrl(QLatin1String("content/homepages"));
1172     ListJob<HomePageType> *job = new ListJob<HomePageType>(d->m_internals, createRequest(url));
1173     return job;
1174 }
1175 
1176 ListJob<Content> *Provider::searchContents(const Category::List &categories, const QString &search, SortMode sortMode, uint page, uint pageSize)
1177 {
1178     return searchContents(categories, QString(), Distribution::List(), License::List(), search, sortMode, page, pageSize);
1179 }
1180 
1181 ListJob<Content> *
1182 Provider::searchContentsByPerson(const Category::List &categories, const QString &person, const QString &search, SortMode sortMode, uint page, uint pageSize)
1183 {
1184     return searchContents(categories, person, Distribution::List(), License::List(), search, sortMode, page, pageSize);
1185 }
1186 
1187 ListJob<Content> *Provider::searchContents(const Category::List &categories,
1188                                            const QString &person,
1189                                            const Distribution::List &distributions,
1190                                            const License::List &licenses,
1191                                            const QString &search,
1192                                            SortMode sortMode,
1193                                            uint page,
1194                                            uint pageSize)
1195 {
1196     if (!isValid()) {
1197         return nullptr;
1198     }
1199 
1200     QUrl url = createUrl(QStringLiteral("content/data"));
1201     QUrlQuery q(url);
1202     QStringList categoryIds;
1203     categoryIds.reserve(categories.count());
1204     for (const Category &category : categories) {
1205         categoryIds.append(category.id());
1206     }
1207     q.addQueryItem(QStringLiteral("categories"), categoryIds.join(QLatin1Char('x')));
1208 
1209     QStringList distributionIds;
1210     for (const Distribution &distribution : distributions) {
1211         distributionIds.append(QString::number(distribution.id()));
1212     }
1213     q.addQueryItem(QStringLiteral("distribution"), distributionIds.join(QLatin1Char(',')));
1214 
1215     QStringList licenseIds;
1216     for (const License &license : licenses) {
1217         licenseIds.append(QString::number(license.id()));
1218     }
1219     q.addQueryItem(QStringLiteral("license"), licenseIds.join(QLatin1Char(',')));
1220 
1221     if (!person.isEmpty()) {
1222         q.addQueryItem(QStringLiteral("user"), person);
1223     }
1224 
1225     q.addQueryItem(QStringLiteral("search"), search);
1226     QString sortModeString;
1227     switch (sortMode) {
1228     case Newest:
1229         sortModeString = QLatin1String("new");
1230         break;
1231     case Alphabetical:
1232         sortModeString = QLatin1String("alpha");
1233         break;
1234     case Rating:
1235         sortModeString = QLatin1String("high");
1236         break;
1237     case Downloads:
1238         sortModeString = QLatin1String("down");
1239         break;
1240     }
1241 
1242     if (!sortModeString.isEmpty()) {
1243         q.addQueryItem(QStringLiteral("sortmode"), sortModeString);
1244     }
1245 
1246     q.addQueryItem(QStringLiteral("page"), QString::number(page));
1247     q.addQueryItem(QStringLiteral("pagesize"), QString::number(pageSize));
1248 
1249     url.setQuery(q);
1250     ListJob<Content> *job = new ListJob<Content>(d->m_internals, createRequest(url));
1251     return job;
1252 }
1253 
1254 ItemJob<Content> *Provider::requestContent(const QString &id)
1255 {
1256     if (!isValid()) {
1257         return nullptr;
1258     }
1259 
1260     QUrl url = createUrl(QLatin1String("content/data/") + id);
1261     ItemJob<Content> *job = new ItemJob<Content>(d->m_internals, createRequest(url));
1262     return job;
1263 }
1264 
1265 ItemPostJob<Content> *Provider::addNewContent(const Category &category, const Content &cont)
1266 {
1267     if (!isValid() || !category.isValid()) {
1268         return nullptr;
1269     }
1270 
1271     QUrl url = createUrl(QLatin1String("content/add"));
1272     StringMap pars(cont.attributes());
1273 
1274     pars.insert(QLatin1String("type"), category.id());
1275     pars.insert(QLatin1String("name"), cont.name());
1276 
1277     // qCDebug(ATTICA) << "Parameter map: " << pars;
1278 
1279     return new ItemPostJob<Content>(d->m_internals, createRequest(url), pars);
1280 }
1281 
1282 ItemPostJob<Content> *Provider::editContent(const Category &updatedCategory, const QString &contentId, const Content &updatedContent)
1283 {
1284     if (!isValid()) {
1285         return nullptr;
1286     }
1287 
1288     // FIXME I get a server error message here, though the name of the item is changed
1289     QUrl url = createUrl(QLatin1String("content/edit/") + contentId);
1290     StringMap pars(updatedContent.attributes());
1291 
1292     pars.insert(QLatin1String("type"), updatedCategory.id());
1293     pars.insert(QLatin1String("name"), updatedContent.name());
1294 
1295     // qCDebug(ATTICA) << "Parameter map: " << pars;
1296 
1297     return new ItemPostJob<Content>(d->m_internals, createRequest(url), pars);
1298 }
1299 
1300 /*
1301 PostJob* Provider::setDownloadFile(const QString& contentId, QIODevice* payload)
1302 {
1303     QUrl url = createUrl("content/uploaddownload/" + contentId);
1304     PostFileData postRequest(url);
1305     // FIXME mime type
1306     //postRequest.addFile("localfile", payload, "application/octet-stream");
1307     postRequest.addFile("localfile", payload, "image/jpeg");
1308     return new PostJob(d->m_internals, postRequest.request(), postRequest.data());
1309 }
1310 */
1311 
1312 PostJob *Provider::deleteContent(const QString &contentId)
1313 {
1314     if (!isValid()) {
1315         return nullptr;
1316     }
1317 
1318     QUrl url = createUrl(QLatin1String("content/delete/") + contentId);
1319     PostFileData postRequest(url);
1320     postRequest.addArgument(QLatin1String("contentid"), contentId);
1321     return new PostJob(d->m_internals, postRequest.request(), postRequest.data());
1322 }
1323 
1324 PostJob *Provider::setDownloadFile(const QString &contentId, const QString &fileName, const QByteArray &payload)
1325 {
1326     if (!isValid()) {
1327         return nullptr;
1328     }
1329 
1330     QUrl url = createUrl(QLatin1String("content/uploaddownload/") + contentId);
1331     PostFileData postRequest(url);
1332     postRequest.addArgument(QLatin1String("contentid"), contentId);
1333     // FIXME mime type
1334     postRequest.addFile(fileName, payload, QLatin1String("application/octet-stream"));
1335     return new PostJob(d->m_internals, postRequest.request(), postRequest.data());
1336 }
1337 
1338 PostJob *Provider::deleteDownloadFile(const QString &contentId)
1339 {
1340     if (!isValid()) {
1341         return nullptr;
1342     }
1343 
1344     QUrl url = createUrl(QLatin1String("content/deletedownload/") + contentId);
1345     PostFileData postRequest(url);
1346     postRequest.addArgument(QLatin1String("contentid"), contentId);
1347     return new PostJob(d->m_internals, postRequest.request(), postRequest.data());
1348 }
1349 
1350 PostJob *Provider::setPreviewImage(const QString &contentId, const QString &previewId, const QString &fileName, const QByteArray &image)
1351 {
1352     if (!isValid()) {
1353         return nullptr;
1354     }
1355 
1356     QUrl url = createUrl(QLatin1String("content/uploadpreview/") + contentId + QLatin1Char('/') + previewId);
1357 
1358     PostFileData postRequest(url);
1359     postRequest.addArgument(QLatin1String("contentid"), contentId);
1360     postRequest.addArgument(QLatin1String("previewid"), previewId);
1361     // FIXME mime type
1362     postRequest.addFile(fileName, image, QLatin1String("application/octet-stream"));
1363 
1364     return new PostJob(d->m_internals, postRequest.request(), postRequest.data());
1365 }
1366 
1367 PostJob *Provider::deletePreviewImage(const QString &contentId, const QString &previewId)
1368 {
1369     if (!isValid()) {
1370         return nullptr;
1371     }
1372 
1373     QUrl url = createUrl(QLatin1String("content/deletepreview/") + contentId + QLatin1Char('/') + previewId);
1374     PostFileData postRequest(url);
1375     postRequest.addArgument(QLatin1String("contentid"), contentId);
1376     postRequest.addArgument(QLatin1String("previewid"), previewId);
1377     return new PostJob(d->m_internals, postRequest.request(), postRequest.data());
1378 }
1379 
1380 #if ATTICA_BUILD_DEPRECATED_SINCE(0, 2)
1381 PostJob *Provider::voteForContent(const QString &contentId, bool positiveVote)
1382 {
1383     if (!isValid()) {
1384         return nullptr;
1385     }
1386 
1387     StringMap postParameters;
1388     postParameters.insert(QLatin1String("vote"), positiveVote ? QLatin1String("good") : QLatin1String("bad"));
1389     // qCDebug(ATTICA) << "vote: " << positiveVote;
1390     return new PostJob(d->m_internals, createRequest(QLatin1String("content/vote/") + contentId), postParameters);
1391 }
1392 #endif
1393 
1394 PostJob *Provider::voteForContent(const QString &contentId, uint rating)
1395 {
1396     if (!isValid()) {
1397         return nullptr;
1398     }
1399 
1400     // according to OCS API, the rating is 0..100
1401     if (rating > 100) {
1402         qWarning() << "Rating cannot be superior to 100, fallback to 100.";
1403         rating = 100;
1404     }
1405 
1406     StringMap postParameters;
1407     postParameters.insert(QLatin1String("vote"), QString::number(rating));
1408     // qCDebug(ATTICA) << "vote: " << QString::number(rating);
1409     return new PostJob(d->m_internals, createRequest(QLatin1String("content/vote/") + contentId), postParameters);
1410 }
1411 
1412 PostJob *Provider::becomeFan(const QString &contentId)
1413 {
1414     if (!isValid()) {
1415         return nullptr;
1416     }
1417 
1418     QUrl url = createUrl(QLatin1String("fan/add/") + contentId);
1419     PostFileData postRequest(url);
1420     postRequest.addArgument(QLatin1String("contentid"), contentId);
1421     return new PostJob(d->m_internals, postRequest.request(), postRequest.data());
1422 }
1423 
1424 ListJob<Person> *Provider::requestFans(const QString &contentId, uint page, uint pageSize)
1425 {
1426     if (!isValid()) {
1427         return nullptr;
1428     }
1429 
1430     QUrl url = createUrl(QLatin1String("fan/data/") + contentId);
1431     QUrlQuery q(url);
1432     q.addQueryItem(QStringLiteral("contentid"), contentId);
1433     q.addQueryItem(QStringLiteral("page"), QString::number(page));
1434     q.addQueryItem(QStringLiteral("pagesize"), QString::number(pageSize));
1435     url.setQuery(q);
1436     ListJob<Person> *job = new ListJob<Person>(d->m_internals, createRequest(url));
1437     return job;
1438 }
1439 
1440 ListJob<Forum> *Provider::requestForums(uint page, uint pageSize)
1441 {
1442     if (!isValid()) {
1443         return nullptr;
1444     }
1445 
1446     QUrl url = createUrl(QStringLiteral("forum/list"));
1447     QUrlQuery q(url);
1448     q.addQueryItem(QStringLiteral("page"), QString::number(page));
1449     q.addQueryItem(QStringLiteral("pagesize"), QString::number(pageSize));
1450     url.setQuery(q);
1451 
1452     return doRequestForumList(url);
1453 }
1454 
1455 ListJob<Topic> *
1456 Provider::requestTopics(const QString &forum, const QString &search, const QString &description, Provider::SortMode mode, int page, int pageSize)
1457 {
1458     if (!isValid()) {
1459         return nullptr;
1460     }
1461 
1462     QUrl url = createUrl(QStringLiteral("forum/topics/list"));
1463     QUrlQuery q(url);
1464     q.addQueryItem(QStringLiteral("forum"), forum);
1465     q.addQueryItem(QStringLiteral("search"), search);
1466     q.addQueryItem(QStringLiteral("description"), description);
1467     QString sortModeString;
1468     switch (mode) {
1469     case Newest:
1470         sortModeString = QLatin1String("new");
1471         break;
1472     case Alphabetical:
1473         sortModeString = QLatin1String("alpha");
1474         break;
1475     default:
1476         break;
1477     }
1478     if (!sortModeString.isEmpty()) {
1479         q.addQueryItem(QStringLiteral("sortmode"), sortModeString);
1480     }
1481     q.addQueryItem(QStringLiteral("page"), QString::number(page));
1482     q.addQueryItem(QStringLiteral("pagesize"), QString::number(pageSize));
1483     url.setQuery(q);
1484 
1485     return doRequestTopicList(url);
1486 }
1487 
1488 PostJob *Provider::postTopic(const QString &forumId, const QString &subject, const QString &content)
1489 {
1490     if (!isValid()) {
1491         return nullptr;
1492     }
1493 
1494     StringMap postParameters;
1495     postParameters.insert(QLatin1String("subject"), subject);
1496     postParameters.insert(QLatin1String("content"), content);
1497     postParameters.insert(QLatin1String("forum"), forumId);
1498     return new PostJob(d->m_internals, createRequest(QLatin1String("forum/topic/add")), postParameters);
1499 }
1500 
1501 ItemJob<DownloadItem> *Provider::downloadLink(const QString &contentId, const QString &itemId)
1502 {
1503     if (!isValid()) {
1504         return nullptr;
1505     }
1506 
1507     QUrl url = createUrl(QLatin1String("content/download/") + contentId + QLatin1Char('/') + itemId);
1508     ItemJob<DownloadItem> *job = new ItemJob<DownloadItem>(d->m_internals, createRequest(url));
1509     return job;
1510 }
1511 
1512 ItemJob<KnowledgeBaseEntry> *Provider::requestKnowledgeBaseEntry(const QString &id)
1513 {
1514     if (!isValid()) {
1515         return nullptr;
1516     }
1517 
1518     QUrl url = createUrl(QLatin1String("knowledgebase/data/") + id);
1519     ItemJob<KnowledgeBaseEntry> *job = new ItemJob<KnowledgeBaseEntry>(d->m_internals, createRequest(url));
1520     return job;
1521 }
1522 
1523 ListJob<KnowledgeBaseEntry> *Provider::searchKnowledgeBase(const Content &content, const QString &search, Provider::SortMode sortMode, int page, int pageSize)
1524 {
1525     if (!isValid()) {
1526         return nullptr;
1527     }
1528 
1529     QUrl url = createUrl(QStringLiteral("knowledgebase/data"));
1530     QUrlQuery q(url);
1531     if (content.isValid()) {
1532         q.addQueryItem(QStringLiteral("content"), content.id());
1533     }
1534 
1535     q.addQueryItem(QStringLiteral("search"), search);
1536     QString sortModeString;
1537     switch (sortMode) {
1538     case Newest:
1539         sortModeString = QLatin1String("new");
1540         break;
1541     case Alphabetical:
1542         sortModeString = QLatin1String("alpha");
1543         break;
1544     case Rating:
1545         sortModeString = QLatin1String("high");
1546         break;
1547     // FIXME: knowledge base doesn't have downloads
1548     case Downloads:
1549         sortModeString = QLatin1String("new");
1550         break;
1551     }
1552     if (!sortModeString.isEmpty()) {
1553         q.addQueryItem(QStringLiteral("sortmode"), sortModeString);
1554     }
1555 
1556     q.addQueryItem(QStringLiteral("page"), QString::number(page));
1557     q.addQueryItem(QStringLiteral("pagesize"), QString::number(pageSize));
1558     url.setQuery(q);
1559 
1560     ListJob<KnowledgeBaseEntry> *job = new ListJob<KnowledgeBaseEntry>(d->m_internals, createRequest(url));
1561     return job;
1562 }
1563 
1564 ItemJob<Event> *Provider::requestEvent(const QString &id)
1565 {
1566     if (!isValid()) {
1567         return nullptr;
1568     }
1569 
1570     ItemJob<Event> *job = new ItemJob<Event>(d->m_internals, createRequest(QLatin1String("event/data/") + id));
1571     return job;
1572 }
1573 
1574 ListJob<Event> *Provider::requestEvent(const QString &country, const QString &search, const QDate &startAt, Provider::SortMode mode, int page, int pageSize)
1575 {
1576     if (!isValid()) {
1577         return nullptr;
1578     }
1579 
1580     QUrl url = createUrl(QStringLiteral("event/data"));
1581     QUrlQuery q(url);
1582 
1583     if (!search.isEmpty()) {
1584         q.addQueryItem(QStringLiteral("search"), search);
1585     }
1586 
1587     QString sortModeString;
1588     switch (mode) {
1589     case Newest:
1590         sortModeString = QLatin1String("new");
1591         break;
1592     case Alphabetical:
1593         sortModeString = QLatin1String("alpha");
1594         break;
1595     default:
1596         break;
1597     }
1598     if (!sortModeString.isEmpty()) {
1599         q.addQueryItem(QStringLiteral("sortmode"), sortModeString);
1600     }
1601 
1602     if (!country.isEmpty()) {
1603         q.addQueryItem(QStringLiteral("country"), country);
1604     }
1605 
1606     q.addQueryItem(QStringLiteral("startat"), startAt.toString(Qt::ISODate));
1607 
1608     q.addQueryItem(QStringLiteral("page"), QString::number(page));
1609     q.addQueryItem(QStringLiteral("pagesize"), QString::number(pageSize));
1610     url.setQuery(q);
1611 
1612     ListJob<Event> *job = new ListJob<Event>(d->m_internals, createRequest(url));
1613     return job;
1614 }
1615 
1616 ListJob<Comment> *Provider::requestComments(const Comment::Type commentType, const QString &id, const QString &id2, int page, int pageSize)
1617 {
1618     if (!isValid()) {
1619         return nullptr;
1620     }
1621 
1622     QString commentTypeString;
1623     commentTypeString = Comment::commentTypeToString(commentType);
1624     if (commentTypeString.isEmpty()) {
1625         return nullptr;
1626     }
1627 
1628     QUrl url = createUrl(QLatin1String("comments/data/") + commentTypeString + QLatin1Char('/') + id + QLatin1Char('/') + id2);
1629 
1630     QUrlQuery q(url);
1631     q.addQueryItem(QStringLiteral("page"), QString::number(page));
1632     q.addQueryItem(QStringLiteral("pagesize"), QString::number(pageSize));
1633     url.setQuery(q);
1634 
1635     ListJob<Comment> *job = new ListJob<Comment>(d->m_internals, createRequest(url));
1636     return job;
1637 }
1638 
1639 ItemPostJob<Comment> *Provider::addNewComment(const Comment::Type commentType,
1640                                               const QString &id,
1641                                               const QString &id2,
1642                                               const QString &parentId,
1643                                               const QString &subject,
1644                                               const QString &message)
1645 {
1646     if (!isValid()) {
1647         return nullptr;
1648     }
1649 
1650     QString commentTypeString;
1651     commentTypeString = Comment::commentTypeToString(commentType);
1652     if (commentTypeString.isEmpty()) {
1653         return nullptr;
1654     }
1655 
1656     QMap<QString, QString> postParameters;
1657 
1658     postParameters.insert(QLatin1String("type"), commentTypeString);
1659     postParameters.insert(QLatin1String("content"), id);
1660     postParameters.insert(QLatin1String("content2"), id2);
1661     postParameters.insert(QLatin1String("parent"), parentId);
1662     postParameters.insert(QLatin1String("subject"), subject);
1663     postParameters.insert(QLatin1String("message"), message);
1664 
1665     return new ItemPostJob<Comment>(d->m_internals, createRequest(QLatin1String("comments/add")), postParameters);
1666 }
1667 
1668 PostJob *Provider::voteForComment(const QString &id, uint rating)
1669 {
1670     if (!isValid() || (rating > 100)) {
1671         return nullptr;
1672     }
1673 
1674     QMap<QString, QString> postParameters;
1675     postParameters.insert(QLatin1String("vote"), QString::number(rating));
1676 
1677     QUrl url = createUrl(QLatin1String("comments/vote/") + id);
1678     return new PostJob(d->m_internals, createRequest(url), postParameters);
1679 }
1680 
1681 PostJob *Provider::setPrivateData(const QString &app, const QString &key, const QString &value)
1682 {
1683     if (!isValid()) {
1684         return nullptr;
1685     }
1686 
1687     QUrl url = createUrl(QLatin1String("privatedata/setattribute/") + app + QLatin1Char('/') + key);
1688     PostFileData postRequest(url);
1689 
1690     postRequest.addArgument(QLatin1String("value"), value);
1691 
1692     return new PostJob(d->m_internals, postRequest.request(), postRequest.data());
1693 }
1694 
1695 ItemJob<PrivateData> *Provider::requestPrivateData(const QString &app, const QString &key)
1696 {
1697     if (!isValid()) {
1698         return nullptr;
1699     }
1700 
1701     ItemJob<PrivateData> *job =
1702         new ItemJob<PrivateData>(d->m_internals, createRequest(QLatin1String("privatedata/getattribute/") + app + QLatin1Char('/') + key));
1703     return job;
1704 }
1705 
1706 QUrl Provider::createUrl(const QString &path)
1707 {
1708     QUrl url(d->m_baseUrl.toString() + path);
1709     return url;
1710 }
1711 
1712 QNetworkRequest Provider::createRequest(const QUrl &url)
1713 {
1714     QNetworkRequest request(url);
1715     request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/x-www-form-urlencoded"));
1716 
1717     QString agentHeader;
1718     if (QCoreApplication::instance()) {
1719         agentHeader = QString::fromLocal8Bit("%1/%2").arg(QCoreApplication::instance()->applicationName(), QCoreApplication::instance()->applicationVersion());
1720     } else {
1721         agentHeader = QString::fromLocal8Bit("Attica/%1").arg(QLatin1String(LIBATTICA_VERSION_STRING));
1722     }
1723     if (!d->m_additionalAgentInformation.isEmpty()) {
1724         agentHeader = QString::fromLocal8Bit("%1 (+%2)").arg(agentHeader, d->m_additionalAgentInformation);
1725     }
1726     request.setHeader(QNetworkRequest::UserAgentHeader, agentHeader);
1727 
1728     if (!d->m_credentialsUserName.isEmpty()) {
1729         request.setAttribute((QNetworkRequest::Attribute)BaseJob::UserAttribute, QVariant(d->m_credentialsUserName));
1730         request.setAttribute((QNetworkRequest::Attribute)BaseJob::PasswordAttribute, QVariant(d->m_credentialsPassword));
1731     }
1732     return request;
1733 }
1734 
1735 QNetworkRequest Provider::createRequest(const QString &path)
1736 {
1737     return createRequest(createUrl(path));
1738 }
1739 
1740 ItemJob<Config> *Provider::doRequestConfig(const QUrl &url)
1741 {
1742     return new ItemJob<Config>(d->m_internals, createRequest(url));
1743 }
1744 
1745 ItemJob<Person> *Provider::doRequestPerson(const QUrl &url)
1746 {
1747     return new ItemJob<Person>(d->m_internals, createRequest(url));
1748 }
1749 
1750 ItemJob<AccountBalance> *Provider::doRequestAccountBalance(const QUrl &url)
1751 {
1752     return new ItemJob<AccountBalance>(d->m_internals, createRequest(url));
1753 }
1754 
1755 ListJob<Person> *Provider::doRequestPersonList(const QUrl &url)
1756 {
1757     return new ListJob<Person>(d->m_internals, createRequest(url));
1758 }
1759 
1760 ListJob<Achievement> *Provider::doRequestAchievementList(const QUrl &url)
1761 {
1762     return new ListJob<Achievement>(d->m_internals, createRequest(url));
1763 }
1764 
1765 ListJob<Activity> *Provider::doRequestActivityList(const QUrl &url)
1766 {
1767     return new ListJob<Activity>(d->m_internals, createRequest(url));
1768 }
1769 
1770 ListJob<Folder> *Provider::doRequestFolderList(const QUrl &url)
1771 {
1772     return new ListJob<Folder>(d->m_internals, createRequest(url));
1773 }
1774 
1775 ListJob<Forum> *Provider::doRequestForumList(const QUrl &url)
1776 {
1777     return new ListJob<Forum>(d->m_internals, createRequest(url));
1778 }
1779 
1780 ListJob<Topic> *Provider::doRequestTopicList(const QUrl &url)
1781 {
1782     return new ListJob<Topic>(d->m_internals, createRequest(url));
1783 }
1784 
1785 ListJob<Message> *Provider::doRequestMessageList(const QUrl &url)
1786 {
1787     return new ListJob<Message>(d->m_internals, createRequest(url));
1788 }
1789 
1790 QString Provider::achievementServiceVersion() const
1791 {
1792     return d->m_achievementVersion;
1793 }
1794 
1795 QString Provider::activityServiceVersion() const
1796 {
1797     return d->m_activityVersion;
1798 }
1799 QString Provider::commentServiceVersion() const
1800 {
1801     return d->m_commentVersion;
1802 }
1803 QString Provider::contentServiceVersion() const
1804 {
1805     return d->m_contentVersion;
1806 }
1807 QString Provider::fanServiceVersion() const
1808 {
1809     return d->m_fanVersion;
1810 }
1811 QString Provider::forumServiceVersion() const
1812 {
1813     return d->m_forumVersion;
1814 }
1815 QString Provider::friendServiceVersion() const
1816 {
1817     return d->m_friendVersion;
1818 }
1819 QString Provider::knowledgebaseServiceVersion() const
1820 {
1821     return d->m_knowledgebaseVersion;
1822 }
1823 QString Provider::messageServiceVersion() const
1824 {
1825     return d->m_messageVersion;
1826 }
1827 QString Provider::personServiceVersion() const
1828 {
1829     return d->m_personVersion;
1830 }
1831 
1832 bool Provider::hasAchievementService() const
1833 {
1834     return !d->m_achievementVersion.isEmpty();
1835 }
1836 
1837 bool Provider::hasActivityService() const
1838 {
1839     return !d->m_activityVersion.isEmpty();
1840 }
1841 bool Provider::hasCommentService() const
1842 {
1843     return !d->m_commentVersion.isEmpty();
1844 }
1845 bool Provider::hasContentService() const
1846 {
1847     return !d->m_contentVersion.isEmpty();
1848 }
1849 bool Provider::hasFanService() const
1850 {
1851     return !d->m_fanVersion.isEmpty();
1852 }
1853 bool Provider::hasForumService() const
1854 {
1855     return !d->m_forumVersion.isEmpty();
1856 }
1857 bool Provider::hasFriendService() const
1858 {
1859     return !d->m_friendVersion.isEmpty();
1860 }
1861 bool Provider::hasKnowledgebaseService() const
1862 {
1863     return !d->m_knowledgebaseVersion.isEmpty();
1864 }
1865 bool Provider::hasMessageService() const
1866 {
1867     return !d->m_messageVersion.isEmpty();
1868 }
1869 bool Provider::hasPersonService() const
1870 {
1871     return !d->m_personVersion.isEmpty();
1872 }