File indexing completed on 2024-04-28 04:55:37

0001 /*
0002     This file is part of Choqok, the KDE micro-blogging client
0003 
0004     SPDX-FileCopyrightText: 2008-2012 Mehrdad Momeny <mehrdad.momeny@gmail.com>
0005 
0006     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007 */
0008 
0009 #include "twitterapimicroblog.h"
0010 
0011 #include <QAction>
0012 #include <QJsonDocument>
0013 #include <QMenu>
0014 #include <QStandardPaths>
0015 
0016 #include <KIO/Job>
0017 #include <KLocalizedString>
0018 #include <KSharedConfig>
0019 
0020 #include "account.h"
0021 #include "accountmanager.h"
0022 #include "application.h"
0023 #include "choqokappearancesettings.h"
0024 #include "choqokbehaviorsettings.h"
0025 #include "choqokuiglobal.h"
0026 #include "editaccountwidget.h"
0027 #include "microblogwidget.h"
0028 #include "notifymanager.h"
0029 #include "postwidget.h"
0030 #include "timelinewidget.h"
0031 
0032 #include "twitterapiaccount.h"
0033 #include "twitterapicomposerwidget.h"
0034 #include "twitterapidebug.h"
0035 #include "twitterapidmessagedialog.h"
0036 #include "twitterapipostwidget.h"
0037 #include "twitterapisearch.h"
0038 #include "twitterapisearchdialog.h"
0039 #include "twitterapisearchtimelinewidget.h"
0040 
0041 class TwitterApiMicroBlog::Private
0042 {
0043 public:
0044     Private(): countOfTimelinesToSave(0), friendsCursor(QLatin1String("-1"))
0045     {
0046         monthes[QLatin1String("Jan")] = 1;
0047         monthes[QLatin1String("Feb")] = 2;
0048         monthes[QLatin1String("Mar")] = 3;
0049         monthes[QLatin1String("Apr")] = 4;
0050         monthes[QLatin1String("May")] = 5;
0051         monthes[QLatin1String("Jun")] = 6;
0052         monthes[QLatin1String("Jul")] = 7;
0053         monthes[QLatin1String("Aug")] = 8;
0054         monthes[QLatin1String("Sep")] = 9;
0055         monthes[QLatin1String("Oct")] = 10;
0056         monthes[QLatin1String("Nov")] = 11;
0057         monthes[QLatin1String("Dec")] = 12;
0058     }
0059     int countOfTimelinesToSave;
0060     QString friendsCursor;
0061     QString followersCursor;
0062     QMap<QString, int> monthes;
0063 };
0064 
0065 TwitterApiMicroBlog::TwitterApiMicroBlog(const QString &componentName, QObject *parent)
0066     : MicroBlog(componentName, parent), d(new Private)
0067 {
0068     qCDebug(CHOQOK);
0069 
0070     QStringList timelineTypes;
0071     timelineTypes << QLatin1String("Home") << QLatin1String("Reply") << QLatin1String("Inbox") << QLatin1String("Outbox") << QLatin1String("Favorite") << QLatin1String("ReTweets") << QLatin1String("Public");
0072     setTimelineNames(timelineTypes);
0073     timelineApiPath[QLatin1String("Home")] = QLatin1String("/statuses/home_timeline.json");
0074     timelineApiPath[QLatin1String("Reply")] = QLatin1String("/statuses/replies.json");
0075     timelineApiPath[QLatin1String("Inbox")] = QLatin1String("/direct_messages.json");
0076     timelineApiPath[QLatin1String("Outbox")] = QLatin1String("/direct_messages/sent.json");
0077     timelineApiPath[QLatin1String("Favorite")] = QLatin1String("/favorites/list.json");
0078     timelineApiPath[QLatin1String("ReTweets")] = QLatin1String("/statuses/retweets_of_me.json");
0079     timelineApiPath[QLatin1String("Public")] = QLatin1String("/statuses/public_timeline.json");
0080     setTimelineInfos();
0081 }
0082 
0083 void TwitterApiMicroBlog::setTimelineInfos()
0084 {
0085     Choqok::TimelineInfo *t = new Choqok::TimelineInfo;
0086     t->name = i18nc("Timeline Name", "Home");
0087     t->description = i18nc("Timeline description", "You and your friends");
0088     t->icon = QLatin1String("user-home");
0089     mTimelineInfos[QLatin1String("Home")] = std::move(t);
0090 
0091     t = new Choqok::TimelineInfo;
0092     t->name = i18nc("Timeline Name", "Reply");
0093     t->description = i18nc("Timeline description", "Replies to you");
0094     t->icon = QLatin1String("edit-undo");
0095     mTimelineInfos[QLatin1String("Reply")] = std::move(t);
0096 
0097     t = new Choqok::TimelineInfo;
0098     t->name = i18nc("Timeline Name", "Inbox");
0099     t->description = i18nc("Timeline description", "Your incoming private messages");
0100     t->icon = QLatin1String("mail-folder-inbox");
0101     mTimelineInfos[QLatin1String("Inbox")] = std::move(t);
0102 
0103     t = new Choqok::TimelineInfo;
0104     t->name = i18nc("Timeline Name", "Outbox");
0105     t->description = i18nc("Timeline description", "Private messages you have sent");
0106     t->icon = QLatin1String("mail-folder-outbox");
0107     mTimelineInfos[QLatin1String("Outbox")] = std::move(t);
0108 
0109     t = new Choqok::TimelineInfo;
0110     t->name = i18nc("Timeline Name", "Favorite");
0111     t->description = i18nc("Timeline description", "Your favorites");
0112     t->icon = QLatin1String("favorites");
0113     mTimelineInfos[QLatin1String("Favorite")] = std::move(t);
0114 
0115     t = new Choqok::TimelineInfo;
0116     t->name = i18nc("Timeline Name", "Public");
0117     t->description = i18nc("Timeline description", "Public timeline");
0118     t->icon = QLatin1String("folder-green");
0119     mTimelineInfos[QLatin1String("Public")] = std::move(t);
0120 
0121     t = new Choqok::TimelineInfo;
0122     t->name = i18nc("Timeline Name", "ReTweets");
0123     t->description = i18nc("Timeline description", "Your posts that were ReTweeted by others");
0124     t->icon = QLatin1String("folder-red");
0125     mTimelineInfos[QLatin1String("ReTweets")] = std::move(t);
0126 }
0127 
0128 TwitterApiMicroBlog::~TwitterApiMicroBlog()
0129 {
0130     qDeleteAll(mTimelineInfos);
0131     delete d;
0132 }
0133 
0134 QMenu *TwitterApiMicroBlog::createActionsMenu(Choqok::Account *theAccount, QWidget *parent)
0135 {
0136     QMenu *menu = MicroBlog::createActionsMenu(theAccount, parent);
0137 
0138     QAction *directMessge = new QAction(QIcon::fromTheme(QLatin1String("mail-message-new")), i18n("Send Private Message..."), menu);
0139     directMessge->setData(theAccount->alias());
0140     connect(directMessge, SIGNAL(triggered(bool)), SLOT(showDirectMessageDialog()));
0141     menu->addAction(directMessge);
0142 
0143     QAction *search = new QAction(QIcon::fromTheme(QLatin1String("edit-find")), i18n("Search..."), menu);
0144     search->setData(theAccount->alias());
0145     connect(search, SIGNAL(triggered(bool)), SLOT(showSearchDialog()));
0146     menu->addAction(search);
0147 
0148     QAction *updateFriendsList = new QAction(QIcon::fromTheme(QLatin1String("arrow-down")), i18n("Update Friends List"), menu);
0149     updateFriendsList->setData(theAccount->alias());
0150     connect(updateFriendsList, &QAction::triggered, this, &TwitterApiMicroBlog::slotUpdateFriendsList);
0151     menu->addAction(updateFriendsList);
0152 
0153     return menu;
0154 }
0155 
0156 QList< Choqok::Post * > TwitterApiMicroBlog::loadTimeline(Choqok::Account *account,
0157         const QString &timelineName)
0158 {
0159     QList< Choqok::Post * > list;
0160     if (timelineName.compare(QLatin1String("Favorite")) == 0) {
0161         return list;    //NOTE Won't cache favorites, and this is for compatibility with older versions!
0162     }
0163     qCDebug(CHOQOK) << timelineName;
0164     QString fileName = Choqok::AccountManager::generatePostBackupFileName(account->alias(), timelineName);
0165     KConfig postsBackup(fileName, KConfig::NoGlobals, QStandardPaths::DataLocation);
0166     QStringList tmpList = postsBackup.groupList();
0167 
0168 /// to don't load old archives
0169     if (tmpList.isEmpty() || !(QDateTime::fromString(tmpList.first()).isValid())) {
0170         return list;
0171     }
0172 ///--------------
0173 
0174     QList<QDateTime> groupList;
0175     for (const QString &str: tmpList) {
0176         groupList.append(QDateTime::fromString(str));
0177     }
0178     std::sort(groupList.begin(), groupList.end());
0179     int count = groupList.count();
0180     if (count) {
0181         Choqok::Post *st = nullptr;
0182         for (int i = 0; i < count; ++i) {
0183             st = new Choqok::Post;
0184             KConfigGroup grp(&postsBackup, groupList[i].toString());
0185             st->creationDateTime = grp.readEntry("creationDateTime", QDateTime::currentDateTime());
0186             st->postId = grp.readEntry("postId", QString());
0187             st->content = grp.readEntry("text", QString());
0188             st->source = grp.readEntry("source", QString());
0189             st->replyToPostId = grp.readEntry("inReplyToPostId", QString());
0190             st->replyToUser.userId = grp.readEntry("inReplyToUserId", QString());
0191             st->isFavorited = grp.readEntry("favorited", false);
0192             st->replyToUser.userName = grp.readEntry("inReplyToUserName", QString());
0193             st->author.userId = grp.readEntry("authorId", QString());
0194             st->author.userName = grp.readEntry("authorUserName", QString());
0195             st->author.realName = grp.readEntry("authorRealName", QString());
0196             st->author.homePageUrl = grp.readEntry("authorHomePageUrl", QUrl());
0197             st->author.profileImageUrl = grp.readEntry("authorProfileImageUrl", QUrl());
0198             st->author.description = grp.readEntry("authorDescription" , QString());
0199             st->author.isProtected = grp.readEntry("isProtected", false);
0200             st->isPrivate = grp.readEntry("isPrivate" , false);
0201             st->author.location = grp.readEntry("authorLocation", QString());
0202             st->link = postUrl(account, st->author.userName, st->postId);
0203             st->isRead = grp.readEntry("isRead", true);
0204             st->repeatedFromUser.userName = grp.readEntry("repeatedFrom", QString());
0205             st->repeatedFromUser.homePageUrl = grp.readEntry("repeatedFromUserHomePage", QUrl());
0206             st->repeatedPostId = grp.readEntry("repeatedPostId", QString());
0207             st->repeatedDateTime = grp.readEntry("repeatedDateTime", QDateTime());
0208             st->conversationId = grp.readEntry("conversationId", QString());
0209             st->media = grp.readEntry("mediaUrl", QUrl());
0210             st->quotedPost.postId = grp.readEntry("quotedPostId", QString());
0211             st->quotedPost.user.profileImageUrl = grp.readEntry("quotedProfileUrl", QUrl());
0212             st->quotedPost.content = grp.readEntry("quotedContent", QString());
0213             st->quotedPost.user.userName = grp.readEntry("quotedUsername", QString());
0214 
0215             list.append(st);
0216         }
0217         mTimelineLatestId[account][timelineName] = st->postId;
0218     }
0219     return list;
0220 }
0221 
0222 void TwitterApiMicroBlog::saveTimeline(Choqok::Account *account,
0223                                        const QString &timelineName,
0224                                        const QList< Choqok::UI::PostWidget * > &timeline)
0225 {
0226     if (timelineName.compare(QLatin1String("Favorite")) != 0) {
0227         qCDebug(CHOQOK);
0228         QString fileName = Choqok::AccountManager::generatePostBackupFileName(account->alias(), timelineName);
0229         KConfig postsBackup(fileName, KConfig::NoGlobals, QStandardPaths::DataLocation);
0230 
0231         ///Clear previous data:
0232         for (const QString &group: postsBackup.groupList()) {
0233             postsBackup.deleteGroup(group);
0234         }
0235 
0236         for (Choqok::UI::PostWidget *wd: timeline) {
0237             const Choqok::Post *post = (wd->currentPost());
0238             KConfigGroup grp(&postsBackup, post->creationDateTime.toString());
0239             grp.writeEntry("creationDateTime", post->creationDateTime);
0240             grp.writeEntry("postId", post->postId);
0241             grp.writeEntry("text", post->content);
0242             grp.writeEntry("source", post->source);
0243             grp.writeEntry("inReplyToPostId", post->replyToPostId);
0244             grp.writeEntry("inReplyToUserId", post->replyToUser.userId);
0245             grp.writeEntry("favorited", post->isFavorited);
0246             grp.writeEntry("inReplyToUserName", post->replyToUser.userName);
0247             grp.writeEntry("authorId", post->author.userId);
0248             grp.writeEntry("authorUserName", post->author.userName);
0249             grp.writeEntry("authorRealName", post->author.realName);
0250             grp.writeEntry("authorHomePageUrl", post->author.homePageUrl);
0251             grp.writeEntry("authorProfileImageUrl", post->author.profileImageUrl);
0252             grp.writeEntry("authorDescription" , post->author.description);
0253             grp.writeEntry("isPrivate" , post->isPrivate);
0254             grp.writeEntry("authorLocation" , post->author.location);
0255             grp.writeEntry("isProtected" , post->author.isProtected);
0256             grp.writeEntry("isRead" , post->isRead);
0257             grp.writeEntry("repeatedFrom", post->repeatedFromUser.userName);
0258             grp.writeEntry("repeatedFromUserHomePage", post->repeatedFromUser.homePageUrl);
0259             grp.writeEntry("repeatedPostId", post->repeatedPostId);
0260             grp.writeEntry("repeatedDateTime", post->repeatedDateTime);
0261             grp.writeEntry("conversationId", post->conversationId);
0262             grp.writeEntry("mediaUrl", post->media);
0263             grp.writeEntry("quotedPostId", post->quotedPost.postId);
0264             grp.writeEntry("quotedProfileUrl", post->quotedPost.user.profileImageUrl);
0265             grp.writeEntry("quotedContent", post->quotedPost.content);
0266             grp.writeEntry("quotedUsername", post->quotedPost.user.userName);
0267         }
0268         postsBackup.sync();
0269     }
0270     if (Choqok::Application::isShuttingDown()) {
0271         --d->countOfTimelinesToSave;
0272         if (d->countOfTimelinesToSave < 1) {
0273             Q_EMIT readyForUnload();
0274         }
0275     }
0276 }
0277 
0278 Choqok::UI::ComposerWidget *TwitterApiMicroBlog::createComposerWidget(Choqok::Account *account, QWidget *parent)
0279 {
0280     return new TwitterApiComposerWidget(account, parent);
0281 }
0282 
0283 TwitterApiSearchTimelineWidget *TwitterApiMicroBlog::createSearchTimelineWidget(Choqok::Account *theAccount,
0284         QString name,
0285         const SearchInfo &info,
0286         QWidget *parent)
0287 {
0288     return new TwitterApiSearchTimelineWidget(theAccount, name, info, parent);
0289 }
0290 
0291 void TwitterApiMicroBlog::createPost(Choqok::Account *theAccount, Choqok::Post *post)
0292 {
0293     qCDebug(CHOQOK);
0294     TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount);
0295     QByteArray data;
0296     QVariantMap params;
0297     if (!post || post->content.isEmpty()) {
0298         qCDebug(CHOQOK) << "ERROR: Status text is empty!";
0299         Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::OtherError,
0300                          i18n("Creating the new post failed. Text is empty."), MicroBlog::Critical);
0301         return;
0302     }
0303     if (!post->isPrivate) {  ///Status Update
0304         QUrl url = account->apiUrl();
0305         url.setPath(url.path() + QLatin1String("/statuses/update.json"));
0306         params.insert(QLatin1String("status"), post->content);
0307         if (!post->replyToPostId.isEmpty()) {
0308             params.insert(QLatin1String("in_reply_to_status_id"), post->replyToPostId.toLatin1());
0309         }
0310         data = "status=";
0311         data += QUrl::toPercentEncoding(post->content);
0312         if (!post->replyToPostId.isEmpty()) {
0313             data += "&in_reply_to_status_id=";
0314             data += post->replyToPostId.toLatin1();
0315         }
0316         if (!account->usingOAuth()) {
0317             data += "&source=Choqok";
0318         }
0319         KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo) ;
0320         if (!job) {
0321             qCDebug(CHOQOK) << "Cannot create an http POST request!";
0322             return;
0323         }
0324         job->addMetaData(QStringLiteral("content-type"),
0325                          QStringLiteral("Content-Type: application/x-www-form-urlencoded"));
0326         job->addMetaData(QStringLiteral("customHTTPHeader"),
0327                          QStringLiteral("Authorization: ") +
0328                          QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation, params)));
0329         mCreatePostMap[ job ] = post;
0330         mJobsAccount[job] = theAccount;
0331         connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotCreatePost);
0332         job->start();
0333     } else {///Direct message
0334         QString recipientScreenName = post->replyToUser.userName;
0335         QUrl url = account->apiUrl();
0336         url.setPath(url.path() + QLatin1String("/direct_messages/new.json"));
0337         params.insert(QLatin1String("user"), recipientScreenName.toLatin1());
0338         params.insert(QLatin1String("text"), post->content);
0339         data = "user=";
0340         data += recipientScreenName.toLatin1();
0341         data += "&text=";
0342         data += QUrl::toPercentEncoding(post->content);
0343         if (!account->usingOAuth()) {
0344             data += "&source=Choqok";
0345         }
0346         KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo) ;
0347         if (!job) {
0348             qCDebug(CHOQOK) << "Cannot create an http POST request!";
0349 //             QString errMsg = i18n ( "Creating the new post failed. Cannot create an http POST request. Please check your KDE installation." );
0350 //             emit errorPost ( theAccount, post, Choqok::MicroBlog::OtherError, errMsg, MicroBlog::Critical );
0351             return;
0352         }
0353         job->addMetaData(QStringLiteral("content-type"),
0354                          QStringLiteral("Content-Type: application/x-www-form-urlencoded"));
0355         job->addMetaData(QStringLiteral("customHTTPHeader"),
0356                          QStringLiteral("Authorization: ")
0357                          + QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation, params)));
0358         mCreatePostMap[ job ] = post;
0359         mJobsAccount[job] = theAccount;
0360         connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotCreatePost);
0361         job->start();
0362     }
0363 }
0364 
0365 void TwitterApiMicroBlog::repeatPost(Choqok::Account *theAccount, const QString &postId)
0366 {
0367     qCDebug(CHOQOK);
0368     if (postId.isEmpty()) {
0369         qCCritical(CHOQOK) << "ERROR: PostId is empty!";
0370         return;
0371     }
0372     TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount);
0373     QUrl url = account->apiUrl();
0374     url.setPath(url.path() + QStringLiteral("/statuses/retweet/%1.json").arg(postId));
0375     QByteArray data;
0376     KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo) ;
0377     if (!job) {
0378         qCDebug(CHOQOK) << "Cannot create an http POST request!";
0379         return;
0380     }
0381     job->addMetaData(QStringLiteral("content-type"),
0382                      QStringLiteral("Content-Type: application/x-www-form-urlencoded"));
0383     job->addMetaData(QStringLiteral("customHTTPHeader"),
0384                      QStringLiteral("Authorization: ") +
0385                      QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation)));
0386     Choqok::Post *post = new Choqok::Post;
0387     post->postId = postId;
0388     mCreatePostMap[ job ] = post;
0389     mJobsAccount[job] = theAccount;
0390     connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotCreatePost);
0391     job->start();
0392 }
0393 
0394 void TwitterApiMicroBlog::slotCreatePost(KJob *job)
0395 {
0396     qCDebug(CHOQOK);
0397     if (!job) {
0398         qCDebug(CHOQOK) << "Job is null pointer";
0399         return;
0400     }
0401     Choqok::Post *post = mCreatePostMap.take(job);
0402     Choqok::Account *theAccount = mJobsAccount.take(job);
0403     if (!post || !theAccount) {
0404         qCDebug(CHOQOK) << "Account or Post is NULL pointer";
0405         return;
0406     }
0407     if (job->error()) {
0408         qCDebug(CHOQOK) << "Job Error:" << job->errorString();
0409         Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::CommunicationError,
0410                          i18n("Creating the new post failed: %1", job->errorString()), MicroBlog::Critical);
0411     } else {
0412         KIO::StoredTransferJob *stj = qobject_cast< KIO::StoredTransferJob * > (job);
0413         if (!post->isPrivate) {
0414             readPost(theAccount, stj->data(), post);
0415             if (post->isError) {
0416                 QString errorMsg;
0417                 errorMsg = checkForError(stj->data());
0418                 if (errorMsg.isEmpty()) {    // We get the error message by parsing the JSON output, if there was a parsing error, then we don't have an error message, while there were still an error because of the error flag
0419                     qCCritical(CHOQOK) << "Creating post: JSON parsing error:" << stj->data() ;
0420                     Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::ParsingError,
0421                                      i18n("Creating the new post failed. The result data could not be parsed."), MicroBlog::Critical);
0422                 } else {
0423                     qCCritical(CHOQOK) << "Server Error:" << errorMsg ;
0424                     Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::ServerError,
0425                                      i18n("Creating the new post failed, with error: %1", errorMsg),
0426                                      MicroBlog::Critical);
0427                 }
0428             } else {
0429                 Choqok::NotifyManager::success(i18n("New post for account %1 submitted successfully", theAccount->alias()));
0430                 Q_EMIT postCreated(theAccount, post);
0431             }
0432         } else {
0433             Choqok::NotifyManager::success(i18n("Private message sent successfully"));
0434             Q_EMIT postCreated(theAccount, post);
0435         }
0436     }
0437 }
0438 
0439 void TwitterApiMicroBlog::abortAllJobs(Choqok::Account *theAccount)
0440 {
0441     for (KJob *job: mJobsAccount.keys(theAccount)) {
0442         job->kill(KJob::EmitResult);
0443     }
0444 }
0445 
0446 void TwitterApiMicroBlog::abortCreatePost(Choqok::Account *theAccount, Choqok::Post *post)
0447 {
0448     if (mCreatePostMap.isEmpty()) {
0449         return;
0450     }
0451     if (post) {
0452         mCreatePostMap.key(post)->kill(KJob::EmitResult);
0453     } else {
0454         for (KJob *job: mCreatePostMap.keys()) {
0455             if (mJobsAccount[job] == theAccount) {
0456                 job->kill(KJob::EmitResult);
0457             }
0458         }
0459     }
0460 }
0461 
0462 void TwitterApiMicroBlog::fetchPost(Choqok::Account *theAccount, Choqok::Post *post)
0463 {
0464     qCDebug(CHOQOK);
0465     if (!post || post->postId.isEmpty()) {
0466         return;
0467     }
0468     TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount);
0469     QUrl url = account->apiUrl();
0470     url.setPath(url.path() + QStringLiteral("/statuses/show/%1.json").arg(post->postId));
0471 
0472     KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo) ;
0473     if (!job) {
0474         qCDebug(CHOQOK) << "Cannot create an http GET request!";
0475 //         QString errMsg = i18n ( "Fetching the new post failed. Cannot create an HTTP GET request."
0476 //                                 "Please check your KDE installation." );
0477 //         emit errorPost ( theAccount, post, Choqok::MicroBlog::OtherError, errMsg, Low );
0478         return;
0479     }
0480     job->addMetaData(QStringLiteral("customHTTPHeader"),
0481                      QStringLiteral("Authorization: ") +
0482                      QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::GetOperation)));
0483     mFetchPostMap[ job ] = post;
0484     mJobsAccount[ job ] = theAccount;
0485     connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotFetchPost);
0486     job->start();
0487 }
0488 
0489 void TwitterApiMicroBlog::slotFetchPost(KJob *job)
0490 {
0491     qCDebug(CHOQOK);
0492     if (!job) {
0493         qCWarning(CHOQOK) << "NULL Job returned";
0494         return;
0495     }
0496     Choqok::Post *post = mFetchPostMap.take(job);
0497     Choqok::Account *theAccount = mJobsAccount.take(job);
0498     if (job->error()) {
0499         qCDebug(CHOQOK) << "Job Error:" << job->errorString();
0500         Q_EMIT error(theAccount, Choqok::MicroBlog::CommunicationError,
0501                      i18n("Fetching the new post failed. %1", job->errorString()), Low);
0502     } else {
0503         KIO::StoredTransferJob *stj = qobject_cast<KIO::StoredTransferJob *> (job);
0504         readPost(theAccount, stj->data(), post);
0505         if (post->isError) {
0506             QString errorMsg;
0507             errorMsg = checkForError(stj->data());
0508             if (errorMsg.isEmpty()) {
0509                 qCDebug(CHOQOK) << "Parsing Error";
0510                 Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::ParsingError,
0511                                  i18n("Fetching new post failed. The result data could not be parsed."),
0512                                  Low);
0513             } else {
0514                 qCCritical(CHOQOK) << "Fetching post: Server Error:" << errorMsg;
0515                 Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::ServerError,
0516                                  i18n("Fetching new post failed, with error:%1", errorMsg),
0517                                  Low);
0518             }
0519         } else {
0520             post->isError = true;
0521             Q_EMIT postFetched(theAccount, post);
0522         }
0523     }
0524 }
0525 
0526 void TwitterApiMicroBlog::removePost(Choqok::Account *theAccount, Choqok::Post *post)
0527 {
0528     qCDebug(CHOQOK);
0529     if (!post->postId.isEmpty()) {
0530         TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount);
0531         QUrl url = account->apiUrl();
0532         if (!post->isPrivate) {
0533             url.setPath(url.path() + QStringLiteral("/statuses/destroy/%1.json").arg(post->postId));
0534         } else {
0535             url.setPath(url.path() + QStringLiteral("/direct_messages/destroy/%1.json").arg(post->postId));
0536         }
0537         KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ;
0538         if (!job) {
0539             qCDebug(CHOQOK) << "Cannot create an http POST request!";
0540 //             QString errMsg = i18n ( "Removing the post failed. Cannot create an HTTP POST request. Please check your KDE installation." );
0541 //             emit errorPost ( theAccount, post, Choqok::MicroBlog::OtherError, errMsg, MicroBlog::Critical );
0542             return;
0543         }
0544         job->addMetaData(QStringLiteral("customHTTPHeader"),
0545                          QStringLiteral("Authorization: ") +
0546                          QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation)));
0547         mRemovePostMap[job] = post;
0548         mJobsAccount[job] = theAccount;
0549         connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotRemovePost);
0550         job->start();
0551     }
0552 }
0553 
0554 void TwitterApiMicroBlog::slotRemovePost(KJob *job)
0555 {
0556     qCDebug(CHOQOK);
0557     if (!job) {
0558         qCDebug(CHOQOK) << "Job is null pointer.";
0559         return;
0560     }
0561     Choqok::Post *post = mRemovePostMap.take(job);
0562     Choqok::Account *theAccount = mJobsAccount.take(job);
0563     if (job->error()) {
0564         qCDebug(CHOQOK) << "Job Error:" << job->errorString();
0565         Q_EMIT errorPost(theAccount, post, CommunicationError,
0566                          i18n("Removing the post failed. %1", job->errorString()), MicroBlog::Critical);
0567     } else {
0568         KIO::StoredTransferJob *stj = qobject_cast<KIO::StoredTransferJob *>(job);
0569         QString errMsg = checkForError(stj->data());
0570         if (errMsg.isEmpty()) {
0571             Q_EMIT postRemoved(theAccount, post);
0572         } else {
0573             qCCritical(CHOQOK) << "Server error on removing post:" << errMsg;
0574             Q_EMIT errorPost(theAccount, post, ServerError,
0575                              i18n("Removing the post failed. %1", errMsg), MicroBlog::Critical);
0576         }
0577     }
0578 }
0579 
0580 void TwitterApiMicroBlog::createFavorite(Choqok::Account *theAccount, const QString &postId)
0581 {
0582     qCDebug(CHOQOK);
0583     TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount);
0584     QUrl url = account->apiUrl();
0585     url.setPath(url.path() + QLatin1String("/favorites/create.json"));
0586 
0587     QUrlQuery urlQuery;
0588     urlQuery.addQueryItem(QLatin1String("id"), postId);
0589     url.setQuery(urlQuery);
0590 
0591     KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ;
0592     if (!job) {
0593         qCDebug(CHOQOK) << "Cannot create an http POST request!";
0594 //         QString errMsg = i18n ( "The Favorite creation failed. Cannot create an http POST request. "
0595 //                                 "Please check your KDE installation." );
0596 //         emit error ( theAccount, OtherError, errMsg );
0597         return;
0598     }
0599     job->addMetaData(QStringLiteral("customHTTPHeader"),
0600                      QStringLiteral("Authorization: ") +
0601                      QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation)));
0602     mFavoriteMap[job] = postId;
0603     mJobsAccount[job] = theAccount;
0604     connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotCreateFavorite);
0605     job->start();
0606 }
0607 
0608 void TwitterApiMicroBlog::slotCreateFavorite(KJob *job)
0609 {
0610     qCDebug(CHOQOK);
0611     if (!job) {
0612         qCDebug(CHOQOK) << "Job is null pointer.";
0613         return;
0614     }
0615     Choqok::Account *theAccount = mJobsAccount.take(job);
0616     QString postId = mFavoriteMap.take(job);
0617     if (job->error()) {
0618         qCDebug(CHOQOK) << "Job Error:" << job->errorString();
0619         Q_EMIT error(theAccount, CommunicationError, i18n("Favorite creation failed. %1", job->errorString()));
0620     } else {
0621         KIO::StoredTransferJob *stJob = qobject_cast<KIO::StoredTransferJob *>(job);
0622         QString err = checkForError(stJob->data());
0623         if (!err.isEmpty()) {
0624             Q_EMIT error(theAccount, ServerError, err, Critical);
0625             return;
0626         } else {
0627             Q_EMIT favoriteCreated(theAccount, postId);
0628         }
0629     }
0630 }
0631 
0632 void TwitterApiMicroBlog::removeFavorite(Choqok::Account *theAccount, const QString &postId)
0633 {
0634     qCDebug(CHOQOK);
0635     TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount);
0636     QUrl url = account->apiUrl();
0637     url.setPath(url.path() + QLatin1String("/favorites/destroy.json"));
0638 
0639     QUrlQuery urlQuery;
0640     urlQuery.addQueryItem(QLatin1String("id"), postId);
0641     url.setQuery(urlQuery);
0642 
0643     KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ;
0644     if (!job) {
0645         qCDebug(CHOQOK) << "Cannot create an http POST request!";
0646 //         QString errMsg = i18n ( "Removing the favorite failed. Cannot create an http POST request. "
0647 //                                 "Please check your KDE installation." );
0648 //         emit error ( theAccount, OtherError, errMsg );
0649         return;
0650     }
0651     job->addMetaData(QStringLiteral("customHTTPHeader"),
0652                      QStringLiteral("Authorization: ") +
0653                      QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation)));
0654     mFavoriteMap[job] = postId;
0655     mJobsAccount[job] = theAccount;
0656     connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotRemoveFavorite);
0657     job->start();
0658 }
0659 
0660 void TwitterApiMicroBlog::slotRemoveFavorite(KJob *job)
0661 {
0662     qCDebug(CHOQOK);
0663     if (!job) {
0664         qCDebug(CHOQOK) << "Job is null pointer.";
0665         return;
0666     }
0667     QString id = mFavoriteMap.take(job);
0668     Choqok::Account *theAccount = mJobsAccount.take(job);
0669     if (job->error()) {
0670         qCDebug(CHOQOK) << "Job Error:" << job->errorString();
0671         Q_EMIT error(theAccount, CommunicationError, i18n("Removing the favorite failed. %1", job->errorString()));
0672     } else {
0673         KIO::StoredTransferJob *stJob = qobject_cast<KIO::StoredTransferJob *>(job);
0674         QString err = checkForError(stJob->data());
0675         if (!err.isEmpty()) {
0676             Q_EMIT error(theAccount, ServerError, err, Critical);
0677             return;
0678         } else {
0679             Q_EMIT favoriteRemoved(theAccount, id);
0680         }
0681     }
0682 }
0683 
0684 void TwitterApiMicroBlog::listFriendsUsername(TwitterApiAccount *theAccount, bool active)
0685 {
0686     friendsList.clear();
0687     d->friendsCursor = QLatin1String("-1");
0688     if (theAccount) {
0689         requestFriendsScreenName(theAccount, active);
0690     }
0691 }
0692 
0693 void TwitterApiMicroBlog::requestFriendsScreenName(TwitterApiAccount *theAccount, bool active)
0694 {
0695     qCDebug(CHOQOK);
0696     TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount);
0697     QUrl url = account->apiUrl();
0698     url = url.adjusted(QUrl::StripTrailingSlash);
0699     url.setPath(url.path() + QLatin1String("/friends/list.json"));
0700 
0701     QUrlQuery urlQuery;
0702     urlQuery.addQueryItem(QLatin1String("cursor"), d->friendsCursor);
0703     urlQuery.addQueryItem(QLatin1String("count"), QLatin1String("200"));
0704     url.setQuery(urlQuery);
0705 
0706     KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo) ;
0707     if (!job) {
0708         qCDebug(CHOQOK) << "Cannot create an http GET request!";
0709         return;
0710     }
0711     job->addMetaData(QStringLiteral("customHTTPHeader"),
0712                      QStringLiteral("Authorization: ") +
0713                      QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::GetOperation)));
0714     mJobsAccount[job] = theAccount;
0715     if (active) {
0716         connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotRequestFriendsScreenNameActive);
0717     } else {
0718         connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotRequestFriendsScreenNamePassive);
0719     }
0720     job->start();
0721     Choqok::UI::Global::mainWindow()->showStatusMessage(i18n("Updating friends list for account %1...",
0722                                                              theAccount->alias()));
0723 }
0724 
0725 void TwitterApiMicroBlog::slotRequestFriendsScreenNameActive(KJob *job)
0726 {
0727     finishRequestFriendsScreenName(job, true);
0728 }
0729 
0730 void TwitterApiMicroBlog::slotRequestFriendsScreenNamePassive(KJob *job)
0731 {
0732     finishRequestFriendsScreenName(job, false);
0733 }
0734 
0735 void TwitterApiMicroBlog::finishRequestFriendsScreenName(KJob *job, bool active)
0736 {
0737     qCDebug(CHOQOK);
0738     TwitterApiAccount *theAccount = qobject_cast<TwitterApiAccount *>(mJobsAccount.take(job));
0739     KIO::StoredTransferJob *stJob = qobject_cast<KIO::StoredTransferJob *>(job);
0740     Choqok::MicroBlog::ErrorLevel level = active ? Critical : Low;
0741     if (stJob->error()) {
0742         Q_EMIT error(theAccount, ServerError, i18n("Friends list for account %1 could not be updated:\n%2",
0743                      theAccount->username(), stJob->errorString()), level);
0744         return;
0745     }
0746     QStringList newList = readFriendsScreenName(theAccount, stJob->data());
0747     newList.removeDuplicates();
0748     if (! checkForError(stJob->data()).isEmpty()) {           // if an error occurred, do not replace the friends list.
0749         theAccount->setFriendsList(friendsList);
0750         Q_EMIT friendsUsernameListed(theAccount, friendsList);
0751     } else if (QString::compare(d->friendsCursor, QLatin1String("0"))) {   // if the cursor is not "0", there is more friends data to be had
0752         friendsList << newList;
0753         requestFriendsScreenName(theAccount, active);
0754     } else {
0755         friendsList << newList;
0756         theAccount->setFriendsList(friendsList);
0757         Choqok::UI::Global::mainWindow()->showStatusMessage(i18n("Friends list for account %1 has been updated.",
0758                                                                  theAccount->alias()));
0759         Q_EMIT friendsUsernameListed(theAccount, friendsList);
0760     }
0761 }
0762 
0763 void TwitterApiMicroBlog::listFollowersUsername(TwitterApiAccount* theAccount, bool active)
0764 {
0765     followersList.clear();
0766     d->followersCursor = QLatin1String("-1");
0767     if ( theAccount ) {
0768         requestFollowersScreenName(theAccount, active);
0769     }
0770 }
0771 
0772 void TwitterApiMicroBlog::requestFollowersScreenName(TwitterApiAccount* theAccount, bool active)
0773 {
0774     qCDebug(CHOQOK);
0775     TwitterApiAccount* account = qobject_cast<TwitterApiAccount*>(theAccount);
0776     QUrl url = account->apiUrl();
0777     url = url.adjusted(QUrl::StripTrailingSlash);
0778     url.setPath(url.path() + QLatin1String("/followers/list.json"));
0779 
0780     QUrlQuery urlQuery;
0781     urlQuery.addQueryItem(QLatin1String("cursor"), d->followersCursor);
0782     urlQuery.addQueryItem(QLatin1String("count"), QLatin1String("200"));
0783     url.setQuery(urlQuery);
0784 
0785     KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo);
0786     if (!job) {
0787         qCDebug(CHOQOK) << "Cannot create an http GET request!";
0788         return;
0789     }
0790     job->addMetaData(QStringLiteral("customHTTPHeader"),
0791                      QStringLiteral("Authorization: ") +
0792                      QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::GetOperation)));
0793     mJobsAccount[job] = theAccount;
0794     if (active) {
0795         connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotRequestFollowersScreenNameActive);
0796     } else {
0797         connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotRequestFollowersScreenNamePassive);
0798     }
0799     job->start();
0800     Choqok::UI::Global::mainWindow()->showStatusMessage(i18n("Updating followers list for account %1...",
0801                                                              theAccount->alias()));
0802 }
0803 
0804 void TwitterApiMicroBlog::slotRequestFollowersScreenNameActive(KJob* job)
0805 {
0806     finishRequestFollowersScreenName(job, true);
0807 }
0808 
0809 void TwitterApiMicroBlog::slotRequestFollowersScreenNamePassive(KJob* job)
0810 {
0811     finishRequestFollowersScreenName(job, false);
0812 }
0813 
0814 void TwitterApiMicroBlog::finishRequestFollowersScreenName(KJob* job, bool active)
0815 {
0816     qCDebug(CHOQOK);
0817     TwitterApiAccount *theAccount = qobject_cast<TwitterApiAccount *>( mJobsAccount.take(job) );
0818     KIO::StoredTransferJob* stJob = qobject_cast<KIO::StoredTransferJob*>( job );
0819     Choqok::MicroBlog::ErrorLevel level = active ? Critical : Low;
0820     if (stJob->error()) {
0821         Q_EMIT error(theAccount, ServerError, i18n("Followers list for account %1 could not be updated:\n%2",
0822             theAccount->username(), stJob->errorString()), level);
0823         return;
0824     }
0825     QStringList newList = readFollowersScreenName(theAccount, stJob->data());
0826     newList.removeDuplicates();
0827     if (!checkForError(stJob->data()).isEmpty()) {        // if an error occurred, do not replace the friends list.
0828         theAccount->setFollowersList(followersList);
0829         Q_EMIT followersUsernameListed(theAccount, followersList);
0830     } else if (QString::compare(d->followersCursor, QLatin1String("0"))) {    // if the cursor is not "0", there is more friends data to be had
0831         followersList << newList;
0832         requestFollowersScreenName(theAccount, active);
0833     } else {
0834         followersList << newList;
0835         theAccount->setFollowersList(followersList);
0836         Choqok::UI::Global::mainWindow()->showStatusMessage(i18n("Followers list for account %1 has been updated.",
0837                                                                  theAccount->alias()));
0838         Q_EMIT followersUsernameListed(theAccount, followersList);
0839     }
0840 }
0841 
0842 void TwitterApiMicroBlog::updateTimelines(Choqok::Account *theAccount)
0843 {
0844     qCDebug(CHOQOK);
0845     for (const QString &tm: theAccount->timelineNames()) {
0846         requestTimeLine(theAccount, tm, mTimelineLatestId[theAccount][tm]);
0847     }
0848 }
0849 
0850 void TwitterApiMicroBlog::requestTimeLine(Choqok::Account *theAccount, QString type,
0851         QString latestStatusId, int page, QString maxId)
0852 {
0853     qCDebug(CHOQOK);
0854     TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount);
0855     QUrl url = account->apiUrl();
0856     url.setPath(url.path() + timelineApiPath[type]);
0857 
0858     QUrlQuery urlQuery;
0859     // needed because lists have different parameter names but
0860     // returned timelines have the same JSON format
0861     if (timelineApiPath[type].contains(QLatin1String("lists/statuses"))) {
0862 
0863         // type contains @username/timelinename
0864         const QString slug = type.mid(type.indexOf(QLatin1String("/")) + 1);
0865         urlQuery.addQueryItem(QLatin1String("slug"), slug);
0866 
0867         const QString owner = type.mid(1, type.indexOf(QLatin1String("/")) - 1);
0868         urlQuery.addQueryItem(QLatin1String("owner_screen_name"), owner);
0869     } else {
0870         int countOfPost = Choqok::BehaviorSettings::countOfPosts();
0871         if (!latestStatusId.isEmpty()) {
0872             urlQuery.addQueryItem(QLatin1String("since_id"), latestStatusId);
0873             countOfPost = 200;
0874         }
0875 
0876         urlQuery.addQueryItem(QLatin1String("count"), QString::number(countOfPost));
0877 
0878         if (!maxId.isEmpty()) {
0879             urlQuery.addQueryItem(QLatin1String("max_id"), maxId);
0880         }
0881 
0882         if (page) {
0883             urlQuery.addQueryItem(QLatin1String("page"), QString::number(page));
0884         }
0885     }
0886 
0887     url.setQuery(urlQuery);
0888 
0889     qCDebug(CHOQOK) << "Latest" << type << "Id:" << latestStatusId;// << "apiReq:" << url;
0890 
0891     KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo) ;
0892     if (!job) {
0893         qCDebug(CHOQOK) << "Cannot create an http GET request!";
0894 //         QString errMsg = i18n ( "Cannot create an http GET request. Please check your KDE installation." );
0895 //         emit error ( theAccount, OtherError, errMsg, Low );
0896         return;
0897     }
0898     job->addMetaData(QStringLiteral("customHTTPHeader"),
0899                      QStringLiteral("Authorization: ")
0900                      + QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::GetOperation)));
0901     mRequestTimelineMap[job] = type;
0902     mJobsAccount[job] = theAccount;
0903     connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotRequestTimeline);
0904     job->start();
0905 }
0906 
0907 void TwitterApiMicroBlog::slotRequestTimeline(KJob *job)
0908 {
0909     qCDebug(CHOQOK);//TODO Add error detection
0910     if (!job) {
0911         qCDebug(CHOQOK) << "Job is null pointer";
0912         return;
0913     }
0914     Choqok::Account *theAccount = mJobsAccount.take(job);
0915     if (job->error()) {
0916         qCDebug(CHOQOK) << "Job Error:" << job->errorString();
0917         Q_EMIT error(theAccount, CommunicationError,
0918                      i18n("Timeline update failed: %1", job->errorString()), Low);
0919         return;
0920     }
0921     QString type = mRequestTimelineMap.take(job);
0922     if (isValidTimeline(type)) {
0923         KIO::StoredTransferJob *j = qobject_cast<KIO::StoredTransferJob *>(job);
0924         QList<Choqok::Post *> list;
0925         if (type == QLatin1String("Inbox") || type == QLatin1String("Outbox")) {
0926             list = readDirectMessages(theAccount, j->data());
0927         } else {
0928             list = readTimeline(theAccount, j->data());
0929 
0930         }
0931         if (!list.isEmpty()) {
0932             mTimelineLatestId[theAccount][type] = list.last()->postId;
0933             Q_EMIT timelineDataReceived(theAccount, type, list);
0934         }
0935     }
0936 }
0937 
0938 QByteArray TwitterApiMicroBlog::authorizationHeader(TwitterApiAccount *theAccount, const QUrl &requestUrl,
0939                                                     QNetworkAccessManager::Operation method, const QVariantMap &params)
0940 {
0941     QByteArray auth;
0942     if (theAccount->usingOAuth()) {
0943         auth = theAccount->oauthInterface()->authorizationHeader(requestUrl, method, params);
0944     } else {
0945         auth = theAccount->username().toUtf8() + ':' + theAccount->password().toUtf8();
0946         auth = auth.toBase64().prepend("Basic ");
0947     }
0948     return auth;
0949 }
0950 
0951 void TwitterApiMicroBlog::setRepeatedOfInfo(Choqok::Post *post, Choqok::Post *repeatedPost)
0952 {
0953     post->content = repeatedPost->content;
0954     post->replyToPostId = repeatedPost->replyToPostId;
0955     post->replyToUser.userId = repeatedPost->replyToUser.userId;
0956     post->replyToUser.userName = repeatedPost->replyToUser.userName;
0957     post->repeatedPostId = repeatedPost->postId;
0958     post->repeatedDateTime = repeatedPost->creationDateTime;
0959 
0960     if (Choqok::AppearanceSettings::showRetweetsInChoqokWay()) {
0961         post->repeatedFromUser.userName = repeatedPost->author.userName;
0962         post->repeatedFromUser.homePageUrl = repeatedPost->author.homePageUrl;
0963     } else {
0964         post->repeatedFromUser.userName = post->author.userName;
0965         post->repeatedFromUser.homePageUrl = post->author.homePageUrl;
0966         post->author = repeatedPost->author;
0967     }
0968 
0969     if (!repeatedPost->quotedPost.content.isEmpty()) {
0970         post->quotedPost = repeatedPost->quotedPost;
0971     }
0972 }
0973 void TwitterApiMicroBlog::setQuotedPost(Choqok::Post* post, Choqok::Post* quotedPost)
0974 {
0975     post->quotedPost.user.profileImageUrl = quotedPost->author.profileImageUrl;
0976     post->quotedPost.user.userName = quotedPost->author.userName;
0977     post->quotedPost.postId = quotedPost->postId;
0978     post->quotedPost.content = quotedPost->content;
0979 }
0980 
0981 QDateTime TwitterApiMicroBlog::dateFromString(const QString &date)
0982 {
0983     char s[10];
0984     int year, day, hours, minutes, seconds, tz;
0985     sscanf(qPrintable(date), "%*s %s %d %d:%d:%d %d %d", s, &day, &hours, &minutes, &seconds, &tz, &year);
0986     int month = d->monthes[QLatin1String(s)];
0987     QDateTime recognized(QDate(year, month, day), QTime(hours, minutes, seconds));
0988     if (tz == 0) { //tz is the timezone, in Twitter it's always UTC(0) in Identica it's local +/-NUMBER
0989         recognized.setTimeSpec(Qt::UTC);
0990     }
0991     return recognized.toLocalTime();
0992 }
0993 
0994 void TwitterApiMicroBlog::aboutToUnload()
0995 {
0996     d->countOfTimelinesToSave = 0;
0997     for (Choqok::Account *acc: Choqok::AccountManager::self()->accounts()) {
0998         if (acc->microblog() == this) {
0999             d->countOfTimelinesToSave += acc->timelineNames().count();
1000         }
1001     }
1002     Q_EMIT saveTimelines();
1003 }
1004 
1005 void TwitterApiMicroBlog::showDirectMessageDialog(TwitterApiAccount *theAccount, const QString &toUsername)
1006 {
1007     qCDebug(CHOQOK);
1008     if (!theAccount) {
1009         QAction *act = qobject_cast<QAction *>(sender());
1010         theAccount = qobject_cast<TwitterApiAccount *>(
1011                          Choqok::AccountManager::self()->findAccount(act->data().toString()));
1012     }
1013     TwitterApiDMessageDialog *dmsg = new TwitterApiDMessageDialog(theAccount, Choqok::UI::Global::mainWindow());
1014     if (!toUsername.isEmpty()) {
1015         dmsg->setTo(toUsername);
1016     }
1017     dmsg->show();
1018 }
1019 
1020 Choqok::TimelineInfo *TwitterApiMicroBlog::timelineInfo(const QString &timelineName)
1021 {
1022     if (isValidTimeline(timelineName)) {
1023         return mTimelineInfos.value(timelineName);
1024     } else {
1025         return nullptr;
1026     }
1027 }
1028 
1029 void TwitterApiMicroBlog::showSearchDialog(TwitterApiAccount *theAccount)
1030 {
1031     if (!theAccount) {
1032         QAction *act = qobject_cast<QAction *>(sender());
1033         theAccount = qobject_cast<TwitterApiAccount *>(
1034                          Choqok::AccountManager::self()->findAccount(act->data().toString()));
1035     }
1036     QPointer<TwitterApiSearchDialog> searchDlg = new TwitterApiSearchDialog(theAccount,
1037             Choqok::UI::Global::mainWindow());
1038     searchDlg->show();
1039 }
1040 
1041 void TwitterApiMicroBlog::slotUpdateFriendsList()
1042 {
1043     QAction *act = qobject_cast<QAction *>(sender());
1044     TwitterApiAccount *theAccount = qobject_cast<TwitterApiAccount *>(
1045                                         Choqok::AccountManager::self()->findAccount(act->data().toString()));
1046     listFriendsUsername(theAccount, true);
1047 }
1048 
1049 void TwitterApiMicroBlog::createFriendship(Choqok::Account *theAccount, const QString &username)
1050 {
1051     qCDebug(CHOQOK);
1052     TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount);
1053     QUrl url = account->apiUrl();
1054     url.setPath(url.path() + QLatin1String("/friendships/create.json"));
1055 
1056     QUrlQuery urlQuery;
1057     urlQuery.addQueryItem(QLatin1String("screen_name"), username);
1058     url.setQuery(urlQuery);
1059 
1060     KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ;
1061     qCDebug(CHOQOK) << url;
1062     if (!job) {
1063         qCCritical(CHOQOK) << "Cannot create an http POST request!";
1064         return;
1065     }
1066     job->addMetaData(QStringLiteral("customHTTPHeader"),
1067                      QStringLiteral("Authorization: ") +
1068                      QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation)));
1069     mJobsAccount[job] = theAccount;
1070     mFriendshipMap[ job ] = username;
1071     connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotCreateFriendship);
1072     job->start();
1073 }
1074 
1075 void TwitterApiMicroBlog::slotCreateFriendship(KJob *job)
1076 {
1077     qCDebug(CHOQOK);
1078     if (!job) {
1079         qCCritical(CHOQOK) << "Job is a null Pointer!";
1080         return;
1081     }
1082     TwitterApiAccount *theAccount = qobject_cast<TwitterApiAccount *>(mJobsAccount.take(job));
1083     QString username = mFriendshipMap.take(job);
1084     if (job->error()) {
1085         qCDebug(CHOQOK) << "Job Error:" << job->errorString();
1086         Q_EMIT error(theAccount, CommunicationError,
1087                      i18n("Creating friendship with %1 failed. %2", username, job->errorString()));
1088         return;
1089     }
1090     KIO::StoredTransferJob *stj = qobject_cast<KIO::StoredTransferJob *>(job);
1091     Choqok::User *user = readUserInfo(stj->data());
1092     if (user /*&& user->userName.compare(username, Qt::CaseInsensitive)*/) {
1093         Q_EMIT friendshipCreated(theAccount, username);
1094         Choqok::NotifyManager::success(i18n("You are now listening to %1's posts.", username));
1095         theAccount->setFriendsList(QStringList());
1096         listFriendsUsername(theAccount);
1097     } else {
1098         QString errorMsg = checkForError(stj->data());
1099         if (errorMsg.isEmpty()) {
1100             qCDebug(CHOQOK) << "Parse Error:" << stj->data();
1101             Q_EMIT error(theAccount, ParsingError,
1102                          i18n("Creating friendship with %1 failed: the server returned invalid data.",
1103                               username));
1104         } else {
1105             qCDebug(CHOQOK) << "Server error:" << errorMsg;
1106             Q_EMIT error(theAccount, ServerError,
1107                          i18n("Creating friendship with %1 failed: %2",
1108                               username, errorMsg));
1109         }
1110     }
1111 }
1112 
1113 void TwitterApiMicroBlog::destroyFriendship(Choqok::Account *theAccount, const QString &username)
1114 {
1115     qCDebug(CHOQOK);
1116     TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount);
1117     QUrl url = account->apiUrl();
1118     url.setPath(url.path() + QLatin1String("/friendships/destroy.json"));
1119 
1120     QUrlQuery urlQuery;
1121     urlQuery.addQueryItem(QLatin1String("screen_name"), username);
1122     url.setQuery(urlQuery);
1123 
1124     KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ;
1125     if (!job) {
1126         qCCritical(CHOQOK) << "Cannot create an http POST request!";
1127         return;
1128     }
1129     job->addMetaData(QStringLiteral("customHTTPHeader"),
1130                      QStringLiteral("Authorization: ") +
1131                      QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation)));
1132     mJobsAccount[job] = theAccount;
1133     mFriendshipMap[ job ] = username;
1134     connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotDestroyFriendship);
1135     job->start();
1136 }
1137 
1138 void TwitterApiMicroBlog::slotDestroyFriendship(KJob *job)
1139 {
1140     qCDebug(CHOQOK);
1141     if (!job) {
1142         qCCritical(CHOQOK) << "Job is a null Pointer!";
1143         return;
1144     }
1145     TwitterApiAccount *theAccount = qobject_cast<TwitterApiAccount *>(mJobsAccount.take(job));
1146     QString username = mFriendshipMap.take(job);
1147     if (job->error()) {
1148         qCDebug(CHOQOK) << "Job Error:" << job->errorString();
1149         Q_EMIT error(theAccount, CommunicationError,
1150                      i18n("Destroying friendship with %1 failed. %2", username, job->errorString()));
1151         return;
1152     }
1153     KIO::StoredTransferJob *stj = qobject_cast<KIO::StoredTransferJob *>(job);
1154     Choqok::User *user = readUserInfo(stj->data());
1155     if (user /*&& user->userName.compare( username, Qt::CaseInsensitive )*/) {
1156         Q_EMIT friendshipDestroyed(theAccount, username);
1157         Choqok::NotifyManager::success(i18n("You will not receive %1's updates.", username));
1158         theAccount->setFriendsList(QStringList());
1159         listFriendsUsername(theAccount);
1160     } else {
1161         QString errorMsg = checkForError(stj->data());
1162         if (errorMsg.isEmpty()) {
1163             qCDebug(CHOQOK) << "Parse Error:" << stj->data();
1164             Q_EMIT error(theAccount, ParsingError,
1165                          i18n("Destroying friendship with %1 failed: the server returned invalid data.",
1166                               username));
1167         } else {
1168             qCDebug(CHOQOK) << "Server error:" << errorMsg;
1169             Q_EMIT error(theAccount, ServerError,
1170                          i18n("Destroying friendship with %1 failed: %2",
1171                               username, errorMsg));
1172         }
1173     }
1174 }
1175 
1176 void TwitterApiMicroBlog::blockUser(Choqok::Account *theAccount, const QString &username)
1177 {
1178     qCDebug(CHOQOK);
1179     TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount);
1180     QUrl url = account->apiUrl();
1181     url.setPath(url.path() + QLatin1String("/blocks/create.json"));
1182 
1183     QUrlQuery urlQuery;
1184     urlQuery.addQueryItem(QLatin1String("screen_name"), username);
1185     url.setQuery(urlQuery);
1186 
1187     KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ;
1188     if (!job) {
1189         qCCritical(CHOQOK) << "Cannot create an http POST request!";
1190         return;
1191     }
1192     job->addMetaData(QStringLiteral("customHTTPHeader"),
1193                      QStringLiteral("Authorization: ") +
1194                      QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation)));
1195     mJobsAccount[job] = theAccount;
1196     mFriendshipMap[ job ] = username;
1197     connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotBlockUser);
1198     job->start();
1199 }
1200 
1201 void TwitterApiMicroBlog::reportUserAsSpam(Choqok::Account *theAccount, const QString &username)
1202 {
1203     qCDebug(CHOQOK);
1204     TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount);
1205     QUrl url = account->apiUrl();
1206     url = url.adjusted(QUrl::StripTrailingSlash);
1207     url.setPath(url.path() + QLatin1String("/users/report_spam.json"));
1208 
1209     QUrlQuery urlQuery;
1210     urlQuery.addQueryItem(QLatin1String("screen_name"), username);
1211     url.setQuery(urlQuery);
1212 
1213     KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ;
1214     if (!job) {
1215         qCCritical(CHOQOK) << "Cannot create an http POST request!";
1216         return;
1217     }
1218     job->addMetaData(QStringLiteral("customHTTPHeader"),
1219                      QStringLiteral("Authorization: ") +
1220                      QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation)));
1221     mJobsAccount[job] = theAccount;
1222     mFriendshipMap[ job ] = username;
1223     connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotReportUser);
1224     job->start();
1225 
1226 }
1227 
1228 void TwitterApiMicroBlog::slotBlockUser(KJob *job)
1229 {
1230     qCDebug(CHOQOK);
1231     if (!job) {
1232         qCCritical(CHOQOK) << "Job is a null Pointer!";
1233         return;
1234     }
1235     Choqok::Account *theAccount = mJobsAccount.take(job);
1236     QString username = mFriendshipMap.take(job);
1237     if (job->error()) {
1238         qCDebug(CHOQOK) << "Job Error:" << job->errorString();
1239         Q_EMIT error(theAccount, CommunicationError,
1240                      i18n("Blocking %1 failed. %2", username, job->errorString()));
1241         return;
1242     }
1243     Choqok::User *user = readUserInfo(qobject_cast<KIO::StoredTransferJob *>(job)->data());
1244     if (user /*&& user->userName.compare( username, Qt::CaseInsensitive )*/) {
1245         Q_EMIT userBlocked(theAccount, username);
1246         Choqok::NotifyManager::success(i18n("You will no longer be disturbed by %1.", username));
1247     } else {
1248         qCDebug(CHOQOK) << "Parse Error:" << qobject_cast<KIO::StoredTransferJob *>(job)->data();
1249         Q_EMIT error(theAccount, ParsingError,
1250                      i18n("Blocking %1 failed: the server returned invalid data.",
1251                           username));
1252     }
1253     //TODO Check for failor!
1254 }
1255 
1256 void TwitterApiMicroBlog::slotReportUser(KJob *job)
1257 {
1258     qCDebug(CHOQOK);
1259     if (!job) {
1260         qCCritical(CHOQOK) << "Job is a null Pointer!";
1261         return;
1262     }
1263 
1264     Choqok::Account *theAccount = mJobsAccount.take(job);
1265     QString username = mFriendshipMap.take(job);
1266     if (job->error()) {
1267         qCDebug(CHOQOK) << "Job Error:" << job->errorString();
1268         Q_EMIT error(theAccount, CommunicationError,
1269                      i18n("Reporting %1 failed. %2", username, job->errorString()));
1270         return;
1271     }
1272     Choqok::User *user = readUserInfo(qobject_cast<KIO::StoredTransferJob *>(job)->data());
1273     if (user) {
1274         Choqok::NotifyManager::success(i18n("Report sent successfully."));
1275     } else {
1276         qCDebug(CHOQOK) << "Parse Error:" << qobject_cast<KIO::StoredTransferJob *>(job)->data();
1277         Q_EMIT error(theAccount, ParsingError,
1278                      i18n("Reporting %1 failed: the server returned invalid data.",
1279                           username));
1280     }
1281 }
1282 
1283 ///===================================================================
1284 
1285 QString TwitterApiMicroBlog::checkForError(const QByteArray &buffer)
1286 {
1287     const QJsonDocument json = QJsonDocument::fromJson(buffer);
1288     if (!json.isNull()) {
1289         const QVariantMap map = json.toVariant().toMap();
1290         if (map.contains(QLatin1String("errors"))) {
1291             QStringList errors;
1292             for (const QVariant &msg: map[QLatin1String("errors")].toList()) {
1293                 errors.append(msg.toMap()[QLatin1String("message")].toString());
1294                 qCCritical(CHOQOK) << "Error:" << errors.last();
1295             }
1296 
1297             return errors.join(QLatin1Char(';'));
1298         }
1299     }
1300     return QString();
1301 }
1302 
1303 QList< Choqok::Post * > TwitterApiMicroBlog::readTimeline(Choqok::Account *theAccount,
1304         const QByteArray &buffer)
1305 {
1306     QList<Choqok::Post *> postList;
1307     const QJsonDocument json = QJsonDocument::fromJson(buffer);
1308     if (!json.isNull()) {
1309         for (const QVariant &list: json.toVariant().toList()) {
1310             Choqok::Post *post = readPost(theAccount, list.toMap(), new Choqok::Post);
1311 
1312             if (post) {
1313                 postList.prepend(post);
1314             }
1315         }
1316     } else {
1317         const QString err = checkForError(buffer);
1318         if (err.isEmpty()) {
1319             qCCritical(CHOQOK) << "JSON parsing failed.\nBuffer was: \n" << buffer;
1320             Q_EMIT error(theAccount, ParsingError, i18n("Could not parse the data that has been received from the server."));
1321         } else {
1322             Q_EMIT error(theAccount, ServerError, err);
1323         }
1324     }
1325     return postList;
1326 }
1327 
1328 Choqok::Post *TwitterApiMicroBlog::readPost(Choqok::Account *theAccount,
1329         const QByteArray &buffer, Choqok::Post *post)
1330 {
1331     const QJsonDocument json = QJsonDocument::fromJson(buffer);
1332     if (!json.isNull()) {
1333         return readPost(theAccount, json.toVariant().toMap(), post);
1334     } else {
1335         if (!post) {
1336             qCCritical(CHOQOK) << "TwitterApiMicroBlog::readPost: post is NULL!";
1337             post = new Choqok::Post;
1338         }
1339         Q_EMIT errorPost(theAccount, post, ParsingError, i18n("Could not parse the data that has been received from the server."));
1340         qCCritical(CHOQOK) << "JSon parsing failed. Buffer was:" << buffer;
1341         post->isError = true;
1342         return post;
1343     }
1344 }
1345 
1346 Choqok::Post *TwitterApiMicroBlog::readPost(Choqok::Account *theAccount,
1347         const QVariantMap &var, Choqok::Post *post)
1348 {
1349     if (!post) {
1350         qCCritical(CHOQOK) << "TwitterApiMicroBlog::readPost: post is NULL!";
1351         return nullptr;
1352     }
1353     post->content = var[QLatin1String("text")].toString();
1354     post->creationDateTime = dateFromString(var[QLatin1String("created_at")].toString());
1355     post->isFavorited = var[QLatin1String("favorited")].toBool();
1356     post->postId = var[QLatin1String("id")].toString();
1357     post->replyToPostId = var[QLatin1String("in_reply_to_status_id")].toString();
1358     post->replyToUser.userId = var[QLatin1String("in_reply_to_user_id")].toString();
1359     post->replyToUser.userName = var[QLatin1String("in_reply_to_screen_name")].toString();
1360     post->source = var[QLatin1String("source")].toString();
1361     QVariantMap userMap = var[QLatin1String("user")].toMap();
1362     post->author.description = userMap[QLatin1String("description")].toString();
1363     post->author.location = userMap[QLatin1String("location")].toString();
1364     post->author.realName = userMap[QLatin1String("name")].toString();
1365     post->author.userId = userMap[QLatin1String("id")].toString();
1366     post->author.userName = userMap[QLatin1String("screen_name")].toString();
1367     post->author.profileImageUrl = userMap[QLatin1String("profile_image_url")].toUrl();
1368     QVariantMap entities = var[QLatin1String("entities")].toMap();
1369     QVariantMap mediaMap;
1370     QVariantList media = entities[QLatin1String("media")].toList();
1371     if (media.size() > 0) {
1372         mediaMap = media.at(0).toMap();
1373         post->media = QUrl::fromUserInput(mediaMap[QLatin1String("media_url")].toString() + QLatin1String(":small"));
1374         QVariantMap sizes = mediaMap[QLatin1String("sizes")].toMap();
1375         QVariantMap w = sizes[QLatin1String("small")].toMap();
1376     } else {
1377         post->media = QUrl();
1378     }
1379 
1380     QVariantMap retweetedMap = var[QLatin1String("retweeted_status")].toMap();
1381     if (!retweetedMap.isEmpty()) {
1382         Choqok::Post *retweetedPost = readPost(theAccount, retweetedMap, new Choqok::Post);
1383         setRepeatedOfInfo(post, retweetedPost);
1384         delete retweetedPost;
1385     }
1386     QVariantMap quotedMap = var[QLatin1String("quoted_status")].toMap();
1387     if (!quotedMap.isEmpty()) {
1388         Choqok::Post *quotedPost = readPost(theAccount, quotedMap, new Choqok::Post);
1389         setQuotedPost(post, quotedPost);
1390         delete quotedPost;
1391     }
1392     post->link = postUrl(theAccount, post->author.userName, post->postId);
1393     post->isRead = post->isFavorited || (post->repeatedFromUser.userName.compare(theAccount->username(), Qt::CaseInsensitive) == 0);
1394 
1395     if(post->postId.isEmpty() || post->author.userName.isEmpty())
1396         post->isError = true;
1397 
1398     return post;
1399 }
1400 
1401 QList< Choqok::Post * > TwitterApiMicroBlog::readDirectMessages(Choqok::Account *theAccount,
1402         const QByteArray &buffer)
1403 {
1404     QList<Choqok::Post *> postList;
1405     const QJsonDocument json = QJsonDocument::fromJson(buffer);
1406     if (!json.isNull()) {
1407         for (const QVariant &list: json.toVariant().toList()) {
1408             postList.prepend(readDirectMessage(theAccount, list.toMap()));
1409         }
1410     } else {
1411         const QString err = checkForError(buffer);
1412         if (err.isEmpty()) {
1413             qCCritical(CHOQOK) << "JSON parsing failed.\nBuffer was: \n" << buffer;
1414             Q_EMIT error(theAccount, ParsingError, i18n("Could not parse the data that has been received from the server."));
1415         } else {
1416             Q_EMIT error(theAccount, ServerError, err);
1417         }
1418     }
1419     return postList;
1420 }
1421 
1422 Choqok::Post *TwitterApiMicroBlog::readDirectMessage(Choqok::Account *theAccount,
1423         const QByteArray &buffer)
1424 {
1425     const QJsonDocument json = QJsonDocument::fromJson(buffer);
1426     if (!json.isNull()) {
1427         return readDirectMessage(theAccount, json.toVariant().toMap());
1428     } else {
1429         Choqok::Post *post = new Choqok::Post;
1430         post->isError = true;
1431         return post;
1432     }
1433 }
1434 
1435 Choqok::Post *TwitterApiMicroBlog::readDirectMessage(Choqok::Account *theAccount,
1436         const QVariantMap &var)
1437 {
1438     Choqok::Post *msg = new Choqok::Post;
1439 
1440     msg->isPrivate = true;
1441     QString senderId, recipientId, senderScreenName, recipientScreenName, senderName,
1442             senderDescription, recipientName, recipientDescription;
1443     QUrl senderProfileImageUrl, recipientProfileImageUrl;
1444 
1445     msg->creationDateTime = dateFromString(var[QLatin1String("created_at")].toString());
1446     msg->content = var[QLatin1String("text")].toString();
1447     msg->postId = var[QLatin1String("id")].toString();;
1448     senderId = var[QLatin1String("sender_id")].toString();
1449     recipientId = var[QLatin1String("recipient_id")].toString();
1450     senderScreenName = var[QLatin1String("sender_screen_name")].toString();
1451     recipientScreenName = var[QLatin1String("recipient_screen_name")].toString();
1452     QVariantMap sender = var[QLatin1String("sender")].toMap();
1453     senderProfileImageUrl = sender[QLatin1String("profile_image_url")].toUrl();
1454     senderName = sender[QLatin1String("name")].toString();
1455     senderDescription = sender[QLatin1String("description")].toString();
1456     QVariantMap recipient = var[QLatin1String("recipient")].toMap();
1457     recipientProfileImageUrl = recipient[QLatin1String("profile_image_url")].toUrl();
1458     recipientName = recipient[QLatin1String("name")].toString();
1459     recipientDescription = recipient[QLatin1String("description")].toString();
1460     if (senderScreenName.compare(theAccount->username(), Qt::CaseInsensitive) == 0) {
1461         msg->author.description = recipientDescription;
1462         msg->author.userName = recipientScreenName;
1463         msg->author.profileImageUrl = recipientProfileImageUrl;
1464         msg->author.realName = recipientName;
1465         msg->author.userId = recipientId;
1466         msg->replyToUser.userId = recipientId;
1467         msg->replyToUser.userName = recipientScreenName;
1468         msg->isRead = true;
1469     } else {
1470         msg->author.description = senderDescription;
1471         msg->author.userName = senderScreenName;
1472         msg->author.profileImageUrl = senderProfileImageUrl;
1473         msg->author.realName = senderName;
1474         msg->author.userId = senderId;
1475         msg->replyToUser.userId = recipientId;
1476         msg->replyToUser.userName = recipientScreenName;
1477     }
1478     return msg;
1479 }
1480 
1481 Choqok::User *TwitterApiMicroBlog::readUserInfo(const QByteArray &buffer)
1482 {
1483     Choqok::User *user = nullptr;
1484     const QJsonDocument json = QJsonDocument::fromJson(buffer);
1485     if (!json.isNull()) {
1486         user = new Choqok::User(readUser(nullptr, json.toVariant().toMap()));
1487     } else {
1488         QString err = i18n("Retrieving the friends list failed. The data returned from the server is corrupted.");
1489         qCDebug(CHOQOK) << "JSON parse error:the buffer is: \n" << buffer;
1490         Q_EMIT error(nullptr, ParsingError, err, Critical);
1491     }
1492     return user;
1493 }
1494 
1495 QStringList TwitterApiMicroBlog::readFriendsScreenName(Choqok::Account *theAccount,
1496         const QByteArray &buffer)
1497 {
1498     QStringList list;
1499     const QJsonDocument json = QJsonDocument::fromJson(buffer);
1500     if (!json.isNull()) {
1501         const QVariantMap map = json.toVariant().toMap();
1502         QVariantList jsonList = map[QLatin1String("users")].toList();
1503         QString nextCursor = map[QLatin1String("next_cursor_str")].toString();
1504 
1505         if (nextCursor.isEmpty()) {
1506             nextCursor = QLatin1String("0"); // we probably ran the rate limit; stop bugging the server already
1507         }
1508 
1509         for (const QVariant &user: jsonList) {
1510             list << user.toMap()[QLatin1String("screen_name")].toString();
1511         }
1512         d->friendsCursor = nextCursor;
1513     } else {
1514         QString err = i18n("Retrieving the friends list failed. The data returned from the server is corrupted.");
1515         qCDebug(CHOQOK) << "JSON parse error:the buffer is: \n" << buffer;
1516         Q_EMIT error(theAccount, ParsingError, err, Critical);
1517     }
1518 
1519     return list;
1520 }
1521 
1522 QStringList TwitterApiMicroBlog::readFollowersScreenName(Choqok::Account *theAccount,
1523         const QByteArray &buffer)
1524 {
1525     QStringList list;
1526     const QJsonDocument json = QJsonDocument::fromJson(buffer);
1527     if (!json.isNull()) {
1528         const QVariantMap map = json.toVariant().toMap();
1529         QVariantList jsonList = map[QLatin1String("users")].toList();
1530         QString nextCursor = map[QLatin1String("next_cursor_str")].toString();
1531 
1532         if (nextCursor.isEmpty()) {
1533             nextCursor = QLatin1String("0"); // we probably ran the rate limit; stop bugging the server already
1534         }
1535 
1536         for (const QVariant &user: jsonList) {
1537             list << user.toMap()[QLatin1String("screen_name")].toString();
1538         }
1539 
1540         d->followersCursor = nextCursor;
1541     } else {
1542         QString err = i18n("Retrieving the followers list failed. The data returned from the server is corrupted.");
1543         qCDebug(CHOQOK) << "JSON parse error:the buffer is: \n" << buffer;
1544         Q_EMIT error(theAccount, ParsingError, err, Critical);
1545     }
1546 
1547     return list;
1548 }
1549 
1550 Choqok::User TwitterApiMicroBlog::readUser(Choqok::Account *theAccount, const QVariantMap &map)
1551 {
1552     Q_UNUSED(theAccount);
1553     Choqok::User u;
1554     u.description = map[QLatin1String("description")].toString();
1555     u.homePageUrl = map[QLatin1String("url")].toUrl();
1556     u.isProtected = map[QLatin1String("protected")].toBool();
1557     u.location = map[QLatin1String("location")].toString();
1558     u.profileImageUrl = map[QLatin1String("profile_image_url")].toUrl();
1559     u.realName = map[QLatin1String("name")].toString();
1560     u.userId = map[QLatin1String("id_str")].toString();
1561     u.userName = map[QLatin1String("screen_name")].toString();
1562     return u;
1563 }
1564 
1565 #include "moc_twitterapimicroblog.cpp"