File indexing completed on 2024-04-14 03:49:34

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 PostJob *Provider::voteForContent(const QString &contentId, uint rating)
1381 {
1382     if (!isValid()) {
1383         return nullptr;
1384     }
1385 
1386     // according to OCS API, the rating is 0..100
1387     if (rating > 100) {
1388         qWarning() << "Rating cannot be superior to 100, fallback to 100.";
1389         rating = 100;
1390     }
1391 
1392     StringMap postParameters;
1393     postParameters.insert(QLatin1String("vote"), QString::number(rating));
1394     // qCDebug(ATTICA) << "vote: " << QString::number(rating);
1395     return new PostJob(d->m_internals, createRequest(QLatin1String("content/vote/") + contentId), postParameters);
1396 }
1397 
1398 PostJob *Provider::becomeFan(const QString &contentId)
1399 {
1400     if (!isValid()) {
1401         return nullptr;
1402     }
1403 
1404     QUrl url = createUrl(QLatin1String("fan/add/") + contentId);
1405     PostFileData postRequest(url);
1406     postRequest.addArgument(QLatin1String("contentid"), contentId);
1407     return new PostJob(d->m_internals, postRequest.request(), postRequest.data());
1408 }
1409 
1410 ListJob<Person> *Provider::requestFans(const QString &contentId, uint page, uint pageSize)
1411 {
1412     if (!isValid()) {
1413         return nullptr;
1414     }
1415 
1416     QUrl url = createUrl(QLatin1String("fan/data/") + contentId);
1417     QUrlQuery q(url);
1418     q.addQueryItem(QStringLiteral("contentid"), contentId);
1419     q.addQueryItem(QStringLiteral("page"), QString::number(page));
1420     q.addQueryItem(QStringLiteral("pagesize"), QString::number(pageSize));
1421     url.setQuery(q);
1422     ListJob<Person> *job = new ListJob<Person>(d->m_internals, createRequest(url));
1423     return job;
1424 }
1425 
1426 ListJob<Forum> *Provider::requestForums(uint page, uint pageSize)
1427 {
1428     if (!isValid()) {
1429         return nullptr;
1430     }
1431 
1432     QUrl url = createUrl(QStringLiteral("forum/list"));
1433     QUrlQuery q(url);
1434     q.addQueryItem(QStringLiteral("page"), QString::number(page));
1435     q.addQueryItem(QStringLiteral("pagesize"), QString::number(pageSize));
1436     url.setQuery(q);
1437 
1438     return doRequestForumList(url);
1439 }
1440 
1441 ListJob<Topic> *
1442 Provider::requestTopics(const QString &forum, const QString &search, const QString &description, Provider::SortMode mode, int page, int pageSize)
1443 {
1444     if (!isValid()) {
1445         return nullptr;
1446     }
1447 
1448     QUrl url = createUrl(QStringLiteral("forum/topics/list"));
1449     QUrlQuery q(url);
1450     q.addQueryItem(QStringLiteral("forum"), forum);
1451     q.addQueryItem(QStringLiteral("search"), search);
1452     q.addQueryItem(QStringLiteral("description"), description);
1453     QString sortModeString;
1454     switch (mode) {
1455     case Newest:
1456         sortModeString = QLatin1String("new");
1457         break;
1458     case Alphabetical:
1459         sortModeString = QLatin1String("alpha");
1460         break;
1461     default:
1462         break;
1463     }
1464     if (!sortModeString.isEmpty()) {
1465         q.addQueryItem(QStringLiteral("sortmode"), sortModeString);
1466     }
1467     q.addQueryItem(QStringLiteral("page"), QString::number(page));
1468     q.addQueryItem(QStringLiteral("pagesize"), QString::number(pageSize));
1469     url.setQuery(q);
1470 
1471     return doRequestTopicList(url);
1472 }
1473 
1474 PostJob *Provider::postTopic(const QString &forumId, const QString &subject, const QString &content)
1475 {
1476     if (!isValid()) {
1477         return nullptr;
1478     }
1479 
1480     StringMap postParameters;
1481     postParameters.insert(QLatin1String("subject"), subject);
1482     postParameters.insert(QLatin1String("content"), content);
1483     postParameters.insert(QLatin1String("forum"), forumId);
1484     return new PostJob(d->m_internals, createRequest(QLatin1String("forum/topic/add")), postParameters);
1485 }
1486 
1487 ItemJob<DownloadItem> *Provider::downloadLink(const QString &contentId, const QString &itemId)
1488 {
1489     if (!isValid()) {
1490         return nullptr;
1491     }
1492 
1493     QUrl url = createUrl(QLatin1String("content/download/") + contentId + QLatin1Char('/') + itemId);
1494     ItemJob<DownloadItem> *job = new ItemJob<DownloadItem>(d->m_internals, createRequest(url));
1495     return job;
1496 }
1497 
1498 ItemJob<KnowledgeBaseEntry> *Provider::requestKnowledgeBaseEntry(const QString &id)
1499 {
1500     if (!isValid()) {
1501         return nullptr;
1502     }
1503 
1504     QUrl url = createUrl(QLatin1String("knowledgebase/data/") + id);
1505     ItemJob<KnowledgeBaseEntry> *job = new ItemJob<KnowledgeBaseEntry>(d->m_internals, createRequest(url));
1506     return job;
1507 }
1508 
1509 ListJob<KnowledgeBaseEntry> *Provider::searchKnowledgeBase(const Content &content, const QString &search, Provider::SortMode sortMode, int page, int pageSize)
1510 {
1511     if (!isValid()) {
1512         return nullptr;
1513     }
1514 
1515     QUrl url = createUrl(QStringLiteral("knowledgebase/data"));
1516     QUrlQuery q(url);
1517     if (content.isValid()) {
1518         q.addQueryItem(QStringLiteral("content"), content.id());
1519     }
1520 
1521     q.addQueryItem(QStringLiteral("search"), search);
1522     QString sortModeString;
1523     switch (sortMode) {
1524     case Newest:
1525         sortModeString = QLatin1String("new");
1526         break;
1527     case Alphabetical:
1528         sortModeString = QLatin1String("alpha");
1529         break;
1530     case Rating:
1531         sortModeString = QLatin1String("high");
1532         break;
1533     // FIXME: knowledge base doesn't have downloads
1534     case Downloads:
1535         sortModeString = QLatin1String("new");
1536         break;
1537     }
1538     if (!sortModeString.isEmpty()) {
1539         q.addQueryItem(QStringLiteral("sortmode"), sortModeString);
1540     }
1541 
1542     q.addQueryItem(QStringLiteral("page"), QString::number(page));
1543     q.addQueryItem(QStringLiteral("pagesize"), QString::number(pageSize));
1544     url.setQuery(q);
1545 
1546     ListJob<KnowledgeBaseEntry> *job = new ListJob<KnowledgeBaseEntry>(d->m_internals, createRequest(url));
1547     return job;
1548 }
1549 
1550 ItemJob<Event> *Provider::requestEvent(const QString &id)
1551 {
1552     if (!isValid()) {
1553         return nullptr;
1554     }
1555 
1556     ItemJob<Event> *job = new ItemJob<Event>(d->m_internals, createRequest(QLatin1String("event/data/") + id));
1557     return job;
1558 }
1559 
1560 ListJob<Event> *Provider::requestEvent(const QString &country, const QString &search, const QDate &startAt, Provider::SortMode mode, int page, int pageSize)
1561 {
1562     if (!isValid()) {
1563         return nullptr;
1564     }
1565 
1566     QUrl url = createUrl(QStringLiteral("event/data"));
1567     QUrlQuery q(url);
1568 
1569     if (!search.isEmpty()) {
1570         q.addQueryItem(QStringLiteral("search"), search);
1571     }
1572 
1573     QString sortModeString;
1574     switch (mode) {
1575     case Newest:
1576         sortModeString = QLatin1String("new");
1577         break;
1578     case Alphabetical:
1579         sortModeString = QLatin1String("alpha");
1580         break;
1581     default:
1582         break;
1583     }
1584     if (!sortModeString.isEmpty()) {
1585         q.addQueryItem(QStringLiteral("sortmode"), sortModeString);
1586     }
1587 
1588     if (!country.isEmpty()) {
1589         q.addQueryItem(QStringLiteral("country"), country);
1590     }
1591 
1592     q.addQueryItem(QStringLiteral("startat"), startAt.toString(Qt::ISODate));
1593 
1594     q.addQueryItem(QStringLiteral("page"), QString::number(page));
1595     q.addQueryItem(QStringLiteral("pagesize"), QString::number(pageSize));
1596     url.setQuery(q);
1597 
1598     ListJob<Event> *job = new ListJob<Event>(d->m_internals, createRequest(url));
1599     return job;
1600 }
1601 
1602 ListJob<Comment> *Provider::requestComments(const Comment::Type commentType, const QString &id, const QString &id2, int page, int pageSize)
1603 {
1604     if (!isValid()) {
1605         return nullptr;
1606     }
1607 
1608     QString commentTypeString;
1609     commentTypeString = Comment::commentTypeToString(commentType);
1610     if (commentTypeString.isEmpty()) {
1611         return nullptr;
1612     }
1613 
1614     QUrl url = createUrl(QLatin1String("comments/data/") + commentTypeString + QLatin1Char('/') + id + QLatin1Char('/') + id2);
1615 
1616     QUrlQuery q(url);
1617     q.addQueryItem(QStringLiteral("page"), QString::number(page));
1618     q.addQueryItem(QStringLiteral("pagesize"), QString::number(pageSize));
1619     url.setQuery(q);
1620 
1621     ListJob<Comment> *job = new ListJob<Comment>(d->m_internals, createRequest(url));
1622     return job;
1623 }
1624 
1625 ItemPostJob<Comment> *Provider::addNewComment(const Comment::Type commentType,
1626                                               const QString &id,
1627                                               const QString &id2,
1628                                               const QString &parentId,
1629                                               const QString &subject,
1630                                               const QString &message)
1631 {
1632     if (!isValid()) {
1633         return nullptr;
1634     }
1635 
1636     QString commentTypeString;
1637     commentTypeString = Comment::commentTypeToString(commentType);
1638     if (commentTypeString.isEmpty()) {
1639         return nullptr;
1640     }
1641 
1642     QMap<QString, QString> postParameters;
1643 
1644     postParameters.insert(QLatin1String("type"), commentTypeString);
1645     postParameters.insert(QLatin1String("content"), id);
1646     postParameters.insert(QLatin1String("content2"), id2);
1647     postParameters.insert(QLatin1String("parent"), parentId);
1648     postParameters.insert(QLatin1String("subject"), subject);
1649     postParameters.insert(QLatin1String("message"), message);
1650 
1651     return new ItemPostJob<Comment>(d->m_internals, createRequest(QLatin1String("comments/add")), postParameters);
1652 }
1653 
1654 PostJob *Provider::voteForComment(const QString &id, uint rating)
1655 {
1656     if (!isValid() || (rating > 100)) {
1657         return nullptr;
1658     }
1659 
1660     QMap<QString, QString> postParameters;
1661     postParameters.insert(QLatin1String("vote"), QString::number(rating));
1662 
1663     QUrl url = createUrl(QLatin1String("comments/vote/") + id);
1664     return new PostJob(d->m_internals, createRequest(url), postParameters);
1665 }
1666 
1667 PostJob *Provider::setPrivateData(const QString &app, const QString &key, const QString &value)
1668 {
1669     if (!isValid()) {
1670         return nullptr;
1671     }
1672 
1673     QUrl url = createUrl(QLatin1String("privatedata/setattribute/") + app + QLatin1Char('/') + key);
1674     PostFileData postRequest(url);
1675 
1676     postRequest.addArgument(QLatin1String("value"), value);
1677 
1678     return new PostJob(d->m_internals, postRequest.request(), postRequest.data());
1679 }
1680 
1681 ItemJob<PrivateData> *Provider::requestPrivateData(const QString &app, const QString &key)
1682 {
1683     if (!isValid()) {
1684         return nullptr;
1685     }
1686 
1687     ItemJob<PrivateData> *job =
1688         new ItemJob<PrivateData>(d->m_internals, createRequest(QLatin1String("privatedata/getattribute/") + app + QLatin1Char('/') + key));
1689     return job;
1690 }
1691 
1692 QUrl Provider::createUrl(const QString &path)
1693 {
1694     QUrl url(d->m_baseUrl.toString() + path);
1695     return url;
1696 }
1697 
1698 QNetworkRequest Provider::createRequest(const QUrl &url)
1699 {
1700     QNetworkRequest request(url);
1701     request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/x-www-form-urlencoded"));
1702 
1703     QString agentHeader;
1704     if (QCoreApplication::instance()) {
1705         agentHeader = QString::fromLocal8Bit("%1/%2").arg(QCoreApplication::instance()->applicationName(), QCoreApplication::instance()->applicationVersion());
1706     } else {
1707         agentHeader = QString::fromLocal8Bit("Attica/%1").arg(QLatin1String(LIBATTICA_VERSION_STRING));
1708     }
1709     if (!d->m_additionalAgentInformation.isEmpty()) {
1710         agentHeader = QString::fromLocal8Bit("%1 (+%2)").arg(agentHeader, d->m_additionalAgentInformation);
1711     }
1712     request.setHeader(QNetworkRequest::UserAgentHeader, agentHeader);
1713 
1714     if (!d->m_credentialsUserName.isEmpty()) {
1715         request.setAttribute((QNetworkRequest::Attribute)BaseJob::UserAttribute, QVariant(d->m_credentialsUserName));
1716         request.setAttribute((QNetworkRequest::Attribute)BaseJob::PasswordAttribute, QVariant(d->m_credentialsPassword));
1717     }
1718     return request;
1719 }
1720 
1721 QNetworkRequest Provider::createRequest(const QString &path)
1722 {
1723     return createRequest(createUrl(path));
1724 }
1725 
1726 ItemJob<Config> *Provider::doRequestConfig(const QUrl &url)
1727 {
1728     return new ItemJob<Config>(d->m_internals, createRequest(url));
1729 }
1730 
1731 ItemJob<Person> *Provider::doRequestPerson(const QUrl &url)
1732 {
1733     return new ItemJob<Person>(d->m_internals, createRequest(url));
1734 }
1735 
1736 ItemJob<AccountBalance> *Provider::doRequestAccountBalance(const QUrl &url)
1737 {
1738     return new ItemJob<AccountBalance>(d->m_internals, createRequest(url));
1739 }
1740 
1741 ListJob<Person> *Provider::doRequestPersonList(const QUrl &url)
1742 {
1743     return new ListJob<Person>(d->m_internals, createRequest(url));
1744 }
1745 
1746 ListJob<Achievement> *Provider::doRequestAchievementList(const QUrl &url)
1747 {
1748     return new ListJob<Achievement>(d->m_internals, createRequest(url));
1749 }
1750 
1751 ListJob<Activity> *Provider::doRequestActivityList(const QUrl &url)
1752 {
1753     return new ListJob<Activity>(d->m_internals, createRequest(url));
1754 }
1755 
1756 ListJob<Folder> *Provider::doRequestFolderList(const QUrl &url)
1757 {
1758     return new ListJob<Folder>(d->m_internals, createRequest(url));
1759 }
1760 
1761 ListJob<Forum> *Provider::doRequestForumList(const QUrl &url)
1762 {
1763     return new ListJob<Forum>(d->m_internals, createRequest(url));
1764 }
1765 
1766 ListJob<Topic> *Provider::doRequestTopicList(const QUrl &url)
1767 {
1768     return new ListJob<Topic>(d->m_internals, createRequest(url));
1769 }
1770 
1771 ListJob<Message> *Provider::doRequestMessageList(const QUrl &url)
1772 {
1773     return new ListJob<Message>(d->m_internals, createRequest(url));
1774 }
1775 
1776 QString Provider::achievementServiceVersion() const
1777 {
1778     return d->m_achievementVersion;
1779 }
1780 
1781 QString Provider::activityServiceVersion() const
1782 {
1783     return d->m_activityVersion;
1784 }
1785 QString Provider::commentServiceVersion() const
1786 {
1787     return d->m_commentVersion;
1788 }
1789 QString Provider::contentServiceVersion() const
1790 {
1791     return d->m_contentVersion;
1792 }
1793 QString Provider::fanServiceVersion() const
1794 {
1795     return d->m_fanVersion;
1796 }
1797 QString Provider::forumServiceVersion() const
1798 {
1799     return d->m_forumVersion;
1800 }
1801 QString Provider::friendServiceVersion() const
1802 {
1803     return d->m_friendVersion;
1804 }
1805 QString Provider::knowledgebaseServiceVersion() const
1806 {
1807     return d->m_knowledgebaseVersion;
1808 }
1809 QString Provider::messageServiceVersion() const
1810 {
1811     return d->m_messageVersion;
1812 }
1813 QString Provider::personServiceVersion() const
1814 {
1815     return d->m_personVersion;
1816 }
1817 
1818 bool Provider::hasAchievementService() const
1819 {
1820     return !d->m_achievementVersion.isEmpty();
1821 }
1822 
1823 bool Provider::hasActivityService() const
1824 {
1825     return !d->m_activityVersion.isEmpty();
1826 }
1827 bool Provider::hasCommentService() const
1828 {
1829     return !d->m_commentVersion.isEmpty();
1830 }
1831 bool Provider::hasContentService() const
1832 {
1833     return !d->m_contentVersion.isEmpty();
1834 }
1835 bool Provider::hasFanService() const
1836 {
1837     return !d->m_fanVersion.isEmpty();
1838 }
1839 bool Provider::hasForumService() const
1840 {
1841     return !d->m_forumVersion.isEmpty();
1842 }
1843 bool Provider::hasFriendService() const
1844 {
1845     return !d->m_friendVersion.isEmpty();
1846 }
1847 bool Provider::hasKnowledgebaseService() const
1848 {
1849     return !d->m_knowledgebaseVersion.isEmpty();
1850 }
1851 bool Provider::hasMessageService() const
1852 {
1853     return !d->m_messageVersion.isEmpty();
1854 }
1855 bool Provider::hasPersonService() const
1856 {
1857     return !d->m_personVersion.isEmpty();
1858 }