diff --git a/CMakeLists.txt b/CMakeLists.txt index 45beceaa..556ee969 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,75 +1,75 @@ project(choqok) cmake_minimum_required(VERSION 2.8.12) find_package(ECM REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) include(KDEInstallDirs) include(ECMInstallIcons) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(FeatureSummary) find_package(Qt5 CONFIG REQUIRED Core DBus Network + NetworkAuth Widgets ) find_package(KF5 REQUIRED ConfigWidgets CoreAddons DocTools I18n Emoticons GlobalAccel GuiAddons KCMUtils KIO Notifications NotifyConfig Sonnet TextWidgets Wallet WidgetsAddons XmlGui ) #option(QTINDICATE_DISABLE "Disable support for notifications via indicator") find_package(Qca-qt5 REQUIRED) -find_package(QtOAuth 2.0.1 REQUIRED) #if(NOT QTINDICATE_DISABLE) # find_package(QtIndicate) # if(QtIndicate_FOUND) # set(QTINDICATE_BUILD TRUE) # endif(QtIndicate_FOUND) #endif(NOT QTINDICATE_DISABLE) #if(QTINDICATE_BUILD) # message(STATUS "Found libindicate-qt: Will support notifications via indicator") #else(QTINDICATE_BUILD) # message(STATUS "Optional dependency libindicate-qt not found: Won't support notifications via indicator") #endif(QTINDICATE_BUILD) set(CHOQOK_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/libchoqok/ ${CMAKE_CURRENT_SOURCE_DIR}/libchoqok/ui ) add_subdirectory( libchoqok ) add_subdirectory( helperlibs ) add_subdirectory( choqok ) add_subdirectory( icons ) add_subdirectory( plugins ) add_subdirectory( microblogs ) add_subdirectory( images ) add_subdirectory( cmake ) include(ECMOptionalAddSubdirectory) ecm_optional_add_subdirectory( doc ) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/helperlibs/gnusocialapihelper/CMakeLists.txt b/helperlibs/gnusocialapihelper/CMakeLists.txt index ca148382..0eee31c0 100644 --- a/helperlibs/gnusocialapihelper/CMakeLists.txt +++ b/helperlibs/gnusocialapihelper/CMakeLists.txt @@ -1,48 +1,48 @@ include_directories( ${CHOQOK_INCLUDES} ) set(gnusocialapihelper_SRCS gnusocialapiaccount.cpp gnusocialapicomposerwidget.cpp gnusocialapiconversationtimelinewidget.cpp gnusocialapidebug.cpp gnusocialapidmessagedialog.cpp gnusocialapimicroblog.cpp gnusocialapipostwidget.cpp gnusocialapisearch.cpp ) add_library(gnusocialapihelper SHARED ${gnusocialapihelper_SRCS}) target_link_libraries(gnusocialapihelper PUBLIC Qt5::Core + Qt5::NetworkAuth Qt5::Xml Qt5::Widgets KF5::CoreAddons KF5::I18n KF5::KIOCore KF5::KIOWidgets KF5::WidgetsAddons choqok twitterapihelper - ${QTOAUTH_LIBRARY} ) set_target_properties(gnusocialapihelper PROPERTIES VERSION 1.3.0 SOVERSION 1 ) install(TARGETS gnusocialapihelper ${INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES gnusocialapiaccount.h gnusocialapicomposerwidget.h gnusocialapiconversationtimelinewidget.h gnusocialapidmessagedialog.h gnusocialapimicroblog.h gnusocialapipostwidget.h gnusocialapisearch.h DESTINATION ${INCLUDE_INSTALL_DIR}/choqok/gnusocialapihelper COMPONENT Devel) diff --git a/helperlibs/gnusocialapihelper/gnusocialapimicroblog.cpp b/helperlibs/gnusocialapihelper/gnusocialapimicroblog.cpp index d4c1e7d2..40d0a648 100644 --- a/helperlibs/gnusocialapihelper/gnusocialapimicroblog.cpp +++ b/helperlibs/gnusocialapihelper/gnusocialapimicroblog.cpp @@ -1,444 +1,444 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2008-2012 Mehrdad Momeny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #include "gnusocialapimicroblog.h" #include #include #include #include #include #include #include #include #include "account.h" #include "accountmanager.h" #include "choqokappearancesettings.h" #include "composerwidget.h" #include "editaccountwidget.h" #include "mediamanager.h" #include "microblogwidget.h" #include "postwidget.h" #include "timelinewidget.h" #include "twitterapimicroblogwidget.h" #include "twitterapipostwidget.h" #include "twitterapitimelinewidget.h" #include "gnusocialapiaccount.h" #include "gnusocialapicomposerwidget.h" #include "gnusocialapidebug.h" #include "gnusocialapidmessagedialog.h" #include "gnusocialapipostwidget.h" #include "gnusocialapisearch.h" GNUSocialApiMicroBlog::GNUSocialApiMicroBlog(const QString &componentName, QObject *parent = 0) : TwitterApiMicroBlog(componentName, parent), friendsPage(1) { qCDebug(CHOQOK); setServiceName(QLatin1String("GNU social")); mTimelineInfos[QLatin1String("ReTweets")]->name = i18nc("Timeline name", "Repeated"); mTimelineInfos[QLatin1String("ReTweets")]->description = i18nc("Timeline description", "Your posts that were repeated by others"); } GNUSocialApiMicroBlog::~GNUSocialApiMicroBlog() { qCDebug(CHOQOK); } Choqok::Account *GNUSocialApiMicroBlog::createNewAccount(const QString &alias) { GNUSocialApiAccount *acc = qobject_cast(Choqok::AccountManager::self()->findAccount(alias)); if (!acc) { return new GNUSocialApiAccount(this, alias); } else { return 0; } } Choqok::UI::MicroBlogWidget *GNUSocialApiMicroBlog::createMicroBlogWidget(Choqok::Account *account, QWidget *parent) { return new TwitterApiMicroBlogWidget(account, parent); } Choqok::UI::TimelineWidget *GNUSocialApiMicroBlog::createTimelineWidget(Choqok::Account *account, const QString &timelineName, QWidget *parent) { return new TwitterApiTimelineWidget(account, timelineName, parent); } Choqok::UI::PostWidget *GNUSocialApiMicroBlog::createPostWidget(Choqok::Account *account, Choqok::Post *post, QWidget *parent) { return new GNUSocialApiPostWidget(account, post, parent); } Choqok::UI::ComposerWidget *GNUSocialApiMicroBlog::createComposerWidget(Choqok::Account *account, QWidget *parent) { return new GNUSocialApiComposerWidget(account, parent); } Choqok::Post *GNUSocialApiMicroBlog::readPost(Choqok::Account *account, const QVariantMap &var, Choqok::Post *post) { if (!post) { qCCritical(CHOQOK) << "post is NULL!"; return 0; } post = TwitterApiMicroBlog::readPost(account, var, post); if (var.contains(QLatin1String("external_url"))) { post->link = var[QLatin1String("external_url")].toString(); } else { QVariantMap userMap; if (var[QLatin1String("repeated")].toBool()) { userMap = var[QLatin1String("retweeted_status")].toMap()[QLatin1String("user")].toMap(); } else { userMap = var[QLatin1String("user")].toMap(); } const QUrl profileUrl = userMap[QLatin1String("statusnet_profile_url")].toUrl(); post->link = QStringLiteral("%1://%2/notice/%3").arg(profileUrl.scheme()).arg(profileUrl.host()).arg(post->postId); } return post; } QString GNUSocialApiMicroBlog::profileUrl(Choqok::Account *account, const QString &username) const { if (username.contains(QLatin1Char('@'))) { const QStringList lst = username.split(QLatin1Char('@'), QString::SkipEmptyParts); if (lst.count() == 2) { return QStringLiteral("https://%1/%2").arg(lst[1]).arg(lst[0]); } } TwitterApiAccount *acc = qobject_cast(account); if (acc) { return QString(acc->homepageUrl().toDisplayString() + QLatin1Char('/') + username) ; } else { return QString(); } } QString GNUSocialApiMicroBlog::postUrl(Choqok::Account *account, const QString &username, const QString &postId) const { Q_UNUSED(username) TwitterApiAccount *acc = qobject_cast(account); if (acc) { QUrl url(acc->homepageUrl()); url.setPath(url.path() + QStringLiteral("/notice/%1").arg(postId)); return url.toDisplayString(); } else { return QString(); } } TwitterApiSearch *GNUSocialApiMicroBlog::searchBackend() { if (!mSearchBackend) { mSearchBackend = new GNUSocialApiSearch(this); } return mSearchBackend; } void GNUSocialApiMicroBlog::createPostWithAttachment(Choqok::Account *theAccount, Choqok::Post *post, const QString &mediumToAttach) { if (mediumToAttach.isEmpty()) { TwitterApiMicroBlog::createPost(theAccount, post); } else { const QUrl picUrl = QUrl::fromUserInput(mediumToAttach); KIO::StoredTransferJob *picJob = KIO::storedGet(picUrl, KIO::Reload, KIO::HideProgressInfo); picJob->exec(); if (picJob->error()) { qCCritical(CHOQOK) << "Job error:" << picJob->errorString(); KMessageBox::detailedError(Choqok::UI::Global::mainWindow(), i18n("Uploading medium failed: cannot read the medium file."), picJob->errorString()); return; } const QByteArray picData = picJob->data(); if (picData.count() == 0) { qCCritical(CHOQOK) << "Cannot read the media file, please check if it exists."; KMessageBox::error(Choqok::UI::Global::mainWindow(), i18n("Uploading medium failed: cannot read the medium file.")); return; } ///Documentation: http://identi.ca/notice/17779990 TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url.setPath(url.path() + QStringLiteral("/statuses/update.%1").arg(format)); const QMimeDatabase db; QByteArray fileContentType = db.mimeTypeForUrl(picUrl).name().toUtf8(); QMap formdata; formdata[QLatin1String("status")] = post->content.toUtf8(); formdata[QLatin1String("in_reply_to_status_id")] = post->replyToPostId.toLatin1(); formdata[QLatin1String("source")] = QCoreApplication::applicationName().toLatin1(); QMap mediafile; mediafile[QLatin1String("name")] = "media"; mediafile[QLatin1String("filename")] = picUrl.fileName().toUtf8(); mediafile[QLatin1String("mediumType")] = fileContentType; mediafile[QLatin1String("medium")] = picData; QList< QMap > listMediafiles; listMediafiles.append(mediafile); QByteArray data = Choqok::MediaManager::createMultipartFormData(formdata, listMediafiles); KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo) ; if (!job) { qCCritical(CHOQOK) << "Cannot create a http POST request!"; return; } job->addMetaData(QStringLiteral("content-type"), QStringLiteral("Content-Type: multipart/form-data; boundary=AaB03x")); job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + - QLatin1String(authorizationHeader(account, url, QOAuth::POST))); + QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation))); mCreatePostMap[ job ] = post; mJobsAccount[job] = theAccount; connect(job, SIGNAL(result(KJob*)), SLOT(slotCreatePost(KJob*))); job->start(); } } QString GNUSocialApiMicroBlog::generateRepeatedByUserTooltip(const QString &username) { if (Choqok::AppearanceSettings::showRetweetsInChoqokWay()) { return i18n("Repeat of %1", username); } else { return i18n("Repeated by %1", username); } } QString GNUSocialApiMicroBlog::repeatQuestion() { return i18n("Repeat this notice?"); } void GNUSocialApiMicroBlog::listFriendsUsername(TwitterApiAccount *theAccount, bool active) { Q_UNUSED(active); friendsList.clear(); if (theAccount) { doRequestFriendsScreenName(theAccount, 1); } } QStringList GNUSocialApiMicroBlog::readFriendsScreenName(Choqok::Account *theAccount, const QByteArray &buffer) { QStringList list; const QJsonDocument json = QJsonDocument::fromJson(buffer); if (!json.isNull()) { for (const QJsonValue &u: json.array()) { const QJsonObject user = u.toObject(); if (user.contains(QStringLiteral("statusnet_profile_url"))) { list.append(user.value(QLatin1String("statusnet_profile_url")).toString()); } } } else { QString err = i18n("Retrieving the friends list failed. The data returned from the server is corrupted."); qCDebug(CHOQOK) << "JSON parse error:the buffer is: \n" << buffer; Q_EMIT error(theAccount, ParsingError, err, Critical); } return list; } void GNUSocialApiMicroBlog::requestFriendsScreenName(TwitterApiAccount *theAccount, bool active) { Q_UNUSED(active); doRequestFriendsScreenName(theAccount, 1); } void GNUSocialApiMicroBlog::showDirectMessageDialog(TwitterApiAccount *theAccount, const QString &toUsername) { qCDebug(CHOQOK); if (!theAccount) { QAction *act = qobject_cast(sender()); theAccount = qobject_cast(Choqok::AccountManager::self()->findAccount(act->data().toString())); } GNUSocialApiDMessageDialog *dmsg = new GNUSocialApiDMessageDialog(theAccount, Choqok::UI::Global::mainWindow()); if (!toUsername.isEmpty()) { dmsg->setTo(toUsername); } dmsg->show(); } void GNUSocialApiMicroBlog::doRequestFriendsScreenName(TwitterApiAccount *theAccount, int page) { qCDebug(CHOQOK); TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QStringLiteral("/statuses/friends.%1").arg(format)); - QOAuth::ParamMap params; + QVariantMap params; if (page > 1) { - params.insert("page", QByteArray::number(page)); + params.insert(QLatin1String("page"), QByteArray::number(page)); QUrlQuery urlQuery; urlQuery.addQueryItem(QLatin1String("page"), QString::number(page)); url.setQuery(urlQuery); } KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http GET request!"; return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + - QLatin1String(authorizationHeader(account, url, QOAuth::GET, params))); + QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::GetOperation, params))); mJobsAccount[job] = theAccount; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotRequestFriendsScreenName(KJob*))); job->start(); } void GNUSocialApiMicroBlog::slotRequestFriendsScreenName(KJob *job) { qCDebug(CHOQOK); TwitterApiAccount *theAccount = qobject_cast(mJobsAccount.take(job)); if (job->error()) { Q_EMIT error(theAccount, ServerError, i18n("Friends list for account %1 could not be updated:\n%2", theAccount->username(), job->errorString()), Normal); return; } KIO::StoredTransferJob *stJob = qobject_cast(job); QStringList newList = readFriendsScreenName(theAccount, stJob->data()); friendsList << newList; if (newList.count() == 100) { doRequestFriendsScreenName(theAccount, ++friendsPage); } else { friendsList.removeDuplicates(); theAccount->setFriendsList(friendsList); Q_EMIT friendsUsernameListed(theAccount, friendsList); } } /*QStringList GNUSocialApiMicroBlog::readUsersScreenNameFromXml(Choqok::Account* theAccount, const QByteArray& buffer) { qCDebug(CHOQOK); QStringList list; QDomDocument document; document.setContent( buffer ); QDomElement root = document.documentElement(); if ( root.tagName() != "users" ) { QString err = checkXmlForError(buffer); if(!err.isEmpty()){ Q_EMIT error(theAccount, ServerError, err, Critical); } else { err = i18n( "Retrieving the friends list failed. The data returned from the server is corrupted." ); qCDebug(CHOQOK) << "there's no users tag in XML\t the XML is: \n" << buffer; Q_EMIT error(theAccount, ParsingError, err, Critical); list<(theAccount); QUrl url = account->apiUrl(); url.setPath(QStringLiteral("/statusnet/conversation/%1.%2").arg(conversationId).arg(format)); KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http GET request!"; return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + - QLatin1String(authorizationHeader(account, url, QOAuth::GET))); + QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::GetOperation))); mFetchConversationMap[ job ] = conversationId; mJobsAccount[ job ] = theAccount; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotFetchConversation(KJob*))); job->start(); } QString GNUSocialApiMicroBlog::usernameFromProfileUrl(const QString &profileUrl) { // Remove the initial slash from path return QUrl(profileUrl).path().remove(0, 1); } QString GNUSocialApiMicroBlog::hostFromProfileUrl(const QString &profileUrl) { return QUrl(profileUrl).host(); } void GNUSocialApiMicroBlog::slotFetchConversation(KJob *job) { qCDebug(CHOQOK); if (!job) { qCWarning(CHOQOK) << "NULL Job returned"; return; } QList posts; QString conversationId = mFetchConversationMap.take(job); Choqok::Account *theAccount = mJobsAccount.take(job); if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT error(theAccount, Choqok::MicroBlog::CommunicationError, i18n("Fetching conversation failed. %1", job->errorString()), Normal); } else { KIO::StoredTransferJob *stj = qobject_cast (job); //if(format=="json"){ posts = readTimeline(theAccount, stj->data()); //} else { // posts = readTimelineFromXml ( theAccount, stj->data() ); //} if (!posts.isEmpty()) { Q_EMIT conversationFetched(theAccount, conversationId, posts); } } } #include "gnusocialapimicroblog.moc" diff --git a/helperlibs/twitterapihelper/CMakeLists.txt b/helperlibs/twitterapihelper/CMakeLists.txt index 065643ac..bf4a396d 100644 --- a/helperlibs/twitterapihelper/CMakeLists.txt +++ b/helperlibs/twitterapihelper/CMakeLists.txt @@ -1,63 +1,65 @@ include_directories( ${CHOQOK_INCLUDES} ) set(twitterhelper_LIB_SRCS twitterapiaccount.cpp twitterapicomposerwidget.cpp twitterapidebug.cpp twitterapipostwidget.cpp twitterapidmessagedialog.cpp twitterapimicroblog.cpp twitterapimicroblogwidget.cpp + twitterapioauth.cpp twitterapisearch.cpp twitterapisearchdialog.cpp twitterapisearchtimelinewidget.cpp twitterapishowthread.cpp twitterapitextedit.cpp twitterapitimelinewidget.cpp twitterapiwhoiswidget.cpp ) # kconfig_add_kcfg_files(twitterhelper_LIB_SRCS # twitterapisettings.kcfgc # ) add_library(twitterapihelper SHARED ${twitterhelper_LIB_SRCS}) target_link_libraries( twitterapihelper PUBLIC Qt5::Core Qt5::Gui + Qt5::NetworkAuth Qt5::Widgets KF5::ConfigCore KF5::KIOCore KF5::KIOWidgets KF5::I18n KF5::WidgetsAddons - ${QTOAUTH_LIBRARY} qca-qt5 choqok ) set_target_properties(twitterapihelper PROPERTIES VERSION 1.3.0 SOVERSION 1 ) install(TARGETS twitterapihelper ${INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES twitterapiaccount.h twitterapicomposerwidget.h twitterapidmessagedialog.h twitterapimicroblog.h twitterapimicroblogwidget.h + twitterapioauth.h twitterapipostwidget.h twitterapisearch.h twitterapisearchdialog.h twitterapisearchtimelinewidget.h twitterapishowthread.h twitterapitextedit.h twitterapitimelinewidget.h twitterapiwhoiswidget.h DESTINATION ${INCLUDE_INSTALL_DIR}/choqok/twitterapihelper COMPONENT Devel) diff --git a/helperlibs/twitterapihelper/twitterapiaccount.cpp b/helperlibs/twitterapihelper/twitterapiaccount.cpp index 117c9940..92023760 100644 --- a/helperlibs/twitterapihelper/twitterapiaccount.cpp +++ b/helperlibs/twitterapihelper/twitterapiaccount.cpp @@ -1,309 +1,306 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2008-2012 Mehrdad Momeny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #include "twitterapiaccount.h" -#include - #include #include "passwordmanager.h" #include "twitterapidebug.h" #include "twitterapimicroblog.h" +#include "twitterapioauth.h" class TwitterApiAccount::Private { public: Private() : api(QLatin1Char('/')), usingOauth(true), qoauth(0) {} QString userId; int count; QString host; QString api; QUrl apiUrl; QUrl homepageUrl; QStringList friendsList; QStringList followersList; QStringList timelineNames; QByteArray oauthToken; QByteArray oauthTokenSecret; QByteArray oauthConsumerKey; QByteArray oauthConsumerSecret; bool usingOauth; - QOAuth::Interface *qoauth; + TwitterApiOAuth *qoauth; }; TwitterApiAccount::TwitterApiAccount(TwitterApiMicroBlog *parent, const QString &alias) : Account(parent, alias), d(new Private) { qCDebug(CHOQOK); d->usingOauth = configGroup()->readEntry("UsingOAuth", false); d->userId = configGroup()->readEntry("UserId", QString()); d->count = configGroup()->readEntry("CountOfPosts", 20); d->host = configGroup()->readEntry("Host", QString()); d->friendsList = configGroup()->readEntry("Friends", QStringList()); d->friendsList = configGroup()->readEntry("Followers", QStringList()); d->timelineNames = configGroup()->readEntry("Timelines", QStringList()); d->oauthToken = configGroup()->readEntry("OAuthToken", QByteArray()); d->oauthConsumerKey = configGroup()->readEntry("OAuthConsumerKey", QByteArray()); d->oauthConsumerSecret = Choqok::PasswordManager::self()->readPassword( QStringLiteral("%1_consumerSecret").arg(alias)).toUtf8(); d->oauthTokenSecret = Choqok::PasswordManager::self()->readPassword( QStringLiteral("%1_tokenSecret").arg(alias)).toUtf8(); setApi(configGroup()->readEntry(QLatin1String("Api"), QString::fromLatin1("/"))); qCDebug(CHOQOK) << "UsingOAuth:" << d->usingOauth; if (d->usingOauth) { initQOAuthInterface(); } if (d->timelineNames.isEmpty()) { QStringList list = parent->timelineNames(); list.removeOne(QLatin1String("Public")); list.removeOne(QLatin1String("Favorite")); list.removeOne(QLatin1String("ReTweets")); d->timelineNames = list; } if (d->friendsList.isEmpty()) { parent->listFriendsUsername(this); //Result will set on TwitterApiMicroBlog! } setPostCharLimit(140); //TODO: See if we can ask twitter for the char limit and make it dynamic } TwitterApiAccount::~TwitterApiAccount() { if(d->qoauth) d->qoauth->deleteLater(); delete d; } void TwitterApiAccount::writeConfig() { configGroup()->writeEntry("UsingOAuth", d->usingOauth); configGroup()->writeEntry("UserId", d->userId); configGroup()->writeEntry("CountOfPosts", d->count); configGroup()->writeEntry("Host", d->host); configGroup()->writeEntry("Api", d->api); configGroup()->writeEntry("Friends", d->friendsList); configGroup()->writeEntry("Followers", d->followersList); configGroup()->writeEntry("Timelines", d->timelineNames); configGroup()->writeEntry("OAuthToken", d->oauthToken); configGroup()->writeEntry("OAuthConsumerKey", d->oauthConsumerKey); Choqok::PasswordManager::self()->writePassword(QStringLiteral("%1_consumerSecret").arg(alias()), QString::fromUtf8(d->oauthConsumerSecret)); Choqok::PasswordManager::self()->writePassword(QStringLiteral("%1_tokenSecret").arg(alias()), QString::fromUtf8(d->oauthTokenSecret)); Choqok::Account::writeConfig(); } QString TwitterApiAccount::userId() const { return d->userId; } void TwitterApiAccount::setUserId(const QString &id) { d->userId = id; } int TwitterApiAccount::countOfPosts() const { return d->count; } void TwitterApiAccount::setCountOfPosts(int count) { d->count = count; } QUrl TwitterApiAccount::apiUrl() const { return d->apiUrl; } QString TwitterApiAccount::host() const { return d->host; } void TwitterApiAccount::setApiUrl(const QUrl &apiUrl) { d->apiUrl = apiUrl; } QString TwitterApiAccount::api() const { return d->api; } void TwitterApiAccount::setApi(const QString &api) { d->api = api; generateApiUrl(); } void TwitterApiAccount::setHost(const QString &host) { d->host = host; generateApiUrl(); } QUrl TwitterApiAccount::homepageUrl() const { return d->homepageUrl; } void TwitterApiAccount::generateApiUrl() { if (!host().startsWith(QLatin1String("http"))) { //NOTE: This is for compatibility by prev versions. remove it after 1.0 release setHost(host().prepend(QLatin1String("http://"))); } QUrl url(host()); setHomepageUrl(url); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QLatin1Char('/') + (api())); setApiUrl(url); } void TwitterApiAccount::setHomepageUrl(const QUrl &homepageUrl) { d->homepageUrl = homepageUrl; } QStringList TwitterApiAccount::friendsList() const { return d->friendsList; } void TwitterApiAccount::setFriendsList(const QStringList &list) { d->friendsList = list; writeConfig(); } QStringList TwitterApiAccount::followersList() const { return d->followersList; } void TwitterApiAccount::setFollowersList(const QStringList& list) { d->followersList = list; writeConfig(); } QStringList TwitterApiAccount::timelineNames() const { return d->timelineNames; } void TwitterApiAccount::setTimelineNames(const QStringList &list) { d->timelineNames.clear(); for (const QString &name: list) { if (microblog()->timelineNames().contains(name)) { d->timelineNames << name; } } } QByteArray TwitterApiAccount::oauthToken() const { return d->oauthToken; } void TwitterApiAccount::setOauthToken(const QByteArray &token) { d->oauthToken = token; } QByteArray TwitterApiAccount::oauthTokenSecret() const { return d->oauthTokenSecret; } void TwitterApiAccount::setOauthTokenSecret(const QByteArray &tokenSecret) { d->oauthTokenSecret = tokenSecret; } QByteArray TwitterApiAccount::oauthConsumerKey() const { return d->oauthConsumerKey; } void TwitterApiAccount::setOauthConsumerKey(const QByteArray &consumerKey) { d->oauthConsumerKey = consumerKey; } QByteArray TwitterApiAccount::oauthConsumerSecret() const { return d->oauthConsumerSecret; } void TwitterApiAccount::setOauthConsumerSecret(const QByteArray &consumerSecret) { d->oauthConsumerSecret = consumerSecret; } bool TwitterApiAccount::usingOAuth() const { return d->usingOauth; } void TwitterApiAccount::setUsingOAuth(bool use) { if (use) { initQOAuthInterface(); } else { delete d->qoauth; d->qoauth = nullptr; } d->usingOauth = use; } -QOAuth::Interface *TwitterApiAccount::oauthInterface() +TwitterApiOAuth *TwitterApiAccount::oauthInterface() { return d->qoauth; } void TwitterApiAccount::initQOAuthInterface() { qCDebug(CHOQOK); if (!d->qoauth) { - d->qoauth = new QOAuth::Interface(new KIO::AccessManager(this), this); + d->qoauth = new TwitterApiOAuth(this); } - d->qoauth->setConsumerKey(d->oauthConsumerKey); - d->qoauth->setConsumerSecret(d->oauthConsumerSecret); - d->qoauth->setRequestTimeout(20000); - d->qoauth->setIgnoreSslErrors(true); + d->qoauth->setToken(QLatin1String(d->oauthToken)); + d->qoauth->setTokenSecret(QLatin1String(d->oauthTokenSecret)); } diff --git a/helperlibs/twitterapihelper/twitterapiaccount.h b/helperlibs/twitterapihelper/twitterapiaccount.h index 592a3d90..a6441566 100644 --- a/helperlibs/twitterapihelper/twitterapiaccount.h +++ b/helperlibs/twitterapihelper/twitterapiaccount.h @@ -1,115 +1,111 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2008-2012 Mehrdad Momeny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #ifndef TWITTERAPIACCOUNT_H #define TWITTERAPIACCOUNT_H #include #include "account.h" #include "choqok_export.h" #include "twitterapimicroblog.h" - -namespace QOAuth -{ -class Interface; -} +#include "twitterapioauth.h" /** @author Mehrdad Momeny \ */ class CHOQOK_HELPER_EXPORT TwitterApiAccount : public Choqok::Account { Q_OBJECT public: TwitterApiAccount(TwitterApiMicroBlog *parent, const QString &alias); ~TwitterApiAccount(); virtual void writeConfig() override; QString userId() const; void setUserId(const QString &id); int countOfPosts() const; void setCountOfPosts(int count); QString host() const; void setHost(const QString &host); /** @return api path It's defer from apiUrl. For example: in http://identi.ca/api/ identi.ca is @ref host() api is @ref api() http://identi.ca/api/ is @ref apiUrl() */ QString api() const; virtual void setApi(const QString &api); /** Combined from @ref host and @ref api to use for connections and queries */ QUrl apiUrl() const; virtual QUrl homepageUrl() const; QStringList friendsList() const; void setFriendsList(const QStringList &list); QStringList followersList() const; void setFollowersList(const QStringList &list); virtual QStringList timelineNames() const override; virtual void setTimelineNames(const QStringList &list); QByteArray oauthToken() const; void setOauthToken(const QByteArray &token); QByteArray oauthTokenSecret() const; void setOauthTokenSecret(const QByteArray &tokenSecret); QByteArray oauthConsumerKey() const; void setOauthConsumerKey(const QByteArray &consumerKey); QByteArray oauthConsumerSecret() const; void setOauthConsumerSecret(const QByteArray &consumerSecret); bool usingOAuth() const; void setUsingOAuth(bool use = true); - QOAuth::Interface *oauthInterface(); + TwitterApiOAuth *oauthInterface(); protected: void setApiUrl(const QUrl &apiUrl); void setHomepageUrl(const QUrl &homepageUrl); void generateApiUrl(); void initQOAuthInterface(); private: class Private; Private *const d; }; #endif // TWITTERACCOUNT_H diff --git a/helperlibs/twitterapihelper/twitterapimicroblog.cpp b/helperlibs/twitterapihelper/twitterapimicroblog.cpp index bd37a6e8..129a3bf7 100644 --- a/helperlibs/twitterapihelper/twitterapimicroblog.cpp +++ b/helperlibs/twitterapihelper/twitterapimicroblog.cpp @@ -1,1605 +1,1601 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2008-2012 Mehrdad Momeny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #include "twitterapimicroblog.h" #include #include #include #include #include #include #include -#include - #include "account.h" #include "accountmanager.h" #include "application.h" #include "choqokappearancesettings.h" #include "choqokbehaviorsettings.h" #include "choqokuiglobal.h" #include "editaccountwidget.h" #include "microblogwidget.h" #include "notifymanager.h" #include "postwidget.h" #include "timelinewidget.h" #include "twitterapiaccount.h" #include "twitterapicomposerwidget.h" #include "twitterapidebug.h" #include "twitterapidmessagedialog.h" #include "twitterapipostwidget.h" #include "twitterapisearch.h" #include "twitterapisearchdialog.h" #include "twitterapisearchtimelinewidget.h" class TwitterApiMicroBlog::Private { public: Private(): countOfTimelinesToSave(0), friendsCursor(QLatin1String("-1")) { monthes[QLatin1String("Jan")] = 1; monthes[QLatin1String("Feb")] = 2; monthes[QLatin1String("Mar")] = 3; monthes[QLatin1String("Apr")] = 4; monthes[QLatin1String("May")] = 5; monthes[QLatin1String("Jun")] = 6; monthes[QLatin1String("Jul")] = 7; monthes[QLatin1String("Aug")] = 8; monthes[QLatin1String("Sep")] = 9; monthes[QLatin1String("Oct")] = 10; monthes[QLatin1String("Nov")] = 11; monthes[QLatin1String("Dec")] = 12; } int countOfTimelinesToSave; QString friendsCursor; QString followersCursor; QMap monthes; }; TwitterApiMicroBlog::TwitterApiMicroBlog(const QString &componentName, QObject *parent) : MicroBlog(componentName, parent), d(new Private) { qCDebug(CHOQOK); format = QLatin1String("json"); QStringList timelineTypes; timelineTypes << QLatin1String("Home") << QLatin1String("Reply") << QLatin1String("Inbox") << QLatin1String("Outbox") << QLatin1String("Favorite") << QLatin1String("ReTweets") << QLatin1String("Public"); setTimelineNames(timelineTypes); timelineApiPath[QLatin1String("Home")] = QLatin1String("/statuses/home_timeline.%1"); timelineApiPath[QLatin1String("Reply")] = QLatin1String("/statuses/replies.%1"); timelineApiPath[QLatin1String("Inbox")] = QLatin1String("/direct_messages.%1"); timelineApiPath[QLatin1String("Outbox")] = QLatin1String("/direct_messages/sent.%1"); timelineApiPath[QLatin1String("Favorite")] = QLatin1String("/favorites/list.%1"); timelineApiPath[QLatin1String("ReTweets")] = QLatin1String("/statuses/retweets_of_me.%1"); timelineApiPath[QLatin1String("Public")] = QLatin1String("/statuses/public_timeline.%1"); setTimelineInfos(); } void TwitterApiMicroBlog::setTimelineInfos() { Choqok::TimelineInfo *t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "Home"); t->description = i18nc("Timeline description", "You and your friends"); t->icon = QLatin1String("user-home"); mTimelineInfos[QLatin1String("Home")] = std::move(t); t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "Reply"); t->description = i18nc("Timeline description", "Replies to you"); t->icon = QLatin1String("edit-undo"); mTimelineInfos[QLatin1String("Reply")] = std::move(t); t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "Inbox"); t->description = i18nc("Timeline description", "Your incoming private messages"); t->icon = QLatin1String("mail-folder-inbox"); mTimelineInfos[QLatin1String("Inbox")] = std::move(t); t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "Outbox"); t->description = i18nc("Timeline description", "Private messages you have sent"); t->icon = QLatin1String("mail-folder-outbox"); mTimelineInfos[QLatin1String("Outbox")] = std::move(t); t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "Favorite"); t->description = i18nc("Timeline description", "Your favorites"); t->icon = QLatin1String("favorites"); mTimelineInfos[QLatin1String("Favorite")] = std::move(t); t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "Public"); t->description = i18nc("Timeline description", "Public timeline"); t->icon = QLatin1String("folder-green"); mTimelineInfos[QLatin1String("Public")] = std::move(t); t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "ReTweets"); t->description = i18nc("Timeline description", "Your posts that were ReTweeted by others"); t->icon = QLatin1String("folder-red"); mTimelineInfos[QLatin1String("ReTweets")] = std::move(t); } TwitterApiMicroBlog::~TwitterApiMicroBlog() { qDeleteAll(mTimelineInfos); delete d; } QMenu *TwitterApiMicroBlog::createActionsMenu(Choqok::Account *theAccount, QWidget *parent) { QMenu *menu = MicroBlog::createActionsMenu(theAccount, parent); QAction *directMessge = new QAction(QIcon::fromTheme(QLatin1String("mail-message-new")), i18n("Send Private Message..."), menu); directMessge->setData(theAccount->alias()); connect(directMessge, SIGNAL(triggered(bool)), SLOT(showDirectMessageDialog())); menu->addAction(directMessge); QAction *search = new QAction(QIcon::fromTheme(QLatin1String("edit-find")), i18n("Search..."), menu); search->setData(theAccount->alias()); connect(search, SIGNAL(triggered(bool)), SLOT(showSearchDialog())); menu->addAction(search); QAction *updateFriendsList = new QAction(QIcon::fromTheme(QLatin1String("arrow-down")), i18n("Update Friends List"), menu); updateFriendsList->setData(theAccount->alias()); connect(updateFriendsList, SIGNAL(triggered(bool)), SLOT(slotUpdateFriendsList())); menu->addAction(updateFriendsList); return menu; } QList< Choqok::Post * > TwitterApiMicroBlog::loadTimeline(Choqok::Account *account, const QString &timelineName) { QList< Choqok::Post * > list; if (timelineName.compare(QLatin1String("Favorite")) == 0) { return list; //NOTE Won't cache favorites, and this is for compatibility with older versions! } qCDebug(CHOQOK) << timelineName; QString fileName = Choqok::AccountManager::generatePostBackupFileName(account->alias(), timelineName); KConfig postsBackup(fileName, KConfig::NoGlobals, QStandardPaths::DataLocation); QStringList tmpList = postsBackup.groupList(); /// to don't load old archives if (tmpList.isEmpty() || !(QDateTime::fromString(tmpList.first()).isValid())) { return list; } ///-------------- QList groupList; for (const QString &str: tmpList) { groupList.append(QDateTime::fromString(str)); } qSort(groupList); int count = groupList.count(); if (count) { Choqok::Post *st = 0; for (int i = 0; i < count; ++i) { st = new Choqok::Post; KConfigGroup grp(&postsBackup, groupList[i].toString()); st->creationDateTime = grp.readEntry("creationDateTime", QDateTime::currentDateTime()); st->postId = grp.readEntry("postId", QString()); st->content = grp.readEntry("text", QString()); st->source = grp.readEntry("source", QString()); st->replyToPostId = grp.readEntry("inReplyToPostId", QString()); st->replyToUserId = grp.readEntry("inReplyToUserId", QString()); st->isFavorited = grp.readEntry("favorited", false); st->replyToUserName = grp.readEntry("inReplyToUserName", QString()); st->author.userId = grp.readEntry("authorId", QString()); st->author.userName = grp.readEntry("authorUserName", QString()); st->author.realName = grp.readEntry("authorRealName", QString()); st->author.profileImageUrl = grp.readEntry("authorProfileImageUrl", QString()); st->author.description = grp.readEntry("authorDescription" , QString()); st->author.isProtected = grp.readEntry("isProtected", false); st->isPrivate = grp.readEntry("isPrivate" , false); st->author.location = grp.readEntry("authorLocation", QString()); st->link = postUrl(account, st->author.userName, st->postId); st->isRead = grp.readEntry("isRead", true); st->repeatedFromUsername = grp.readEntry("repeatedFrom", QString()); st->repeatedPostId = grp.readEntry("repeatedPostId", QString()); st->repeatedDateTime = grp.readEntry("repeatedDateTime", QDateTime()); st->conversationId = grp.readEntry("conversationId", QString()); st->media = grp.readEntry("mediaUrl", QString()); st->quotedPost.postId = grp.readEntry("quotedPostId", QString()); st->quotedPost.profileImageUrl = grp.readEntry("quotedProfileUrl", QString()); st->quotedPost.content = grp.readEntry("quotedContent", QString()); st->quotedPost.username = grp.readEntry("quotedUsername", QString()); list.append(st); } mTimelineLatestId[account][timelineName] = st->postId; } return list; } void TwitterApiMicroBlog::saveTimeline(Choqok::Account *account, const QString &timelineName, const QList< Choqok::UI::PostWidget * > &timeline) { if (timelineName.compare(QLatin1String("Favorite")) != 0) { qCDebug(CHOQOK); QString fileName = Choqok::AccountManager::generatePostBackupFileName(account->alias(), timelineName); KConfig postsBackup(fileName, KConfig::NoGlobals, QStandardPaths::DataLocation); ///Clear previous data: for (const QString &group: postsBackup.groupList()) { postsBackup.deleteGroup(group); } for (Choqok::UI::PostWidget *wd: timeline) { const Choqok::Post *post = (wd->currentPost()); KConfigGroup grp(&postsBackup, post->creationDateTime.toString()); grp.writeEntry("creationDateTime", post->creationDateTime); grp.writeEntry("postId", post->postId); grp.writeEntry("text", post->content); grp.writeEntry("source", post->source); grp.writeEntry("inReplyToPostId", post->replyToPostId); grp.writeEntry("inReplyToUserId", post->replyToUserId); grp.writeEntry("favorited", post->isFavorited); grp.writeEntry("inReplyToUserName", post->replyToUserName); grp.writeEntry("authorId", post->author.userId); grp.writeEntry("authorUserName", post->author.userName); grp.writeEntry("authorRealName", post->author.realName); grp.writeEntry("authorProfileImageUrl", post->author.profileImageUrl); grp.writeEntry("authorDescription" , post->author.description); grp.writeEntry("isPrivate" , post->isPrivate); grp.writeEntry("authorLocation" , post->author.location); grp.writeEntry("isProtected" , post->author.isProtected); grp.writeEntry("isRead" , post->isRead); grp.writeEntry("repeatedFrom", post->repeatedFromUsername); grp.writeEntry("repeatedPostId", post->repeatedPostId); grp.writeEntry("repeatedDateTime", post->repeatedDateTime); grp.writeEntry("conversationId", post->conversationId); grp.writeEntry("mediaUrl", post->media); grp.writeEntry("quotedPostId", post->quotedPost.postId); grp.writeEntry("quotedProfileUrl", post->quotedPost.profileImageUrl); grp.writeEntry("quotedContent", post->quotedPost.content); grp.writeEntry("quotedUsername", post->quotedPost.username); } postsBackup.sync(); } if (Choqok::Application::isShuttingDown()) { --d->countOfTimelinesToSave; if (d->countOfTimelinesToSave < 1) { Q_EMIT readyForUnload(); } } } Choqok::UI::ComposerWidget *TwitterApiMicroBlog::createComposerWidget(Choqok::Account *account, QWidget *parent) { return new TwitterApiComposerWidget(account, parent); } TwitterApiSearchTimelineWidget *TwitterApiMicroBlog::createSearchTimelineWidget(Choqok::Account *theAccount, QString name, const SearchInfo &info, QWidget *parent) { return new TwitterApiSearchTimelineWidget(theAccount, name, info, parent); } void TwitterApiMicroBlog::createPost(Choqok::Account *theAccount, Choqok::Post *post) { qCDebug(CHOQOK); TwitterApiAccount *account = qobject_cast(theAccount); QByteArray data; - QOAuth::ParamMap params; + QVariantMap params; if (!post || post->content.isEmpty()) { qCDebug(CHOQOK) << "ERROR: Status text is empty!"; Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::OtherError, i18n("Creating the new post failed. Text is empty."), MicroBlog::Critical); return; } if (!post->isPrivate) { ///Status Update QUrl url = account->apiUrl(); url.setPath(url.path() + QStringLiteral("/statuses/update.%1").arg(format)); - params.insert("status", QUrl::toPercentEncoding(post->content)); + params.insert(QLatin1String("status"), QUrl::toPercentEncoding(post->content)); if (!post->replyToPostId.isEmpty()) { - params.insert("in_reply_to_status_id", post->replyToPostId.toLocal8Bit()); + params.insert(QLatin1String("in_reply_to_status_id"), post->replyToPostId.toLatin1()); } data = "status="; data += QUrl::toPercentEncoding(post->content); if (!post->replyToPostId.isEmpty()) { data += "&in_reply_to_status_id="; - data += post->replyToPostId.toLocal8Bit(); + data += post->replyToPostId.toLatin1(); } if (!account->usingOAuth()) { data += "&source=Choqok"; } KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http POST request!"; return; } job->addMetaData(QStringLiteral("content-type"), QStringLiteral("Content-Type: application/x-www-form-urlencoded")); job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + - QLatin1String(authorizationHeader(account, url, QOAuth::POST, params))); + QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation, params))); mCreatePostMap[ job ] = post; mJobsAccount[job] = theAccount; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotCreatePost(KJob*))); job->start(); } else {///Direct message QString recipientScreenName = post->replyToUserName; QUrl url = account->apiUrl(); url.setPath(url.path() + QStringLiteral("/direct_messages/new.%1").arg(format)); - params.insert("user", recipientScreenName.toLocal8Bit()); - params.insert("text", QUrl::toPercentEncoding(post->content)); + params.insert(QLatin1String("user"), recipientScreenName.toLocal8Bit()); + params.insert(QLatin1String("text"), QUrl::toPercentEncoding(post->content)); data = "user="; data += recipientScreenName.toLocal8Bit(); data += "&text="; data += QUrl::toPercentEncoding(post->content); if (!account->usingOAuth()) { data += "&source=Choqok"; } KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http POST request!"; // QString errMsg = i18n ( "Creating the new post failed. Cannot create an http POST request. Please check your KDE installation." ); // emit errorPost ( theAccount, post, Choqok::MicroBlog::OtherError, errMsg, MicroBlog::Critical ); return; } job->addMetaData(QStringLiteral("content-type"), QStringLiteral("Content-Type: application/x-www-form-urlencoded")); job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") - + QLatin1String(authorizationHeader(account, url, QOAuth::POST, params))); + + QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation, params))); mCreatePostMap[ job ] = post; mJobsAccount[job] = theAccount; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotCreatePost(KJob*))); job->start(); } } void TwitterApiMicroBlog::repeatPost(Choqok::Account *theAccount, const QString &postId) { qCDebug(CHOQOK); if (postId.isEmpty()) { qCCritical(CHOQOK) << "ERROR: PostId is empty!"; return; } TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url.setPath(url.path() + QStringLiteral("/statuses/retweet/%1.%2").arg(postId).arg(format)); QByteArray data; KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http POST request!"; return; } job->addMetaData(QStringLiteral("content-type"), QStringLiteral("Content-Type: application/x-www-form-urlencoded")); job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + - QLatin1String(authorizationHeader(account, url, QOAuth::POST))); + QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation))); Choqok::Post *post = new Choqok::Post; post->postId = postId; mCreatePostMap[ job ] = post; mJobsAccount[job] = theAccount; connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotCreatePost); job->start(); } void TwitterApiMicroBlog::slotCreatePost(KJob *job) { qCDebug(CHOQOK); if (!job) { qCDebug(CHOQOK) << "Job is null pointer"; return; } Choqok::Post *post = mCreatePostMap.take(job); Choqok::Account *theAccount = mJobsAccount.take(job); if (!post || !theAccount) { qCDebug(CHOQOK) << "Account or Post is NULL pointer"; return; } if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::CommunicationError, i18n("Creating the new post failed: %1", job->errorString()), MicroBlog::Critical); } else { KIO::StoredTransferJob *stj = qobject_cast< KIO::StoredTransferJob * > (job); if (!post->isPrivate) { readPost(theAccount, stj->data(), post); if (post->isError) { QString errorMsg; errorMsg = checkForError(stj->data()); 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 qCCritical(CHOQOK) << "Creating post: JSON parsing error:" << stj->data() ; Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::ParsingError, i18n("Creating the new post failed. The result data could not be parsed."), MicroBlog::Critical); } else { qCCritical(CHOQOK) << "Server Error:" << errorMsg ; Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::ServerError, i18n("Creating the new post failed, with error: %1", errorMsg), MicroBlog::Critical); } } else { Choqok::NotifyManager::success(i18n("New post submitted successfully")); Q_EMIT postCreated(theAccount, post); } } else { Choqok::NotifyManager::success(i18n("Private message sent successfully")); Q_EMIT postCreated(theAccount, post); } } } void TwitterApiMicroBlog::abortAllJobs(Choqok::Account *theAccount) { for (KJob *job: mJobsAccount.keys(theAccount)) { job->kill(KJob::EmitResult); } } void TwitterApiMicroBlog::abortCreatePost(Choqok::Account *theAccount, Choqok::Post *post) { if (mCreatePostMap.isEmpty()) { return; } if (post) { mCreatePostMap.key(post)->kill(KJob::EmitResult); } else { for (KJob *job: mCreatePostMap.keys()) { if (mJobsAccount[job] == theAccount) { job->kill(KJob::EmitResult); } } } } void TwitterApiMicroBlog::fetchPost(Choqok::Account *theAccount, Choqok::Post *post) { qCDebug(CHOQOK); if (!post || post->postId.isEmpty()) { return; } TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url.setPath(url.path() + QStringLiteral("/statuses/show/%1.%2").arg(post->postId).arg(format)); KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http GET request!"; // QString errMsg = i18n ( "Fetching the new post failed. Cannot create an HTTP GET request." // "Please check your KDE installation." ); // emit errorPost ( theAccount, post, Choqok::MicroBlog::OtherError, errMsg, Low ); return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + - QLatin1String(authorizationHeader(account, url, QOAuth::GET))); + QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::GetOperation))); mFetchPostMap[ job ] = post; mJobsAccount[ job ] = theAccount; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotFetchPost(KJob*))); job->start(); } void TwitterApiMicroBlog::slotFetchPost(KJob *job) { qCDebug(CHOQOK); if (!job) { qCWarning(CHOQOK) << "NULL Job returned"; return; } Choqok::Post *post = mFetchPostMap.take(job); Choqok::Account *theAccount = mJobsAccount.take(job); if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT error(theAccount, Choqok::MicroBlog::CommunicationError, i18n("Fetching the new post failed. %1", job->errorString()), Low); } else { KIO::StoredTransferJob *stj = qobject_cast (job); readPost(theAccount, stj->data(), post); if (post->isError) { QString errorMsg; errorMsg = checkForError(stj->data()); if (errorMsg.isEmpty()) { qCDebug(CHOQOK) << "Parsing Error"; Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::ParsingError, i18n("Fetching new post failed. The result data could not be parsed."), Low); } else { qCCritical(CHOQOK) << "Fetching post: Server Error:" << errorMsg; Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::ServerError, i18n("Fetching new post failed, with error:%1", errorMsg), Low); } } else { post->isError = true; Q_EMIT postFetched(theAccount, post); } } } void TwitterApiMicroBlog::removePost(Choqok::Account *theAccount, Choqok::Post *post) { qCDebug(CHOQOK); if (!post->postId.isEmpty()) { TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); if (!post->isPrivate) { url.setPath(url.path() + QStringLiteral("/statuses/destroy/%1.%2").arg(post->postId).arg(format)); } else { url.setPath(url.path() + QStringLiteral("/direct_messages/destroy/%1.%2").arg(post->postId).arg(format)); } KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http POST request!"; // QString errMsg = i18n ( "Removing the post failed. Cannot create an HTTP POST request. Please check your KDE installation." ); // emit errorPost ( theAccount, post, Choqok::MicroBlog::OtherError, errMsg, MicroBlog::Critical ); return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + - QLatin1String(authorizationHeader(account, url, QOAuth::POST))); + QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation))); mRemovePostMap[job] = post; mJobsAccount[job] = theAccount; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotRemovePost(KJob*))); job->start(); } } void TwitterApiMicroBlog::slotRemovePost(KJob *job) { qCDebug(CHOQOK); if (!job) { qCDebug(CHOQOK) << "Job is null pointer."; return; } Choqok::Post *post = mRemovePostMap.take(job); Choqok::Account *theAccount = mJobsAccount.take(job); if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT errorPost(theAccount, post, CommunicationError, i18n("Removing the post failed. %1", job->errorString()), MicroBlog::Critical); } else { KIO::StoredTransferJob *stj = qobject_cast(job); QString errMsg = checkForError(stj->data()); if (errMsg.isEmpty()) { Q_EMIT postRemoved(theAccount, post); } else { qCCritical(CHOQOK) << "Server error on removing post:" << errMsg; Q_EMIT errorPost(theAccount, post, ServerError, i18n("Removing the post failed. %1", errMsg), MicroBlog::Critical); } } } void TwitterApiMicroBlog::createFavorite(Choqok::Account *theAccount, const QString &postId) { qCDebug(CHOQOK); TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url.setPath(url.path() + QStringLiteral("/favorites/create.%1").arg(format)); QUrl tmp(url); QUrlQuery urlQuery; urlQuery.addQueryItem(QLatin1String("id"), postId); url.setQuery(urlQuery); - QOAuth::ParamMap params; - params.insert("id", postId.toLatin1()); + QVariantMap params; + params.insert(QLatin1String("id"), postId.toLatin1()); KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http POST request!"; // QString errMsg = i18n ( "The Favorite creation failed. Cannot create an http POST request. " // "Please check your KDE installation." ); // emit error ( theAccount, OtherError, errMsg ); return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + - QLatin1String(authorizationHeader(account, tmp, QOAuth::POST, params))); + QLatin1String(authorizationHeader(account, tmp, QNetworkAccessManager::PostOperation, params))); mFavoriteMap[job] = postId; mJobsAccount[job] = theAccount; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotCreateFavorite(KJob*))); job->start(); } void TwitterApiMicroBlog::slotCreateFavorite(KJob *job) { qCDebug(CHOQOK); if (!job) { qCDebug(CHOQOK) << "Job is null pointer."; return; } Choqok::Account *theAccount = mJobsAccount.take(job); QString postId = mFavoriteMap.take(job); if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT error(theAccount, CommunicationError, i18n("Favorite creation failed. %1", job->errorString())); } else { KIO::StoredTransferJob *stJob = qobject_cast(job); QString err = checkForError(stJob->data()); if (!err.isEmpty()) { Q_EMIT error(theAccount, ServerError, err, Critical); return; } else { Q_EMIT favoriteCreated(theAccount, postId); } } } void TwitterApiMicroBlog::removeFavorite(Choqok::Account *theAccount, const QString &postId) { qCDebug(CHOQOK); TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url.setPath(url.path() + QStringLiteral("/favorites/destroy.%1").arg(format)); QUrl tmp(url); QUrlQuery tmpUrlQuery; tmpUrlQuery.addQueryItem(QLatin1String("id"), postId); url.setQuery(tmpUrlQuery); - QOAuth::ParamMap params; - params.insert("id", postId.toLatin1()); + QVariantMap params; + params.insert(QLatin1String("id"), postId.toLatin1()); KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http POST request!"; // QString errMsg = i18n ( "Removing the favorite failed. Cannot create an http POST request. " // "Please check your KDE installation." ); // emit error ( theAccount, OtherError, errMsg ); return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + - QLatin1String(authorizationHeader(account, tmp, QOAuth::POST, params))); + QLatin1String(authorizationHeader(account, tmp, QNetworkAccessManager::PostOperation, params))); mFavoriteMap[job] = postId; mJobsAccount[job] = theAccount; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotRemoveFavorite(KJob*))); job->start(); } void TwitterApiMicroBlog::slotRemoveFavorite(KJob *job) { qCDebug(CHOQOK); if (!job) { qCDebug(CHOQOK) << "Job is null pointer."; return; } QString id = mFavoriteMap.take(job); Choqok::Account *theAccount = mJobsAccount.take(job); if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT error(theAccount, CommunicationError, i18n("Removing the favorite failed. %1", job->errorString())); } else { KIO::StoredTransferJob *stJob = qobject_cast(job); QString err = checkForError(stJob->data()); if (!err.isEmpty()) { Q_EMIT error(theAccount, ServerError, err, Critical); return; } else { Q_EMIT favoriteRemoved(theAccount, id); } } } void TwitterApiMicroBlog::listFriendsUsername(TwitterApiAccount *theAccount, bool active) { friendsList.clear(); d->friendsCursor = QLatin1String("-1"); if (theAccount) { requestFriendsScreenName(theAccount, active); } } void TwitterApiMicroBlog::requestFriendsScreenName(TwitterApiAccount *theAccount, bool active) { qCDebug(CHOQOK); TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + (QStringLiteral("/friends/list.%1").arg(format))); QUrl tmpUrl(url); QUrlQuery urlQuery; urlQuery.addQueryItem(QLatin1String("cursor"), d->friendsCursor); urlQuery.addQueryItem(QLatin1String("count"), QLatin1String("200")); url.setQuery(urlQuery); - QOAuth::ParamMap params; - params.insert("cursor", d->friendsCursor.toLatin1()); - params.insert("count", QStringLiteral("200").toLatin1()); + QVariantMap params; + params.insert(QLatin1String("cursor"), d->friendsCursor.toLatin1()); + params.insert(QLatin1String("count"), QByteArray::number(200)); KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http GET request!"; return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + - QLatin1String(authorizationHeader(account, tmpUrl, QOAuth::GET, params))); + QLatin1String(authorizationHeader(account, tmpUrl, QNetworkAccessManager::GetOperation, params))); mJobsAccount[job] = theAccount; if (active) { connect(job, SIGNAL(result(KJob*)), this, SLOT(slotRequestFriendsScreenNameActive(KJob*))); } else { connect(job, SIGNAL(result(KJob*)), this, SLOT(slotRequestFriendsScreenNamePassive(KJob*))); } job->start(); Choqok::UI::Global::mainWindow()->showStatusMessage(i18n("Updating friends list for account %1...", theAccount->username())); } void TwitterApiMicroBlog::slotRequestFriendsScreenNameActive(KJob *job) { finishRequestFriendsScreenName(job, true); } void TwitterApiMicroBlog::slotRequestFriendsScreenNamePassive(KJob *job) { finishRequestFriendsScreenName(job, false); } void TwitterApiMicroBlog::finishRequestFriendsScreenName(KJob *job, bool active) { qCDebug(CHOQOK); TwitterApiAccount *theAccount = qobject_cast(mJobsAccount.take(job)); KIO::StoredTransferJob *stJob = qobject_cast(job); Choqok::MicroBlog::ErrorLevel level = active ? Critical : Low; if (stJob->error()) { Q_EMIT error(theAccount, ServerError, i18n("Friends list for account %1 could not be updated:\n%2", theAccount->username(), stJob->errorString()), level); return; } QStringList newList = readFriendsScreenName(theAccount, stJob->data()); newList.removeDuplicates(); if (! checkForError(stJob->data()).isEmpty()) { // if an error occurred, do not replace the friends list. theAccount->setFriendsList(friendsList); Q_EMIT friendsUsernameListed(theAccount, friendsList); } else if (QString::compare(d->friendsCursor, QLatin1String("0"))) { // if the cursor is not "0", there is more friends data to be had friendsList << newList; requestFriendsScreenName(theAccount, active); } else { friendsList << newList; theAccount->setFriendsList(friendsList); Choqok::UI::Global::mainWindow()->showStatusMessage(i18n("Friends list for account %1 has been updated.", theAccount->username())); Q_EMIT friendsUsernameListed(theAccount, friendsList); } } void TwitterApiMicroBlog::listFollowersUsername(TwitterApiAccount* theAccount, bool active) { followersList.clear(); d->followersCursor = QLatin1String("-1"); if ( theAccount ) { requestFollowersScreenName(theAccount, active); } } void TwitterApiMicroBlog::requestFollowersScreenName(TwitterApiAccount* theAccount, bool active) { qCDebug(CHOQOK); TwitterApiAccount* account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + (QStringLiteral("/followers/list.%1").arg(format))); QUrl tmpUrl(url); QUrlQuery urlQuery; urlQuery.addQueryItem(QLatin1String("cursor"), d->followersCursor); urlQuery.addQueryItem(QLatin1String("count"), QLatin1String("200")); url.setQuery(urlQuery); - QOAuth::ParamMap params; - params.insert("cursor", d->followersCursor.toLatin1()); - params.insert("count", QStringLiteral("200").toLatin1()); + QVariantMap params; + params.insert(QLatin1String("cursor"), d->followersCursor.toLatin1()); + params.insert(QLatin1String("count"), QByteArray::number(200)); KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo); if (!job) { qCDebug(CHOQOK) << "Cannot create an http GET request!"; return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + - QLatin1String(authorizationHeader(account, tmpUrl, QOAuth::GET, params))); + QLatin1String(authorizationHeader(account, tmpUrl, QNetworkAccessManager::GetOperation, params))); mJobsAccount[job] = theAccount; if (active) { connect( job, SIGNAL( result( KJob* ) ), this, SLOT( slotRequestFollowersScreenNameActive(KJob*) ) ); } else { connect( job, SIGNAL( result( KJob* ) ), this, SLOT( slotRequestFollowersScreenNamePassive(KJob*) ) ); } job->start(); Choqok::UI::Global::mainWindow()->showStatusMessage(i18n("Updating followers list for account %1...", theAccount->username())); } void TwitterApiMicroBlog::slotRequestFollowersScreenNameActive(KJob* job) { finishRequestFollowersScreenName(job, true); } void TwitterApiMicroBlog::slotRequestFollowersScreenNamePassive(KJob* job) { finishRequestFollowersScreenName(job, false); } void TwitterApiMicroBlog::finishRequestFollowersScreenName(KJob* job, bool active) { qCDebug(CHOQOK); TwitterApiAccount *theAccount = qobject_cast( mJobsAccount.take(job) ); KIO::StoredTransferJob* stJob = qobject_cast( job ); Choqok::MicroBlog::ErrorLevel level = active ? Critical : Low; if (stJob->error()) { Q_EMIT error(theAccount, ServerError, i18n("Followers list for account %1 could not be updated:\n%2", theAccount->username(), stJob->errorString()), level); return; } QStringList newList = readFollowersScreenName(theAccount, stJob->data()); newList.removeDuplicates(); if (!checkForError(stJob->data()).isEmpty()) { // if an error occurred, do not replace the friends list. theAccount->setFollowersList(followersList); Q_EMIT followersUsernameListed(theAccount, followersList); } else if (QString::compare(d->followersCursor, QLatin1String("0"))) { // if the cursor is not "0", there is more friends data to be had followersList << newList; requestFollowersScreenName(theAccount, active); } else { followersList << newList; theAccount->setFollowersList(followersList); Choqok::UI::Global::mainWindow()->showStatusMessage(i18n("Followers list for account %1 has been updated.", theAccount->username()) ); Q_EMIT followersUsernameListed(theAccount, followersList); } } void TwitterApiMicroBlog::updateTimelines(Choqok::Account *theAccount) { qCDebug(CHOQOK); for (const QString &tm: theAccount->timelineNames()) { requestTimeLine(theAccount, tm, mTimelineLatestId[theAccount][tm]); } } void TwitterApiMicroBlog::requestTimeLine(Choqok::Account *theAccount, QString type, QString latestStatusId, int page, QString maxId) { qCDebug(CHOQOK); TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url.setPath(url.path() + timelineApiPath[type].arg(format)); QUrl tmpUrl(url); QUrlQuery urlQuery; - QOAuth::ParamMap params; + QVariantMap params; // needed because lists have different parameter names but // returned timelines have the same JSON format if (timelineApiPath[type].contains(QLatin1String("lists/statuses"))) { // type contains @username/timelinename const QString slug = type.mid(type.indexOf(QLatin1String("/")) + 1); urlQuery.addQueryItem(QLatin1String("slug"), slug); - params.insert("slug", slug.toLatin1()); + params.insert(QLatin1String("slug"), slug.toLatin1()); const QString owner = type.mid(1, type.indexOf(QLatin1String("/")) - 1); urlQuery.addQueryItem(QLatin1String("owner_screen_name"), owner); - params.insert("owner_screen_name", owner.toLatin1()); + params.insert(QLatin1String("owner_screen_name"), owner.toLatin1()); } else { int countOfPost = Choqok::BehaviorSettings::countOfPosts(); if (!latestStatusId.isEmpty()) { urlQuery.addQueryItem(QLatin1String("since_id"), latestStatusId); - params.insert("since_id", latestStatusId.toLatin1()); + params.insert(QLatin1String("since_id"), latestStatusId.toLatin1()); countOfPost = 200; } urlQuery.addQueryItem(QLatin1String("count"), QString::number(countOfPost)); - params.insert("count", QByteArray::number(countOfPost)); + params.insert(QLatin1String("count"), QByteArray::number(countOfPost)); if (!maxId.isEmpty()) { urlQuery.addQueryItem(QLatin1String("max_id"), maxId); - params.insert("max_id", maxId.toLatin1()); + params.insert(QLatin1String("max_id"), maxId.toLatin1()); } if (page) { urlQuery.addQueryItem(QLatin1String("page"), QString::number(page)); - params.insert("page", QByteArray::number(page)); + params.insert(QLatin1String("page"), QByteArray::number(page)); } } url.setQuery(urlQuery); qCDebug(CHOQOK) << "Latest" << type << "Id:" << latestStatusId;// << "apiReq:" << url; KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo) ; if (!job) { qCDebug(CHOQOK) << "Cannot create an http GET request!"; // QString errMsg = i18n ( "Cannot create an http GET request. Please check your KDE installation." ); // emit error ( theAccount, OtherError, errMsg, Low ); return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") - + QLatin1String(authorizationHeader(account, tmpUrl, QOAuth::GET, params))); + + QLatin1String(authorizationHeader(account, tmpUrl, QNetworkAccessManager::GetOperation, params))); mRequestTimelineMap[job] = type; mJobsAccount[job] = theAccount; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotRequestTimeline(KJob*))); job->start(); } void TwitterApiMicroBlog::slotRequestTimeline(KJob *job) { qCDebug(CHOQOK);//TODO Add error detection if (!job) { qCDebug(CHOQOK) << "Job is null pointer"; return; } Choqok::Account *theAccount = mJobsAccount.take(job); if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT error(theAccount, CommunicationError, i18n("Timeline update failed: %1", job->errorString()), Low); return; } QString type = mRequestTimelineMap.take(job); if (isValidTimeline(type)) { KIO::StoredTransferJob *j = qobject_cast(job); QList list; if (type == QLatin1String("Inbox") || type == QLatin1String("Outbox")) { list = readDirectMessages(theAccount, j->data()); } else { list = readTimeline(theAccount, j->data()); } if (!list.isEmpty()) { mTimelineLatestId[theAccount][type] = list.last()->postId; Q_EMIT timelineDataReceived(theAccount, type, list); } } } QByteArray TwitterApiMicroBlog::authorizationHeader(TwitterApiAccount *theAccount, const QUrl &requestUrl, - QOAuth::HttpMethod method, QOAuth::ParamMap params) + QNetworkAccessManager::Operation method, const QVariantMap ¶ms) { QByteArray auth; if (theAccount->usingOAuth()) { - auth = theAccount->oauthInterface()->createParametersString(requestUrl.url(), method, theAccount->oauthToken(), - theAccount->oauthTokenSecret(), QOAuth::HMAC_SHA1, - params, QOAuth::ParseForHeaderArguments); + auth = theAccount->oauthInterface()->authorizationHeader(requestUrl, method, params); } else { auth = theAccount->username().toUtf8() + ':' + theAccount->password().toUtf8(); auth = auth.toBase64().prepend("Basic "); } return auth; } void TwitterApiMicroBlog::setRepeatedOfInfo(Choqok::Post *post, Choqok::Post *repeatedPost) { post->content = repeatedPost->content; post->replyToPostId = repeatedPost->replyToPostId; post->replyToUserId = repeatedPost->replyToUserId; post->replyToUserName = repeatedPost->replyToUserName; post->repeatedPostId = repeatedPost->postId; post->repeatedDateTime = repeatedPost->creationDateTime; if (Choqok::AppearanceSettings::showRetweetsInChoqokWay()) { post->repeatedFromUsername = repeatedPost->author.userName; } else { post->repeatedFromUsername = post->author.userName; post->author = repeatedPost->author; } if (!repeatedPost->quotedPost.content.isEmpty()) { post->quotedPost = repeatedPost->quotedPost; } } void TwitterApiMicroBlog::setQuotedPost(Choqok::Post* post, Choqok::Post* quotedPost) { post->quotedPost.profileImageUrl = quotedPost->author.profileImageUrl; post->quotedPost.username = quotedPost->author.userName; post->quotedPost.postId = quotedPost->postId; post->quotedPost.content = quotedPost->content; } QDateTime TwitterApiMicroBlog::dateFromString(const QString &date) { char s[10]; int year, day, hours, minutes, seconds, tz; sscanf(qPrintable(date), "%*s %s %d %d:%d:%d %d %d", s, &day, &hours, &minutes, &seconds, &tz, &year); int month = d->monthes[QLatin1String(s)]; QDateTime recognized(QDate(year, month, day), QTime(hours, minutes, seconds)); if (tz == 0) { //tz is the timezone, in Twitter it's always UTC(0) in Identica it's local +/-NUMBER recognized.setTimeSpec(Qt::UTC); } return recognized.toLocalTime(); } void TwitterApiMicroBlog::aboutToUnload() { d->countOfTimelinesToSave = 0; for (Choqok::Account *acc: Choqok::AccountManager::self()->accounts()) { if (acc->microblog() == this) { d->countOfTimelinesToSave += acc->timelineNames().count(); } } Q_EMIT saveTimelines(); } void TwitterApiMicroBlog::showDirectMessageDialog(TwitterApiAccount *theAccount, const QString &toUsername) { qCDebug(CHOQOK); if (!theAccount) { QAction *act = qobject_cast(sender()); theAccount = qobject_cast( Choqok::AccountManager::self()->findAccount(act->data().toString())); } TwitterApiDMessageDialog *dmsg = new TwitterApiDMessageDialog(theAccount, Choqok::UI::Global::mainWindow()); if (!toUsername.isEmpty()) { dmsg->setTo(toUsername); } dmsg->show(); } Choqok::TimelineInfo *TwitterApiMicroBlog::timelineInfo(const QString &timelineName) { if (isValidTimeline(timelineName)) { return mTimelineInfos.value(timelineName); } else { return nullptr; } } void TwitterApiMicroBlog::showSearchDialog(TwitterApiAccount *theAccount) { if (!theAccount) { QAction *act = qobject_cast(sender()); theAccount = qobject_cast( Choqok::AccountManager::self()->findAccount(act->data().toString())); } QPointer searchDlg = new TwitterApiSearchDialog(theAccount, Choqok::UI::Global::mainWindow()); searchDlg->show(); } void TwitterApiMicroBlog::slotUpdateFriendsList() { QAction *act = qobject_cast(sender()); TwitterApiAccount *theAccount = qobject_cast( Choqok::AccountManager::self()->findAccount(act->data().toString())); listFriendsUsername(theAccount, true); } void TwitterApiMicroBlog::createFriendship(Choqok::Account *theAccount, const QString &username) { qCDebug(CHOQOK); TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url.setPath(url.path() + QStringLiteral("/friendships/create.%1").arg(format)); QUrl tmp(url); QUrlQuery urlQuery; urlQuery.addQueryItem(QLatin1String("screen_name"), username); url.setQuery(urlQuery); - QOAuth::ParamMap params; - params.insert("screen_name", username.toLatin1()); + QVariantMap params; + params.insert(QLatin1String("screen_name"), username.toLatin1()); KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ; qCDebug(CHOQOK) << url; if (!job) { qCCritical(CHOQOK) << "Cannot create an http POST request!"; return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + - QLatin1String(authorizationHeader(account, tmp, QOAuth::POST, params))); + QLatin1String(authorizationHeader(account, tmp, QNetworkAccessManager::PostOperation, params))); mJobsAccount[job] = theAccount; mFriendshipMap[ job ] = username; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotCreateFriendship(KJob*))); job->start(); } void TwitterApiMicroBlog::slotCreateFriendship(KJob *job) { qCDebug(CHOQOK); if (!job) { qCCritical(CHOQOK) << "Job is a null Pointer!"; return; } TwitterApiAccount *theAccount = qobject_cast(mJobsAccount.take(job)); QString username = mFriendshipMap.take(job); if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT error(theAccount, CommunicationError, i18n("Creating friendship with %1 failed. %2", username, job->errorString())); return; } KIO::StoredTransferJob *stj = qobject_cast(job); Choqok::User *user = readUserInfo(stj->data()); if (user /*&& user->userName.compare(username, Qt::CaseInsensitive)*/) { Q_EMIT friendshipCreated(theAccount, username); Choqok::NotifyManager::success(i18n("You are now listening to %1's posts.", username)); theAccount->setFriendsList(QStringList()); listFriendsUsername(theAccount); } else { QString errorMsg = checkForError(stj->data()); if (errorMsg.isEmpty()) { qCDebug(CHOQOK) << "Parse Error:" << stj->data(); Q_EMIT error(theAccount, ParsingError, i18n("Creating friendship with %1 failed: the server returned invalid data.", username)); } else { qCDebug(CHOQOK) << "Server error:" << errorMsg; Q_EMIT error(theAccount, ServerError, i18n("Creating friendship with %1 failed: %2", username, errorMsg)); } } } void TwitterApiMicroBlog::destroyFriendship(Choqok::Account *theAccount, const QString &username) { qCDebug(CHOQOK); TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url.setPath(url.path() + QStringLiteral("/friendships/destroy.%1").arg(format)); QUrl tmp(url); QUrlQuery urlQuery; urlQuery.addQueryItem(QLatin1String("screen_name"), username); url.setQuery(urlQuery); - QOAuth::ParamMap params; - params.insert("screen_name", username.toLatin1()); + QVariantMap params; + params.insert(QLatin1String("screen_name"), username.toLatin1()); KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ; if (!job) { qCCritical(CHOQOK) << "Cannot create an http POST request!"; return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + - QLatin1String(authorizationHeader(account, tmp, QOAuth::POST, params))); + QLatin1String(authorizationHeader(account, tmp, QNetworkAccessManager::PostOperation, params))); mJobsAccount[job] = theAccount; mFriendshipMap[ job ] = username; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotDestroyFriendship(KJob*))); job->start(); } void TwitterApiMicroBlog::slotDestroyFriendship(KJob *job) { qCDebug(CHOQOK); if (!job) { qCCritical(CHOQOK) << "Job is a null Pointer!"; return; } TwitterApiAccount *theAccount = qobject_cast(mJobsAccount.take(job)); QString username = mFriendshipMap.take(job); if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT error(theAccount, CommunicationError, i18n("Destroying friendship with %1 failed. %2", username, job->errorString())); return; } KIO::StoredTransferJob *stj = qobject_cast(job); Choqok::User *user = readUserInfo(stj->data()); if (user /*&& user->userName.compare( username, Qt::CaseInsensitive )*/) { Q_EMIT friendshipDestroyed(theAccount, username); Choqok::NotifyManager::success(i18n("You will not receive %1's updates.", username)); theAccount->setFriendsList(QStringList()); listFriendsUsername(theAccount); } else { QString errorMsg = checkForError(stj->data()); if (errorMsg.isEmpty()) { qCDebug(CHOQOK) << "Parse Error:" << stj->data(); Q_EMIT error(theAccount, ParsingError, i18n("Destroying friendship with %1 failed: the server returned invalid data.", username)); } else { qCDebug(CHOQOK) << "Server error:" << errorMsg; Q_EMIT error(theAccount, ServerError, i18n("Destroying friendship with %1 failed: %2", username, errorMsg)); } } } void TwitterApiMicroBlog::blockUser(Choqok::Account *theAccount, const QString &username) { qCDebug(CHOQOK); TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url.setPath(url.path() + QStringLiteral("/blocks/create.%1").arg(format)); QUrl tmp(url); QUrlQuery urlQuery; urlQuery.addQueryItem(QLatin1String("screen_name"), username); url.setQuery(urlQuery); - QOAuth::ParamMap params; - params.insert("screen_name", username.toLatin1()); + QVariantMap params; + params.insert(QLatin1String("screen_name"), username.toLatin1()); KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ; if (!job) { qCCritical(CHOQOK) << "Cannot create an http POST request!"; return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + - QLatin1String(authorizationHeader(account, tmp, QOAuth::POST, params))); + QLatin1String(authorizationHeader(account, tmp, QNetworkAccessManager::PostOperation, params))); mJobsAccount[job] = theAccount; mFriendshipMap[ job ] = username; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotBlockUser(KJob*))); job->start(); } void TwitterApiMicroBlog::reportUserAsSpam(Choqok::Account *theAccount, const QString &username) { qCDebug(CHOQOK); TwitterApiAccount *account = qobject_cast(theAccount); QUrl url = account->apiUrl(); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QStringLiteral("/users/report_spam.%1").arg(format)); QUrl tmp(url); QUrlQuery urlQuery; urlQuery.addQueryItem(QLatin1String("screen_name"), username); url.setQuery(urlQuery); - QOAuth::ParamMap params; - params.insert("screen_name", username.toLatin1()); + QVariantMap params; + params.insert(QLatin1String("screen_name"), username.toLatin1()); KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ; if (!job) { qCCritical(CHOQOK) << "Cannot create an http POST request!"; return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + - QLatin1String(authorizationHeader(account, tmp, QOAuth::POST, params))); + QLatin1String(authorizationHeader(account, tmp, QNetworkAccessManager::PostOperation, params))); mJobsAccount[job] = theAccount; mFriendshipMap[ job ] = username; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotReportUser(KJob*))); job->start(); } void TwitterApiMicroBlog::slotBlockUser(KJob *job) { qCDebug(CHOQOK); if (!job) { qCCritical(CHOQOK) << "Job is a null Pointer!"; return; } Choqok::Account *theAccount = mJobsAccount.take(job); QString username = mFriendshipMap.take(job); if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT error(theAccount, CommunicationError, i18n("Blocking %1 failed. %2", username, job->errorString())); return; } Choqok::User *user = readUserInfo(qobject_cast(job)->data()); if (user /*&& user->userName.compare( username, Qt::CaseInsensitive )*/) { Q_EMIT userBlocked(theAccount, username); Choqok::NotifyManager::success(i18n("You will no longer be disturbed by %1.", username)); } else { qCDebug(CHOQOK) << "Parse Error:" << qobject_cast(job)->data(); Q_EMIT error(theAccount, ParsingError, i18n("Blocking %1 failed: the server returned invalid data.", username)); } //TODO Check for failor! } void TwitterApiMicroBlog::slotReportUser(KJob *job) { qCDebug(CHOQOK); if (!job) { qCCritical(CHOQOK) << "Job is a null Pointer!"; return; } Choqok::Account *theAccount = mJobsAccount.take(job); QString username = mFriendshipMap.take(job); if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT error(theAccount, CommunicationError, i18n("Reporting %1 failed. %2", username, job->errorString())); return; } Choqok::User *user = readUserInfo(qobject_cast(job)->data()); if (user) { Choqok::NotifyManager::success(i18n("Report sent successfully")); } else { qCDebug(CHOQOK) << "Parse Error:" << qobject_cast(job)->data(); Q_EMIT error(theAccount, ParsingError, i18n("Reporting %1 failed: the server returned invalid data.", username)); } } ///=================================================================== QString TwitterApiMicroBlog::checkForError(const QByteArray &buffer) { const QJsonDocument json = QJsonDocument::fromJson(buffer); if (!json.isNull()) { const QVariantMap map = json.toVariant().toMap(); if (map.contains(QLatin1String("errors"))) { QStringList errors; for (const QVariant &msg: map[QLatin1String("errors")].toList()) { errors.append(msg.toMap()[QLatin1String("message")].toString()); qCCritical(CHOQOK) << "Error:" << errors.last(); } return errors.join(QLatin1Char(';')); } } return QString(); } QList< Choqok::Post * > TwitterApiMicroBlog::readTimeline(Choqok::Account *theAccount, const QByteArray &buffer) { QList postList; const QJsonDocument json = QJsonDocument::fromJson(buffer); if (!json.isNull()) { for (const QVariant &list: json.toVariant().toList()) { postList.prepend(readPost(theAccount, list.toMap(), new Choqok::Post)); } } else { const QString err = checkForError(buffer); if (err.isEmpty()) { qCCritical(CHOQOK) << "JSON parsing failed.\nBuffer was: \n" << buffer; Q_EMIT error(theAccount, ParsingError, i18n("Could not parse the data that has been received from the server.")); } else { Q_EMIT error(theAccount, ServerError, err); } } return postList; } Choqok::Post *TwitterApiMicroBlog::readPost(Choqok::Account *theAccount, const QByteArray &buffer, Choqok::Post *post) { const QJsonDocument json = QJsonDocument::fromJson(buffer); if (!json.isNull()) { return readPost(theAccount, json.toVariant().toMap(), post); } else { if (!post) { qCCritical(CHOQOK) << "TwitterApiMicroBlog::readPost: post is NULL!"; post = new Choqok::Post; } Q_EMIT errorPost(theAccount, post, ParsingError, i18n("Could not parse the data that has been received from the server.")); qCCritical(CHOQOK) << "JSon parsing failed. Buffer was:" << buffer; post->isError = true; return post; } } Choqok::Post *TwitterApiMicroBlog::readPost(Choqok::Account *theAccount, const QVariantMap &var, Choqok::Post *post) { if (!post) { qCCritical(CHOQOK) << "TwitterApiMicroBlog::readPost: post is NULL!"; return nullptr; } post->content = var[QLatin1String("text")].toString(); post->creationDateTime = dateFromString(var[QLatin1String("created_at")].toString()); post->isFavorited = var[QLatin1String("favorited")].toBool(); post->postId = var[QLatin1String("id")].toString(); post->replyToPostId = var[QLatin1String("in_reply_to_status_id")].toString(); post->replyToUserId = var[QLatin1String("in_reply_to_user_id")].toString(); post->replyToUserName = var[QLatin1String("in_reply_to_screen_name")].toString(); post->source = var[QLatin1String("source")].toString(); QVariantMap userMap = var[QLatin1String("user")].toMap(); post->author.description = userMap[QLatin1String("description")].toString(); post->author.location = userMap[QLatin1String("location")].toString(); post->author.realName = userMap[QLatin1String("name")].toString(); post->author.userId = userMap[QLatin1String("id")].toString(); post->author.userName = userMap[QLatin1String("screen_name")].toString(); post->author.profileImageUrl = userMap[QLatin1String("profile_image_url")].toString(); QVariantMap entities = var[QLatin1String("entities")].toMap(); QVariantMap mediaMap; QVariantList media = entities[QLatin1String("media")].toList(); if (media.size() > 0) { mediaMap = media.at(0).toMap(); post->media = mediaMap[QLatin1String("media_url")].toString() + QLatin1String(":small"); QVariantMap sizes = mediaMap[QLatin1String("sizes")].toMap(); QVariantMap w = sizes[QLatin1String("small")].toMap(); } else { post->media = QString(); } QVariantMap retweetedMap = var[QLatin1String("retweeted_status")].toMap(); if (!retweetedMap.isEmpty()) { Choqok::Post *retweetedPost = readPost(theAccount, retweetedMap, new Choqok::Post); setRepeatedOfInfo(post, retweetedPost); delete retweetedPost; } QVariantMap quotedMap = var[QLatin1String("quoted_status")].toMap(); if (!quotedMap.isEmpty()) { Choqok::Post *quotedPost = readPost(theAccount, quotedMap, new Choqok::Post); setQuotedPost(post, quotedPost); delete quotedPost; } post->link = postUrl(theAccount, post->author.userName, post->postId); post->isRead = post->isFavorited || (post->repeatedFromUsername.compare(theAccount->username(), Qt::CaseInsensitive) == 0); if(post->postId.isEmpty() || post->author.userName.isEmpty()) post->isError = true; return post; } QList< Choqok::Post * > TwitterApiMicroBlog::readDirectMessages(Choqok::Account *theAccount, const QByteArray &buffer) { QList postList; const QJsonDocument json = QJsonDocument::fromJson(buffer); if (!json.isNull()) { for (const QVariant &list: json.toVariant().toList()) { postList.prepend(readDirectMessage(theAccount, list.toMap())); } } else { const QString err = checkForError(buffer); if (err.isEmpty()) { qCCritical(CHOQOK) << "JSON parsing failed.\nBuffer was: \n" << buffer; Q_EMIT error(theAccount, ParsingError, i18n("Could not parse the data that has been received from the server.")); } else { Q_EMIT error(theAccount, ServerError, err); } } return postList; } Choqok::Post *TwitterApiMicroBlog::readDirectMessage(Choqok::Account *theAccount, const QByteArray &buffer) { const QJsonDocument json = QJsonDocument::fromJson(buffer); if (!json.isNull()) { return readDirectMessage(theAccount, json.toVariant().toMap()); } else { Choqok::Post *post = new Choqok::Post; post->isError = true; return post; } } Choqok::Post *TwitterApiMicroBlog::readDirectMessage(Choqok::Account *theAccount, const QVariantMap &var) { Choqok::Post *msg = new Choqok::Post; msg->isPrivate = true; QString senderId, recipientId, senderScreenName, recipientScreenName, senderProfileImageUrl, senderName, senderDescription, recipientProfileImageUrl, recipientName, recipientDescription; msg->creationDateTime = dateFromString(var[QLatin1String("created_at")].toString()); msg->content = var[QLatin1String("text")].toString(); msg->postId = var[QLatin1String("id")].toString();; senderId = var[QLatin1String("sender_id")].toString(); recipientId = var[QLatin1String("recipient_id")].toString(); senderScreenName = var[QLatin1String("sender_screen_name")].toString(); recipientScreenName = var[QLatin1String("recipient_screen_name")].toString(); QVariantMap sender = var[QLatin1String("sender")].toMap(); senderProfileImageUrl = sender[QLatin1String("profile_image_url")].toString(); senderName = sender[QLatin1String("name")].toString(); senderDescription = sender[QLatin1String("description")].toString(); QVariantMap recipient = var[QLatin1String("recipient")].toMap(); recipientProfileImageUrl = recipient[QLatin1String("profile_image_url")].toString(); recipientName = recipient[QLatin1String("name")].toString(); recipientDescription = recipient[QLatin1String("description")].toString(); if (senderScreenName.compare(theAccount->username(), Qt::CaseInsensitive) == 0) { msg->author.description = recipientDescription; msg->author.userName = recipientScreenName; msg->author.profileImageUrl = recipientProfileImageUrl; msg->author.realName = recipientName; msg->author.userId = recipientId; msg->replyToUserId = recipientId; msg->replyToUserName = recipientScreenName; msg->isRead = true; } else { msg->author.description = senderDescription; msg->author.userName = senderScreenName; msg->author.profileImageUrl = senderProfileImageUrl; msg->author.realName = senderName; msg->author.userId = senderId; msg->replyToUserId = recipientId; msg->replyToUserName = recipientScreenName; } return msg; } Choqok::User *TwitterApiMicroBlog::readUserInfo(const QByteArray &buffer) { Choqok::User *user = nullptr; const QJsonDocument json = QJsonDocument::fromJson(buffer); if (!json.isNull()) { user = new Choqok::User(readUser(0, json.toVariant().toMap())); } else { QString err = i18n("Retrieving the friends list failed. The data returned from the server is corrupted."); qCDebug(CHOQOK) << "JSON parse error:the buffer is: \n" << buffer; Q_EMIT error(0, ParsingError, err, Critical); } return user; } QStringList TwitterApiMicroBlog::readFriendsScreenName(Choqok::Account *theAccount, const QByteArray &buffer) { QStringList list; const QJsonDocument json = QJsonDocument::fromJson(buffer); if (!json.isNull()) { const QVariantMap map = json.toVariant().toMap(); QVariantList jsonList = map[QLatin1String("users")].toList(); QString nextCursor = map[QLatin1String("next_cursor_str")].toString(); if (nextCursor.isEmpty()) { nextCursor = QLatin1String("0"); // we probably ran the rate limit; stop bugging the server already } for (const QVariant &user: jsonList) { list << user.toMap()[QLatin1String("screen_name")].toString(); } d->friendsCursor = nextCursor; } else { QString err = i18n("Retrieving the friends list failed. The data returned from the server is corrupted."); qCDebug(CHOQOK) << "JSON parse error:the buffer is: \n" << buffer; Q_EMIT error(theAccount, ParsingError, err, Critical); } return list; } QStringList TwitterApiMicroBlog::readFollowersScreenName(Choqok::Account *theAccount, const QByteArray &buffer) { QStringList list; const QJsonDocument json = QJsonDocument::fromJson(buffer); if (!json.isNull()) { const QVariantMap map = json.toVariant().toMap(); QVariantList jsonList = map[QLatin1String("users")].toList(); QString nextCursor = map[QLatin1String("next_cursor_str")].toString(); if (nextCursor.isEmpty()) { nextCursor = QLatin1String("0"); // we probably ran the rate limit; stop bugging the server already } for (const QVariant &user: jsonList) { list << user.toMap()[QLatin1String("screen_name")].toString(); } d->followersCursor = nextCursor; } else { QString err = i18n("Retrieving the followers list failed. The data returned from the server is corrupted."); qCDebug(CHOQOK) << "JSON parse error:the buffer is: \n" << buffer; Q_EMIT error(theAccount, ParsingError, err, Critical); } return list; } Choqok::User TwitterApiMicroBlog::readUser(Choqok::Account *theAccount, const QVariantMap &map) { Q_UNUSED(theAccount); Choqok::User u; u.description = map[QLatin1String("description")].toString(); u.followersCount = map[QLatin1String("followers_count")].toUInt(); u.homePageUrl = map[QLatin1String("url")].toString(); u.isProtected = map[QLatin1String("protected")].toBool(); u.location = map[QLatin1String("location")].toString(); u.profileImageUrl = map[QLatin1String("profile_image_url")].toString(); u.realName = map[QLatin1String("name")].toString(); u.userId = map[QLatin1String("id_str")].toString(); u.userName = map[QLatin1String("screen_name")].toString(); return u; } diff --git a/helperlibs/twitterapihelper/twitterapimicroblog.h b/helperlibs/twitterapihelper/twitterapimicroblog.h index 666b0693..984849fd 100644 --- a/helperlibs/twitterapihelper/twitterapimicroblog.h +++ b/helperlibs/twitterapihelper/twitterapimicroblog.h @@ -1,259 +1,258 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2008-2012 Mehrdad Momeny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #ifndef TWITTERAPIMICROBLOG_H #define TWITTERAPIMICROBLOG_H #include +#include #include -#include - #include "microblog.h" #include "twitterapisearch.h" class TwitterApiSearchTimelineWidget; class TwitterApiAccount; class KJob; /** @author Mehrdad Momeny \ */ class CHOQOK_HELPER_EXPORT TwitterApiMicroBlog : public Choqok::MicroBlog { Q_OBJECT public: ~TwitterApiMicroBlog(); virtual QMenu *createActionsMenu(Choqok::Account *theAccount, QWidget *parent = Choqok::UI::Global::mainWindow()) override; virtual QList< Choqok::Post * > loadTimeline(Choqok::Account *accountAlias, const QString &timelineName) override; virtual void saveTimeline(Choqok::Account *account, const QString &timelineName, const QList< Choqok::UI::PostWidget * > &timeline) override; virtual Choqok::UI::ComposerWidget *createComposerWidget(Choqok::Account *account, QWidget *parent) override; /** \brief Create a new post @see postCreated() @see abortCreatePost() */ virtual void createPost(Choqok::Account *theAccount, Choqok::Post *post) override; /** \brief Abort all requests! */ virtual void abortAllJobs(Choqok::Account *theAccount) override; /** \brief Abort all of createPost requests! */ virtual void abortCreatePost(Choqok::Account *theAccount, Choqok::Post *post = 0) override; /** \brief Fetch a post @see postFetched() */ virtual void fetchPost(Choqok::Account *theAccount, Choqok::Post *post) override; /** \brief Remove a post @see postRemoved() */ virtual void removePost(Choqok::Account *theAccount, Choqok::Post *post) override; /** \brief Repeat/Retweet a post using the API */ virtual void repeatPost(Choqok::Account *theAccount, const QString &postId); /** Request to update all timelines of account! They will arrive in several signals! with timelineDataReceived() signal! @see timelineDataReceived() */ virtual void updateTimelines(Choqok::Account *theAccount) override; /** add post with Id @p postId to @p theAccount favorite list */ virtual void createFavorite(Choqok::Account *theAccount, const QString &postId); /** remove post with Id @p postId from @p theAccount favorite list */ virtual void removeFavorite(Choqok::Account *theAccount, const QString &postId); /** Create friendship, or Follow/Subscribe to user with username or screen name @p username i.e. Follow / Subscribe */ virtual void createFriendship(Choqok::Account *theAccount, const QString &username); /** Destroy friendship with user with username or screen name @p username i.e. Un Follow / UnSubscribe */ virtual void destroyFriendship(Choqok::Account *theAccount, const QString &username); /** Block user with username or screen name @p username */ virtual void blockUser(Choqok::Account *theAccount, const QString &username); /** * Report user as a spam with username or screen name @p username */ virtual void reportUserAsSpam(Choqok::Account *theAccount, const QString &username); virtual void aboutToUnload() override; virtual void listFriendsUsername(TwitterApiAccount *theAccount, bool active = false); virtual void listFollowersUsername(TwitterApiAccount *theAccount, bool active = false); virtual Choqok::TimelineInfo *timelineInfo(const QString &timelineName) override; /** Return search backend to use for search. Should be implemented on sub classes */ virtual TwitterApiSearch *searchBackend() = 0; virtual TwitterApiSearchTimelineWidget *createSearchTimelineWidget(Choqok::Account *theAccount, QString name, const SearchInfo &info, QWidget *parent); QDateTime dateFromString(const QString &date); /** * The text to add under repeated posts, to notice user about it. */ virtual QString generateRepeatedByUserTooltip(const QString &username) = 0; /** * The question will show to confirm repeat post. */ virtual QString repeatQuestion() = 0; virtual QByteArray authorizationHeader(TwitterApiAccount *theAccount, - const QUrl &requestUrl, QOAuth::HttpMethod method, - QOAuth::ParamMap params = QOAuth::ParamMap()); + const QUrl &requestUrl, QNetworkAccessManager::Operation method, + const QVariantMap ¶ms = QVariantMap()); public Q_SLOTS: /** Launch a dialog to send direct message. There are 2 ways to use this function: 1. Calling with theAccount option 2. Get called by a signal from a QAction (Microblog menu) */ virtual void showDirectMessageDialog(TwitterApiAccount *theAccount = 0, const QString &toUsername = QString()); void showSearchDialog(TwitterApiAccount *theAccount = 0); Q_SIGNALS: void favoriteCreated(Choqok::Account *theAccount, const QString &postId); void favoriteRemoved(Choqok::Account *theAccount, const QString &postId); void friendsUsernameListed(TwitterApiAccount *theAccount, const QStringList &friendsList); void followersUsernameListed(TwitterApiAccount *theAccount, const QStringList &friendsList); void friendshipCreated(Choqok::Account *theAccount, const QString &newFriendUsername); void friendshipDestroyed(Choqok::Account *theAccount, const QString &username); void userBlocked(Choqok::Account *theAccount, const QString &blockedUsername); protected Q_SLOTS: virtual void slotCreatePost(KJob *job); virtual void slotFetchPost(KJob *job); virtual void slotRemovePost(KJob *job); virtual void slotCreateFavorite(KJob *job); virtual void slotRemoveFavorite(KJob *job); virtual void slotRequestTimeline(KJob *job); virtual void requestFriendsScreenName(TwitterApiAccount *theAccount, bool active); void slotRequestFriendsScreenNameActive(KJob *job); void slotRequestFriendsScreenNamePassive(KJob *job); virtual void requestFollowersScreenName(TwitterApiAccount *theAccount, bool active); void slotRequestFollowersScreenNameActive(KJob *job); void slotRequestFollowersScreenNamePassive(KJob *job); virtual void slotCreateFriendship(KJob *job); virtual void slotDestroyFriendship(KJob *job); virtual void slotBlockUser(KJob *job); virtual void slotReportUser(KJob *job); virtual void slotUpdateFriendsList(); protected: TwitterApiMicroBlog(const QString &componentName, QObject *parent = 0); /** Request update for @p timelineName timeline. timelineName should be a valid, previously created timeline. */ virtual void requestTimeLine(Choqok::Account *theAccount, QString timelineName, QString sincePostId, int page = 1, QString maxId = QString()); virtual void setTimelineInfos(); void setRepeatedOfInfo(Choqok::Post *post, Choqok::Post *repeatedPost); virtual Choqok::Post *readPost(Choqok::Account *theAccount, const QVariantMap &var, Choqok::Post *post); virtual Choqok::Post *readPost(Choqok::Account *theAccount, const QByteArray &buffer, Choqok::Post *post); virtual QList readTimeline(Choqok::Account *theAccount, const QByteArray &buffer); virtual Choqok::Post *readDirectMessage(Choqok::Account *theAccount, const QByteArray &buffer); virtual Choqok::Post *readDirectMessage(Choqok::Account *theAccount, const QVariantMap &var); virtual QList readDirectMessages(Choqok::Account *theAccount, const QByteArray &buffer); virtual QStringList readFriendsScreenName(Choqok::Account *theAccount, const QByteArray &buffer); virtual QStringList readFollowersScreenName(Choqok::Account *theAccount, const QByteArray &buffer); virtual Choqok::User *readUserInfo(const QByteArray &buffer); virtual Choqok::User readUser(Choqok::Account *theAccount, const QVariantMap &map); /** Checks json returned from server for error, and return error string, Or an empty string if nothing found! */ virtual QString checkForError(const QByteArray &buffer); void finishRequestFriendsScreenName(KJob *job, bool active); void finishRequestFollowersScreenName(KJob *job, bool active); void setQuotedPost(Choqok::Post* post, Choqok::Post* quotedPost); ///========================================== QHash timelineApiPath;//TimelineType, path QMap mTimelineInfos; //timelineName, Info QMap mFavoriteMap; //Job, postId QMap mRemovePostMap; QMap mCreatePostMap; //Job, post QMap mFetchPostMap; QMap mRequestTimelineMap; //Job, TimelineType QHash< Choqok::Account *, QMap > mTimelineLatestId; //TimelineType, LatestId QMap mJobsAccount; QMap mFriendshipMap; QString format; QStringList friendsList; QStringList followersList; private: class Private; Private *const d; }; #endif diff --git a/helperlibs/twitterapihelper/twitterapioauth.cpp b/helperlibs/twitterapihelper/twitterapioauth.cpp new file mode 100644 index 00000000..31e995ee --- /dev/null +++ b/helperlibs/twitterapihelper/twitterapioauth.cpp @@ -0,0 +1,92 @@ +/* + This file is part of Choqok, the KDE micro-blogging client + + Copyright (C) 2017 Andrea Scarpino + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License or (at your option) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see http://www.gnu.org/licenses/ + +*/ + +#include "twitterapioauth.h" + +#include +#include + +#include + +#include "choqokoauthreplyhandler.h" + +#include "twitterapiaccount.h" +#include "twitterapidebug.h" + +TwitterApiOAuth::TwitterApiOAuth(TwitterApiAccount *account) + : QOAuth1(account), m_replyHandler(0), m_networkAccessManager(0) +{ + qCDebug(CHOQOK); + + m_replyHandler = new ChoqokOAuthReplyHandler(this); + setReplyHandler(m_replyHandler); + + m_networkAccessManager = new KIO::AccessManager(this); + setNetworkAccessManager(m_networkAccessManager); + + setClientIdentifier(QLatin1String(account->oauthConsumerKey())); + setClientSharedSecret(QLatin1String(account->oauthConsumerSecret())); + + setSignatureMethod(SignatureMethod::Hmac_Sha1); + + setTemporaryCredentialsUrl(QUrl(account->host() + QLatin1String("/oauth/request_token"))); + setAuthorizationUrl(QUrl(account->host() + QLatin1String("/oauth/authorize"))); + setTokenCredentialsUrl(QUrl(account->host() + QLatin1String("/oauth/access_token"))); +} + +TwitterApiOAuth::~TwitterApiOAuth() +{ + m_replyHandler->deleteLater(); + m_networkAccessManager->deleteLater(); +} + +QByteArray TwitterApiOAuth::authorizationHeader(const QUrl &requestUrl, QNetworkAccessManager::Operation operation, + const QVariantMap &signingParameters) +{ + QVariantMap oauthParams; + QVariantMap otherParams = signingParameters; + // Adding parameters located in the query + { + auto queryItems = QUrlQuery(requestUrl.query()).queryItems(); + for (auto it = queryItems.begin(), end = queryItems.end(); it != end; ++it) + otherParams.insert(it->first, it->second); + } + + const auto currentDateTime = QDateTime::currentDateTimeUtc(); + + oauthParams.insert(QStringLiteral("oauth_consumer_key"), clientIdentifier()); + oauthParams.insert(QStringLiteral("oauth_version"), QStringLiteral("1.0")); + oauthParams.insert(QStringLiteral("oauth_token"), token()); + oauthParams.insert(QStringLiteral("oauth_signature_method"), QStringLiteral("HMAC-SHA1")); + oauthParams.insert(QStringLiteral("oauth_nonce"), QOAuth1::nonce()); + oauthParams.insert(QStringLiteral("oauth_timestamp"), QString::number(currentDateTime.toTime_t())); + + // Add signature parameter + { + const auto parameters = QVariantMap(oauthParams).unite(signingParameters); + oauthParams.insert(QStringLiteral("oauth_signature"), signature(parameters, requestUrl, + operation, clientSharedSecret(), tokenSecret())); + } + + return generateAuthorizationHeader(oauthParams); +} diff --git a/libchoqok/choqoktools.h b/helperlibs/twitterapihelper/twitterapioauth.h similarity index 57% copy from libchoqok/choqoktools.h copy to helperlibs/twitterapihelper/twitterapioauth.h index e0bbbb4d..d39fffb7 100644 --- a/libchoqok/choqoktools.h +++ b/helperlibs/twitterapihelper/twitterapioauth.h @@ -1,43 +1,53 @@ /* This file is part of Choqok, the KDE micro-blogging client - Copyright (C) 2008-2012 Mehrdad Momeny + Copyright (C) 2017 Andrea Scarpino This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ -#ifndef CHOQOK_CHOQOKTOOLS_H -#define CHOQOK_CHOQOKTOOLS_H +#ifndef TWITTERAPIOAUTH_H +#define TWITTERAPIOAUTH_H -#include -#include +#include #include "choqok_export.h" -namespace Choqok -{ +class ChoqokOAuthReplyHandler; +class TwitterApiAccount; -void CHOQOK_EXPORT openUrl(const QUrl &url); +namespace KIO { +class AccessManager; +} -QString CHOQOK_EXPORT qoauthErrorText(int code); +class CHOQOK_HELPER_EXPORT TwitterApiOAuth : public QOAuth1 +{ + Q_OBJECT +public: + explicit TwitterApiOAuth(TwitterApiAccount *account); + ~TwitterApiOAuth(); -QString CHOQOK_EXPORT getColorString(const QColor &color); + QByteArray authorizationHeader(const QUrl &requestUrl, QNetworkAccessManager::Operation method, + const QVariantMap ¶ms = QVariantMap()); -} +private: + ChoqokOAuthReplyHandler *m_replyHandler; + KIO::AccessManager *m_networkAccessManager; +}; -#endif // CHOQOK_CHOQOKTOOLS_H +#endif // TWITTERAPIOAUTH_H diff --git a/helperlibs/twitterapihelper/twitterapitextedit.cpp b/helperlibs/twitterapihelper/twitterapitextedit.cpp index ea16b922..560d6763 100644 --- a/helperlibs/twitterapihelper/twitterapitextedit.cpp +++ b/helperlibs/twitterapihelper/twitterapitextedit.cpp @@ -1,185 +1,183 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2010-2012 Mehrdad Momeny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #include "twitterapitextedit.h" #include #include #include #include -#include - #include "urlutils.h" #include "twitterapiaccount.h" #include "twitterapidebug.h" class TwitterApiTextEdit::Private { public: Private(Choqok::Account *theAccount) : acc(theAccount), c(0) {} Choqok::Account *acc; QCompleter *c; }; TwitterApiTextEdit::TwitterApiTextEdit(Choqok::Account *theAccount, QWidget *parent) : TextEdit(theAccount->postCharLimit(), parent), d(new Private(theAccount)) { qCDebug(CHOQOK); setTabChangesFocus(false); } TwitterApiTextEdit::~TwitterApiTextEdit() { delete d; } void TwitterApiTextEdit::setCompleter(QCompleter *completer) { if (d->c) { QObject::disconnect(d->c, 0, this, 0); } d->c = completer; if (!d->c) { return; } d->c->setWidget(this); d->c->setCompletionMode(QCompleter::PopupCompletion); d->c->setCaseSensitivity(Qt::CaseInsensitive); QObject::connect(d->c, SIGNAL(activated(QString)), this, SLOT(insertCompletion(QString))); } QCompleter *TwitterApiTextEdit::completer() const { return d->c; } void TwitterApiTextEdit::insertCompletion(const QString &completion) { if (d->c->widget() != this) { return; } QString textToInsert = completion + QLatin1Char(' '); QTextCursor tc = textCursor(); tc.movePosition(QTextCursor::EndOfWord); tc.select(QTextCursor::WordUnderCursor); bool startWithAt = toPlainText()[tc.selectionStart() - 1] != QLatin1Char('@'); if (startWithAt) { textToInsert.prepend(QLatin1Char('@')); } tc.insertText(textToInsert); setTextCursor(tc); } // QString TwitterApiTextEdit::textUnderCursor() const // { // QTextCursor tc = textCursor(); // tc.select(QTextCursor::WordUnderCursor); // return tc.selectedText(); // } void TwitterApiTextEdit::focusInEvent(QFocusEvent *e) { if (d->c) { d->c->setWidget(this); } KTextEdit::focusInEvent(e); } void TwitterApiTextEdit::keyPressEvent(QKeyEvent *e) { if (d->c && d->c->popup()->isVisible()) { // The following keys are forwarded by the completer to the widget switch (e->key()) { case Qt::Key_Enter: case Qt::Key_Return: case Qt::Key_Escape: // case Qt::Key_Backtab: e->ignore(); return; // let the completer do default behavior default: // Choqok::UI::TextEdit::keyPressEvent(e); break; } } else if (e->text().isEmpty()) { Choqok::UI::TextEdit::keyPressEvent(e); return; } if (e->key() == Qt::Key_Tab) { e->ignore(); return; } // bool isShortcut = ((e->modifiers() & Qt::ControlModifier) && e->key() == Qt::Key_Space); // CTRL+E // if (!d->c )// || !isShortcut) // don't process the shortcut when we have a completer Choqok::UI::TextEdit::keyPressEvent(e); const bool ctrlOrShift = e->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier | Qt::AltModifier | Qt::MetaModifier); if (!d->c || (ctrlOrShift && e->text().isEmpty())) { return; } static QString eow(QLatin1String("~!@#$%^&*()+{}|:\"<>?,./;'[]\\-= ")); // end of word // bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift; //Implemented internally to get the char before selection :D QTextCursor tc = textCursor(); tc.select(QTextCursor::WordUnderCursor); QString completionPrefix = tc.selectedText(); QChar charBeforeSelection; if (completionPrefix.startsWith(QLatin1Char('@'))) { charBeforeSelection = completionPrefix.at(0); completionPrefix.remove(0, 1); } else { if (!toPlainText().isEmpty() && tc.selectionStart() > 0) { charBeforeSelection = toPlainText()[tc.selectionStart() - 1]; } } if (!e->text().isEmpty() && (eow.contains(e->text().right(1)) || completionPrefix.length() < 1 || charBeforeSelection != QLatin1Char('@'))) { d->c->popup()->hide(); return; } else if ((e->key() != Qt::Key_Enter) && (e->key() != Qt::Key_Return)) { if (textCursor().selectedText().length() && textCursor().selectedText() != completionPrefix) { return; } if (completionPrefix != d->c->completionPrefix()) { d->c->setCompletionPrefix(completionPrefix); d->c->popup()->setCurrentIndex(d->c->completionModel()->index(0, 0)); } QRect cr = cursorRect(); cr.setWidth(d->c->popup()->sizeHintForColumn(0) + d->c->popup()->verticalScrollBar()->sizeHint().width()); d->c->complete(cr); // popup it up! } } diff --git a/helperlibs/twitterapihelper/twitterapiwhoiswidget.cpp b/helperlibs/twitterapihelper/twitterapiwhoiswidget.cpp index 4357cade..c2a21c64 100644 --- a/helperlibs/twitterapihelper/twitterapiwhoiswidget.cpp +++ b/helperlibs/twitterapihelper/twitterapiwhoiswidget.cpp @@ -1,426 +1,426 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2008-2012 Mehrdad Momeny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #include "twitterapiwhoiswidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "choqokappearancesettings.h" #include "choqoktools.h" #include "choqoktypes.h" #include "mediamanager.h" #include "microblog.h" #include "notifymanager.h" #include "twitterapiaccount.h" #include "twitterapidebug.h" #include "twitterapimicroblog.h" class TwitterApiWhoisWidget::Private { public: Private(TwitterApiAccount *account, const QString &userN) : currentAccount(account), waitFrame(0), job(0), username(userN) { mBlog = qobject_cast(account->microblog()); } QTextBrowser *wid; TwitterApiAccount *currentAccount; TwitterApiMicroBlog *mBlog; QFrame *waitFrame; QPointer job; Choqok::Post currentPost; QString username; QString errorMessage; QString followersCount; QString friendsCount; QString statusesCount; QString timeZone; QString imgActions; // bool isFollowing; }; TwitterApiWhoisWidget::TwitterApiWhoisWidget(TwitterApiAccount *theAccount, const QString &username, const Choqok::Post &post, QWidget *parent) : QFrame(parent), d(new Private(theAccount, username)) { qCDebug(CHOQOK); setAttribute(Qt::WA_DeleteOnClose); d->currentPost = post; loadUserInfo(theAccount, username); d->wid = new QTextBrowser(this); setFrameShape(StyledPanel); setFrameShadow(Sunken); d->wid->setFrameShape(QFrame::NoFrame); QVBoxLayout *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(d->wid); this->setLayout(layout); this->setWindowFlags(Qt::Popup);// | Qt::FramelessWindowHint | Qt::Ta); d->wid->setOpenLinks(false); connect(d->wid, SIGNAL(anchorClicked(QUrl)), this, SLOT(checkAnchor(QUrl))); setupUi(); setActionImages(); } TwitterApiWhoisWidget::~TwitterApiWhoisWidget() { qCDebug(CHOQOK); delete d; } void TwitterApiWhoisWidget::loadUserInfo(TwitterApiAccount *theAccount, const QString &username) { qCDebug(CHOQOK); QString urlStr; QString user = username; if (user.contains(QLatin1Char('@'))) { QStringList lst = user.split(QLatin1Char('@')); if (lst.count() == 2) { //USER@HOST QString host = lst[1]; urlStr = QStringLiteral("https://%1/api").arg(host); user = lst[0]; } } else if (d->currentPost.source == QLatin1String("ostatus") && !d->currentPost.author.homePageUrl.isEmpty()) { urlStr = d->currentPost.author.homePageUrl; if (urlStr.endsWith(user)) { int len = urlStr.length(); int userLen = user.length(); urlStr.remove(len - userLen, userLen); qCDebug(CHOQOK) << urlStr; } urlStr.append(QLatin1String("api")); } else { urlStr = theAccount->apiUrl().url(); } QUrl url(urlStr); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + (QStringLiteral("/users/show/%1.json").arg(user))); // qCDebug(CHOQOK) << url; KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo); if (d->currentPost.source != QLatin1String("ostatus")) { job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + - QLatin1String(d->mBlog->authorizationHeader(theAccount, url, QOAuth::GET))); + QLatin1String(d->mBlog->authorizationHeader(theAccount, url, QNetworkAccessManager::GetOperation))); } d->job = job; connect(job, SIGNAL(result(KJob*)), SLOT(userInfoReceived(KJob*))); job->start(); } void TwitterApiWhoisWidget::userInfoReceived(KJob *job) { qCDebug(CHOQOK); if (job->error()) { qCCritical(CHOQOK) << "Job Error:" << job->errorString(); if (Choqok::UI::Global::mainWindow()->statusBar()) { Choqok::UI::Global::mainWindow()->statusBar()->showMessage(job->errorString()); } slotCancel(); return; } KIO::StoredTransferJob *stj = qobject_cast(job); // qCDebug(CHOQOK)<data(); const QJsonDocument json = QJsonDocument::fromJson(stj->data()); if (json.isNull()) { qCDebug(CHOQOK) << "JSON parsing failed! Data is:\n\t" << stj->data(); d->errorMessage = i18n("Cannot load user information."); updateHtml(); showForm(); return; } const QVariantMap map = json.toVariant().toMap(); QString timeStr; Choqok::Post post; d->errorMessage = map[QLatin1String("error")].toString(); if (d->errorMessage.isEmpty()) { //No Error post.author.realName = map[QLatin1String("name")].toString(); post.author.userName = map[QLatin1String("screen_name")].toString(); post.author.location = map[QLatin1String("location")].toString(); post.author.description = map[QLatin1String("description")].toString(); post.author.profileImageUrl = map[QLatin1String("profile_image_url")].toString(); post.author.homePageUrl = map[QLatin1String("url")].toString(); d->timeZone = map[QLatin1String("time_zone")].toString(); d->followersCount = map[QLatin1String("followers_count")].toString(); d->friendsCount = map[QLatin1String("friends_count")].toString(); d->statusesCount = map[QLatin1String("statuses_count")].toString(); QVariantMap var = map[QLatin1String("status")].toMap(); post.content = var[QLatin1String("text")].toString(); post.creationDateTime = d->mBlog->dateFromString(var[QLatin1String("created_at")].toString()); post.isFavorited = var[QLatin1String("favorited")].toBool(); post.postId = var[QLatin1String("id")].toString(); post.replyToPostId = var[QLatin1String("in_reply_to_status_id")].toString(); post.replyToUserId = var[QLatin1String("in_reply_to_user_id")].toString(); post.replyToUserName = var[QLatin1String("in_reply_to_screen_name")].toString(); post.source = var[QLatin1String("source")].toString(); d->currentPost = post; } updateHtml(); showForm(); QPixmap userAvatar = Choqok::MediaManager::self()->fetchImage(post.author.profileImageUrl, Choqok::MediaManager::Async); if (!userAvatar.isNull()) { d->wid->document()->addResource(QTextDocument::ImageResource, QUrl(QLatin1String("img://profileImage")), userAvatar); } else { connect(Choqok::MediaManager::self(), SIGNAL(imageFetched(QString,QPixmap)), this, SLOT(avatarFetched(QString,QPixmap))); connect(Choqok::MediaManager::self(), SIGNAL(fetchError(QString,QString)), this, SLOT(avatarFetchError(QString,QString))); } } void TwitterApiWhoisWidget::avatarFetched(const QString &remoteUrl, const QPixmap &pixmap) { qCDebug(CHOQOK); if (remoteUrl == d->currentPost.author.profileImageUrl) { const QUrl url(QLatin1String("img://profileImage")); d->wid->document()->addResource(QTextDocument::ImageResource, url, pixmap); updateHtml(); disconnect(Choqok::MediaManager::self(), SIGNAL(imageFetched(QString,QPixmap)), this, SLOT(avatarFetched(QString,QPixmap))); disconnect(Choqok::MediaManager::self(), SIGNAL(fetchError(QString,QString)), this, SLOT(avatarFetchError(QString,QString))); } } void TwitterApiWhoisWidget::avatarFetchError(const QString &remoteUrl, const QString &errMsg) { qCDebug(CHOQOK); Q_UNUSED(errMsg); if (remoteUrl == d->currentPost.author.profileImageUrl) { ///Avatar fetching is failed! but will not disconnect to get the img if it fetches later! const QUrl url(QLatin1String("img://profileImage")); d->wid->document()->addResource(QTextDocument::ImageResource, url, QIcon::fromTheme(QLatin1String("image-missing")).pixmap(48)); updateHtml(); } } void TwitterApiWhoisWidget::updateHtml() { qCDebug(CHOQOK); QString html; if (d->errorMessage.isEmpty()) { QString url = d->currentPost.author.homePageUrl.isEmpty() ? QString() : QStringLiteral("%1").arg(d->currentPost.author.homePageUrl); QString mainTable = QString(QLatin1String("\ \
\
%1
\
%2

\ @%3 %4%5
\ %6
\ %7
")) .arg(d->imgActions) .arg(d->currentPost.author.realName.toHtmlEscaped()) .arg(d->currentPost.author.userName).arg(d->currentPost.author.location.toHtmlEscaped()) .arg(!d->timeZone.isEmpty() ? QLatin1Char('(') + d->timeZone + QLatin1Char(')') : QString()) .arg(d->currentPost.author.description) .arg(url); QString countTable = QString(QLatin1String("\ \ \ \
%1
") + i18nc("User posts", "Posts") + QLatin1String("
%2
") + i18nc("User friends", "Friends") + QLatin1String("
%3
") + i18nc("User followers" , "Followers") + QLatin1String("

")) .arg(d->statusesCount) .arg(d->friendsCount) .arg(d->followersCount); html = mainTable + countTable; if (!d->currentPost.content.isEmpty()) { html.append(QString(i18n("Last Status: %1
")).arg(d->currentPost.content)); } } else { html = i18n("

%1

", d->errorMessage); } d->wid->setHtml(html); } void TwitterApiWhoisWidget::showForm() { qCDebug(CHOQOK); QPoint pos = d->waitFrame->pos(); d->waitFrame->deleteLater(); d->wid->resize(320, 200); d->wid->document()->setTextWidth(width() - 2); d->wid->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); int h = d->wid->document()->size().toSize().height() + 10; d->wid->setMinimumHeight(h); d->wid->setMaximumHeight(h); this->resize(320, h + 4); int desktopHeight = QApplication::desktop()->height(); int desktopWidth = QApplication::desktop()->width(); if ((pos.x() + this->width()) > desktopWidth) { pos.setX(desktopWidth - width()); } if ((pos.y() + this->height()) > desktopHeight) { pos.setY(desktopHeight - height()); } move(pos); QWidget::show(); } void TwitterApiWhoisWidget::show(QPoint pos) { qCDebug(CHOQOK); d->waitFrame = new QFrame(this); d->waitFrame->setFrameShape(NoFrame); d->waitFrame->setWindowFlags(Qt::Popup); KAnimatedButton *waitButton = new KAnimatedButton; waitButton->setToolTip(i18n("Please wait...")); connect( waitButton, SIGNAL(clicked(bool)), SLOT(slotCancel()) ); waitButton->setAnimationPath(QLatin1String("process-working-kde")); waitButton->start(); QVBoxLayout *ly = new QVBoxLayout(d->waitFrame); ly->setSpacing(0); ly->setContentsMargins(0, 0, 0, 0); ly->addWidget(waitButton); d->waitFrame->move(pos - QPoint(15, 15)); d->waitFrame->show(); } void TwitterApiWhoisWidget::checkAnchor(const QUrl url) { qCDebug(CHOQOK); if (url.scheme() == QLatin1String("choqok")) { if (url.host() == QLatin1String("close")) { this->close(); } else if (url.host() == QLatin1String("subscribe")) { d->mBlog->createFriendship(d->currentAccount, d->username); connect(d->mBlog, SIGNAL(friendshipCreated(Choqok::Account*,QString)), SLOT(slotFriendshipCreated(Choqok::Account*,QString))); } else if (url.host() == QLatin1String("unsubscribe")) { d->mBlog->destroyFriendship(d->currentAccount, d->username); connect(d->mBlog, SIGNAL(friendshipDestroyed(Choqok::Account*,QString)), SLOT(slotFriendshipDestroyed(Choqok::Account*,QString))); } else if (url.host() == QLatin1String("block")) { d->mBlog->blockUser(d->currentAccount, d->username); // connect(d->mBlog, SIGNAL(userBlocked(Choqok::Account*,QString)), SLOT(slotUserBlocked(Choqok::Account*,QString))); } } else { Choqok::openUrl(url); close(); } } void TwitterApiWhoisWidget::setupUi() { qCDebug(CHOQOK); d->wid->document()->addResource(QTextDocument::ImageResource, QUrl(QLatin1String("icon://close")), QIcon::fromTheme(QLatin1String("dialog-close")).pixmap(16)); QString style = QLatin1String("color: %1; background-color: %2"); if (Choqok::AppearanceSettings::isCustomUi()) { setStyleSheet(style.arg(Choqok::AppearanceSettings::readForeColor().name()) .arg(Choqok::AppearanceSettings::readBackColor().name())); } else { QPalette p = window()->palette(); setStyleSheet(style.arg(p.color(QPalette::WindowText).name()).arg(p.color(QPalette::Window).name())); } } void TwitterApiWhoisWidget::slotCancel() { qCDebug(CHOQOK); if (d->waitFrame) { d->waitFrame->deleteLater(); } if (d->job) { d->job->kill(); } this->close(); } void TwitterApiWhoisWidget::setActionImages() { d->imgActions.clear(); if (d->username.compare(d->currentAccount->username(), Qt::CaseInsensitive) != 0) { if (d->currentAccount->friendsList().contains(d->username, Qt::CaseInsensitive)) { d->wid->document()->addResource(QTextDocument::ImageResource, QUrl(QLatin1String("icon://unsubscribe")), QIcon::fromTheme(QLatin1String("list-remove-user")).pixmap(16)); d->imgActions += QLatin1String(" "); } else { d->wid->document()->addResource(QTextDocument::ImageResource, QUrl(QLatin1String("icon://subscribe")), QIcon::fromTheme(QLatin1String("list-add-user")).pixmap(16)); d->imgActions += QLatin1String(" "); } d->wid->document()->addResource(QTextDocument::ImageResource, QUrl(QLatin1String("icon://block")), QIcon::fromTheme(QLatin1String("dialog-cancel")).pixmap(16)); d->imgActions += QLatin1String(""); } } void TwitterApiWhoisWidget::slotFriendshipCreated(Choqok::Account *theAccount, const QString &username) { if (theAccount == d->currentAccount && username == d->username) { setActionImages(); updateHtml(); } } void TwitterApiWhoisWidget::slotFriendshipDestroyed(Choqok::Account *theAccount, const QString &username) { if (theAccount == d->currentAccount && username == d->username) { setActionImages(); updateHtml(); } } // void TwitterApiWhoisWidget::slotUserBlocked(Choqok::Account* theAccount, const QString &username) // { // if(theAccount == d->currentAccount && username == d->username){ // Choqok::NotifyManager::success( i18n("Your posts are blocked for %1.", username) ); // } // } diff --git a/libchoqok/CMakeLists.txt b/libchoqok/CMakeLists.txt index c0129f4e..820e5e20 100644 --- a/libchoqok/CMakeLists.txt +++ b/libchoqok/CMakeLists.txt @@ -1,125 +1,128 @@ include_directories( ${CHOQOK_INCLUDES} ) set(choqok_LIB_SRCS application.cpp libchoqokdebug.cpp plugin.cpp shortener.cpp uploader.cpp account.cpp microblog.cpp pluginmanager.cpp shortenmanager.cpp accountmanager.cpp passwordmanager.cpp mediamanager.cpp notifymanager.cpp choqokuiglobal.cpp choqoktools.cpp + choqokoauthreplyhandler.cpp dbushandler.cpp urlutils.cpp ui/microblogwidget.cpp ui/editaccountwidget.cpp ui/timelinewidget.cpp ui/postwidget.cpp ui/choqoktextedit.cpp ui/composerwidget.cpp ui/quickpost.cpp ui/choqokmainwindow.cpp ui/uploadmediadialog.cpp ui/textbrowser.cpp ui/choqoktabbar.cpp ) #if(QtIndicate_FOUND) # set(choqok_LIB_SRCS ${choqok_LIB_SRCS} indicatormanager.cpp) # include_directories(${QTINDICATE_INCLUDE_DIRS}) # add_definitions(-DQTINDICATE_BUILD) #endif(QtIndicate_FOUND) qt5_add_dbus_adaptor(choqok_LIB_SRCS org.kde.choqok.xml dbushandler.h Choqok::DbusHandler ChoqokAdaptor) kconfig_add_kcfg_files(choqok_LIB_SRCS choqokbehaviorsettings.kcfgc choqokappearancesettings.kcfgc ) ki18n_wrap_ui(choqok_LIB_SRCS ui/uploadmedia_base.ui) add_library(choqok SHARED ${choqok_LIB_SRCS}) target_link_libraries(choqok PUBLIC Qt5::Core Qt5::DBus + Qt5::NetworkAuth Qt5::Widgets KF5::ConfigCore KF5::Emoticons KF5::GuiAddons KF5::I18n KF5::KIOCore KF5::KIOFileWidgets KF5::KCMUtils KF5::JobWidgets KF5::Notifications KF5::SonnetCore KF5::TextWidgets KF5::XmlGui KF5::Wallet ) #if(QtIndicate_FOUND) # add_definitions(-DXDG_APPS_INSTALL_DIR=${XDG_APPS_INSTALL_DIR}) # target_link_libraries(choqok ${QTINDICATE_LIBRARY}) #endif(QtIndicate_FOUND) set_target_properties(choqok PROPERTIES VERSION 1.3.0 SOVERSION 1 ) if(MSVC) set_target_properties(choqok PROPERTIES OUTPUT_NAME libchoqok) endif(MSVC) install(FILES choqokbehaviorsettings.kcfg choqokappearancesettings.kcfg DESTINATION ${KCFG_INSTALL_DIR}) install(TARGETS choqok ${INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES choqokplugin.desktop choqokshortenerplugin.desktop choqokuploaderplugin.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/choqokbehaviorsettings.h ${CMAKE_CURRENT_BINARY_DIR}/choqokappearancesettings.h application.h account.h accountmanager.h choqok_export.h choqoktypes.h + choqokoauthreplyhandler.h choqokuiglobal.h mediamanager.h microblog.h notifymanager.h passwordmanager.h plugin.h pluginmanager.h shortener.h uploader.h shortenmanager.h dbushandler.h choqoktools.h ui/choqoktextedit.h ui/composerwidget.h ui/editaccountwidget.h ui/choqokmainwindow.h ui/microblogwidget.h ui/postwidget.h ui/quickpost.h ui/timelinewidget.h ui/uploadmediadialog.h ui/textbrowser.h ui/choqoktabbar.h DESTINATION ${INCLUDE_INSTALL_DIR}/choqok COMPONENT Devel) diff --git a/libchoqok/choqoktools.h b/libchoqok/choqokoauthreplyhandler.cpp similarity index 70% copy from libchoqok/choqoktools.h copy to libchoqok/choqokoauthreplyhandler.cpp index e0bbbb4d..92b11a17 100644 --- a/libchoqok/choqoktools.h +++ b/libchoqok/choqokoauthreplyhandler.cpp @@ -1,43 +1,38 @@ /* This file is part of Choqok, the KDE micro-blogging client - Copyright (C) 2008-2012 Mehrdad Momeny + Copyright (C) 2017 Andrea Scarpino This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ -#ifndef CHOQOK_CHOQOKTOOLS_H -#define CHOQOK_CHOQOKTOOLS_H +#include "choqokoauthreplyhandler.h" -#include -#include - -#include "choqok_export.h" - -namespace Choqok +ChoqokOAuthReplyHandler::ChoqokOAuthReplyHandler(QObject *parent) + : QOAuthHttpServerReplyHandler(parent) { +} -void CHOQOK_EXPORT openUrl(const QUrl &url); - -QString CHOQOK_EXPORT qoauthErrorText(int code); - -QString CHOQOK_EXPORT getColorString(const QColor &color); - +ChoqokOAuthReplyHandler::~ChoqokOAuthReplyHandler() +{ } -#endif // CHOQOK_CHOQOKTOOLS_H +QString ChoqokOAuthReplyHandler::callback() const +{ + return QLatin1String("oob"); +} diff --git a/libchoqok/choqoktools.h b/libchoqok/choqokoauthreplyhandler.h similarity index 68% copy from libchoqok/choqoktools.h copy to libchoqok/choqokoauthreplyhandler.h index e0bbbb4d..1621ef7a 100644 --- a/libchoqok/choqoktools.h +++ b/libchoqok/choqokoauthreplyhandler.h @@ -1,43 +1,41 @@ /* This file is part of Choqok, the KDE micro-blogging client - Copyright (C) 2008-2012 Mehrdad Momeny + Copyright (C) 2017 Andrea Scarpino This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ -#ifndef CHOQOK_CHOQOKTOOLS_H -#define CHOQOK_CHOQOKTOOLS_H +#ifndef CHOQOKOAUTHREPLYHANDLER_H +#define CHOQOKOAUTHREPLYHANDLER_H -#include -#include +#include #include "choqok_export.h" -namespace Choqok +class CHOQOK_EXPORT ChoqokOAuthReplyHandler : public QOAuthHttpServerReplyHandler { + Q_OBJECT +public: + explicit ChoqokOAuthReplyHandler(QObject *parent = nullptr); + ~ChoqokOAuthReplyHandler(); -void CHOQOK_EXPORT openUrl(const QUrl &url); + QString callback() const override; +}; -QString CHOQOK_EXPORT qoauthErrorText(int code); - -QString CHOQOK_EXPORT getColorString(const QColor &color); - -} - -#endif // CHOQOK_CHOQOKTOOLS_H +#endif // CHOQOKOAUTHREPLYHANDLER_H diff --git a/libchoqok/choqoktools.cpp b/libchoqok/choqoktools.cpp index 0bfad33c..f8b859bf 100644 --- a/libchoqok/choqoktools.cpp +++ b/libchoqok/choqoktools.cpp @@ -1,92 +1,60 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2008-2012 Mehrdad Momeny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #include "choqoktools.h" #include "choqokuiglobal.h" #include #include #include -#include - #include "choqokbehaviorsettings.h" void Choqok::openUrl(const QUrl &url) { bool urlOpeningFailed = false; QString failureMessage; if (Choqok::BehaviorSettings::useCustomBrowser()) { QStringList args = Choqok::BehaviorSettings::customBrowser().split(QLatin1Char(' ')); args.append(url.toString()); if (KProcess::startDetached(args) == 0) { urlOpeningFailed = true; failureMessage = i18n("Custom web browser \"%1\" is unable to open url \"%2\".\nPlease update the custom web browser in Configurations.", Choqok::BehaviorSettings::customBrowser(), url.toDisplayString()); } - } else { - if( QDesktopServices::openUrl(url) == false ) { - urlOpeningFailed = true; - failureMessage = i18n("Unable to open url \"%1\".\nPlease check Qt installation or set a custom web browser in Configurations.", - url.toDisplayString()); - } + } else if(!QDesktopServices::openUrl(url)) { + urlOpeningFailed = true; + failureMessage = i18n("Unable to open url \"%1\".\nPlease check Qt installation or set a custom web browser in Configurations.", + url.toDisplayString()); } if (urlOpeningFailed) KMessageBox::error(Choqok::UI::Global::mainWindow(), failureMessage); } -QString Choqok::qoauthErrorText(int code) -{ - switch (code) { - case QOAuth::NoError: - return i18n("No Error"); - case QOAuth::BadRequest: - return i18n("Bad request"); - case QOAuth::ConsumerKeyEmpty: - case QOAuth::ConsumerSecretEmpty: - return i18n("Consumer Key or Secret has not been provided"); - case QOAuth::Forbidden: - return i18n("Forbidden"); - case QOAuth::Timeout: - return i18n("Timeout on server"); - case QOAuth::Unauthorized: - return i18n("Authorization Error"); - case QOAuth::UnsupportedHttpMethod: - return i18n("Internal Error"); - case QOAuth::OtherError: - case QOAuth::RSADecodingError: - case QOAuth::RSAKeyFileError: - case QOAuth::RSAPrivateKeyEmpty: - return i18n("Unknown Error"); - default: - return QString(); - } -} - QString Choqok::getColorString(const QColor &color) { return QLatin1String("rgb(") + QString::number(color.red()) + QLatin1Char(',') + QString::number(color.green()) + QLatin1Char(',') + QString::number(color.blue()) + QLatin1Char(')'); } diff --git a/libchoqok/choqoktools.h b/libchoqok/choqoktools.h index e0bbbb4d..f92b95b0 100644 --- a/libchoqok/choqoktools.h +++ b/libchoqok/choqoktools.h @@ -1,43 +1,41 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2008-2012 Mehrdad Momeny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #ifndef CHOQOK_CHOQOKTOOLS_H #define CHOQOK_CHOQOKTOOLS_H #include #include #include "choqok_export.h" namespace Choqok { void CHOQOK_EXPORT openUrl(const QUrl &url); -QString CHOQOK_EXPORT qoauthErrorText(int code); - QString CHOQOK_EXPORT getColorString(const QColor &color); } #endif // CHOQOK_CHOQOKTOOLS_H diff --git a/microblogs/friendica/CMakeLists.txt b/microblogs/friendica/CMakeLists.txt index 564b38ff..1d7bc995 100644 --- a/microblogs/friendica/CMakeLists.txt +++ b/microblogs/friendica/CMakeLists.txt @@ -1,34 +1,34 @@ include_directories( ${CHOQOK_INCLUDES} ) set(choqok_friendica_SRCS friendicadebug.cpp friendicaeditaccount.cpp friendicamicroblog.cpp ) ki18n_wrap_ui(choqok_friendica_SRCS friendicaeditaccount_base.ui) add_library(choqok_friendica MODULE ${choqok_friendica_SRCS}) kcoreaddons_desktop_to_json(choqok_friendica choqok_friendica.desktop) target_link_libraries(choqok_friendica PUBLIC Qt5::Core + Qt5::NetworkAuth Qt5::Widgets KF5::CoreAddons KF5::I18n KF5::KIOCore KF5::KIOWidgets choqok gnusocialapihelper - ${QTOAUTH_LIBRARY} ) install(TARGETS choqok_friendica DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES choqok_friendica.desktop DESTINATION ${SERVICES_INSTALL_DIR}) add_subdirectory(icons) diff --git a/microblogs/friendica/friendicaeditaccount.cpp b/microblogs/friendica/friendicaeditaccount.cpp index 99d6ea6c..775fe751 100644 --- a/microblogs/friendica/friendicaeditaccount.cpp +++ b/microblogs/friendica/friendicaeditaccount.cpp @@ -1,306 +1,301 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2016 Andrea Scarpino This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #include "friendicaeditaccount.h" #include #include #include -#include -#include - #include "accountmanager.h" #include "choqoktools.h" #include "gnusocialapiaccount.h" #include "friendicadebug.h" #include "friendicamicroblog.h" FriendicaEditAccountWidget::FriendicaEditAccountWidget(FriendicaMicroBlog *microblog, GNUSocialApiAccount *account, QWidget *parent) : ChoqokEditAccountWidget(account, parent), mAccount(account), progress(0), isAuthenticated(false) { setupUi(this); // setAuthenticated(false); - oauthConsumerKey = "747d09d8e7b9417f5835f04510cb86ed";//Identi.ca tokens - oauthConsumerSecret = "57605f8507a041525a2d5c0abef15b20"; // connect(kcfg_authorize, SIGNAL(clicked(bool)), SLOT(authorizeUser())); // connect(kcfg_authMethod, SIGNAL(currentIndexChanged(int)), SLOT(slotAuthMethodChanged(int))); // slotAuthMethodChanged(kcfg_authMethod->currentIndex()); connect(kcfg_host, SIGNAL(editingFinished()), SLOT(slotCheckHostUrl())); if (mAccount) { kcfg_alias->setText(mAccount->alias()); kcfg_host->setText(mAccount->host()); kcfg_api->setText(mAccount->api()); // kcfg_oauthUsername->setText( mAccount->username() ); kcfg_basicUsername->setText(mAccount->username()); kcfg_basicPassword->setText(mAccount->password()); kcfg_changeExclamationMark->setChecked(mAccount->isChangeExclamationMark()); kcfg_changeToString->setText(mAccount->changeExclamationMarkToText()); // if(mAccount->usingOAuth()){ // if( !mAccount->oauthConsumerKey().isEmpty() && // !mAccount->oauthConsumerSecret().isEmpty() && // !mAccount->oauthToken().isEmpty() && // !mAccount->oauthTokenSecret().isEmpty() ) { // setAuthenticated(true); // oauthConsumerKey = mAccount->oauthConsumerKey(); // oauthConsumerSecret = mAccount->oauthConsumerSecret(); // token = mAccount->oauthToken(); // tokenSecret = mAccount->oauthTokenSecret(); // } else { // setAuthenticated(false); // } // kcfg_authMethod->setCurrentIndex(0); // } else { // kcfg_authMethod->setCurrentIndex(1); // } } else { // kcfg_authMethod->setCurrentIndex(0); QString newAccountAlias = microblog->serviceName(); QString servName = newAccountAlias; int counter = 1; while (Choqok::AccountManager::self()->findAccount(newAccountAlias)) { newAccountAlias = QStringLiteral("%1%2").arg(servName).arg(counter); counter++; } setAccount(mAccount = new GNUSocialApiAccount(microblog, newAccountAlias)); kcfg_alias->setText(newAccountAlias); const QRegExp userRegExp(QLatin1String("([a-z0-9]){1,64}"), Qt::CaseInsensitive); QValidator *userVal = new QRegExpValidator(userRegExp, 0); kcfg_basicUsername->setValidator(userVal); } loadTimelinesTableState(); kcfg_alias->setFocus(Qt::OtherFocusReason); } FriendicaEditAccountWidget::~FriendicaEditAccountWidget() { } bool FriendicaEditAccountWidget::validateData() { // if( kcfg_authMethod->currentIndex()==0 ) {//OAuth // if(kcfg_alias->text().isEmpty() || kcfg_oauthUsername->text().isEmpty() || !isAuthenticated) // return false; // } else {//Basic if (kcfg_alias->text().isEmpty() || kcfg_basicUsername->text().isEmpty() || kcfg_basicPassword->text().isEmpty()) { return false; } // } return true; } Choqok::Account *FriendicaEditAccountWidget::apply() { qCDebug(CHOQOK); /*if(kcfg_authMethod->currentIndex() == 0){ mAccount->setUsername( kcfg_oauthUsername->text() ); mAccount->setOauthToken( token ); mAccount->setOauthConsumerKey( oauthConsumerKey ); mAccount->setOauthConsumerSecret( oauthConsumerSecret ); mAccount->setOauthTokenSecret( tokenSecret ); mAccount->setUsingOAuth(true); } else*/ { mAccount->setUsername(kcfg_basicUsername->text()); mAccount->setPassword(kcfg_basicPassword->text()); mAccount->setUsingOAuth(false); } mAccount->setHost(kcfg_host->text()); mAccount->setApi(kcfg_api->text()); mAccount->setAlias(kcfg_alias->text()); mAccount->setChangeExclamationMark(kcfg_changeExclamationMark->isChecked()); mAccount->setChangeExclamationMarkToText(kcfg_changeToString->text()); saveTimelinesTableState(); setTextLimit(); mAccount->writeConfig(); return mAccount; } // void FriendicaEditAccountWidget::authorizeUser() // { // qCDebug(CHOQOK); // slotCheckHostUrl(); // if(QUrl(kcfg_host->text()).host()!="identi.ca"){ // KMessageBox::sorry(this, i18n("Sorry, OAuth Method just works with Identi.ca server. You have to use basic authentication for other StatusNet servers.")); // kcfg_authMethod->setCurrentIndex(1); // return; // } // qoauth = new QOAuth::Interface(new KIO::Integration::AccessManager(this), this); // //TODO change this to have support for self hosted StatusNets // qoauth->setConsumerKey( oauthConsumerKey ); // qoauth->setConsumerSecret( oauthConsumerSecret ); // qoauth->setRequestTimeout( 10000 ); // // // send a request for an unauthorized token // QString oauthReqTokenUrl = QString("%1/%2/oauth/request_token").arg(kcfg_host->text()).arg(kcfg_api->text()); // // qCDebug(CHOQOK)<requestToken( oauthReqTokenUrl, QOAuth::GET, QOAuth::HMAC_SHA1, params ); // setAuthenticated(false); // kcfg_authorize->setIcon(QIcon::fromTheme("object-locked")); // // // if no error occurred, read the received token and token secret // if ( qoauth->error() == QOAuth::NoError ) { // token = reply.value( QOAuth::tokenParameterName() ); // tokenSecret = reply.value( QOAuth::tokenSecretParameterName() ); // qCDebug(CHOQOK)<<"token: "<text()).arg(kcfg_api->text())); // url.addQueryItem( QOAuth::tokenParameterName(), token ); // url.addQueryItem( "oauth_token", token ); // Choqok::openUrl(url); // kcfg_authorize->setEnabled(false); // getPinCode(); // } else { // qCDebug(CHOQOK)<<"ERROR:" <error()<error()); // KMessageBox::detailedError(this, i18n("Authentication Error"), // Choqok::qoauthErrorText(qoauth->error())); // } // } // // void FriendicaEditAccountWidget::getPinCode() // { // isAuthenticated = false; // while(!isAuthenticated){ // QString verifier = KInputDialog::getText( i18n("Security code"), // i18nc("Security code recieved from StatusNet", // "Enter security code:")); // if(verifier.isEmpty()) // return; // QOAuth::ParamMap otherArgs; // otherArgs.insert( "oauth_verifier", verifier.toUtf8() ); // // QOAuth::ParamMap reply = // qoauth->accessToken( QString("%1/%2/oauth/access_token").arg(kcfg_host->text()).arg(kcfg_api->text()), // QOAuth::GET, token, tokenSecret, QOAuth::HMAC_SHA1, otherArgs ); // // if no error occurred, read the Access Token (and other arguments, if applicable) // if ( qoauth->error() == QOAuth::NoError ) { // sender()->deleteLater(); // kcfg_authorize->setEnabled(true); // token = reply.value( QOAuth::tokenParameterName() ); // tokenSecret = reply.value( QOAuth::tokenSecretParameterName() ); // qCDebug(CHOQOK)<<"token: "<error()<<' '<error()); // KMessageBox::detailedError(this, i18n("Authentication Error"), // Choqok::qoauthErrorText(qoauth->error())); // } // } // } void FriendicaEditAccountWidget::setTextLimit() { QString url = mAccount->host() + QLatin1Char('/') + mAccount->api() + QLatin1String("/statusnet/config.json"); KIO::StoredTransferJob *job = KIO::storedGet(QUrl(url), KIO::Reload, KIO::HideProgressInfo); job->exec(); if (job->error()) { qCCritical(CHOQOK) << "Job error:" << job->errorString(); return; } const QJsonDocument json = QJsonDocument::fromJson(job->data()); if (!json.isNull()) { const QVariantMap siteInfos = json.toVariant().toMap()[QLatin1String("site")].toMap(); bool ok; mAccount->setPostCharLimit(siteInfos[QLatin1String("textlimit")].toUInt(&ok)); if (!ok) { qCDebug(CHOQOK) << "Cannot parse text limit value"; mAccount->setPostCharLimit(140); } } else { qCDebug(CHOQOK) << "Cannot parse JSON reply"; } } void FriendicaEditAccountWidget::loadTimelinesTableState() { for (const QString &timeline: mAccount->microblog()->timelineNames()) { int newRow = timelinesTable->rowCount(); timelinesTable->insertRow(newRow); Choqok::TimelineInfo *info = mAccount->microblog()->timelineInfo(timeline); QTableWidgetItem *item = new QTableWidgetItem(info->name); item->setData(32, timeline); item->setToolTip(info->description); timelinesTable->setItem(newRow, 0, item); QCheckBox *enable = new QCheckBox(timelinesTable); enable->setChecked(mAccount->timelineNames().contains(timeline)); timelinesTable->setCellWidget(newRow, 1, enable); } } void FriendicaEditAccountWidget::saveTimelinesTableState() { QStringList timelines; int rowCount = timelinesTable->rowCount(); for (int i = 0; i < rowCount; ++i) { QCheckBox *enable = qobject_cast(timelinesTable->cellWidget(i, 1)); if (enable && enable->isChecked()) { timelines << timelinesTable->item(i, 0)->data(32).toString(); } } timelines.removeDuplicates(); mAccount->setTimelineNames(timelines); } // void FriendicaEditAccountWidget::slotAuthMethodChanged(int index) // { // if(index == 0){ // kcfg_BasicBox->hide(); // kcfg_OAuthBox->show(); // } else { // kcfg_BasicBox->show(); // kcfg_OAuthBox->hide(); // } // } // void FriendicaEditAccountWidget::setAuthenticated(bool authenticated) // { // isAuthenticated = authenticated; // if(authenticated){ // kcfg_authorize->setIcon(QIcon::fromTheme("object-unlocked")); // kcfg_authenticateLed->on(); // kcfg_authenticateStatus->setText(i18n("Authenticated")); // } else { // kcfg_authorize->setIcon(QIcon::fromTheme("object-locked")); // kcfg_authenticateLed->off(); // kcfg_authenticateStatus->setText(i18n("Not Authenticated")); // } // } void FriendicaEditAccountWidget::slotCheckHostUrl() { if (!kcfg_host->text().isEmpty() && !kcfg_host->text().startsWith(QLatin1String("http"), Qt::CaseInsensitive) && !kcfg_host->text().startsWith(QLatin1String("https"))) { kcfg_host->setText(kcfg_host->text().prepend(QLatin1String("https://"))); } } diff --git a/microblogs/friendica/friendicaeditaccount.h b/microblogs/friendica/friendicaeditaccount.h index 976341d0..94d4846c 100644 --- a/microblogs/friendica/friendicaeditaccount.h +++ b/microblogs/friendica/friendicaeditaccount.h @@ -1,87 +1,76 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2016 Andrea Scarpino This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #ifndef FRIENDICAEDITACCOUNT_H #define FRIENDICAEDITACCOUNT_H #include #include "editaccountwidget.h" #include "ui_friendicaeditaccount_base.h" -namespace QOAuth -{ -class Interface; -} - class QProgressBar; class GNUSocialApiAccount; class FriendicaMicroBlog; class FriendicaEditAccountWidget : public ChoqokEditAccountWidget, public Ui::FriendicaEditAccountBase { Q_OBJECT public: FriendicaEditAccountWidget(FriendicaMicroBlog *microblog, GNUSocialApiAccount *account, QWidget *parent); /** * Destructor */ ~FriendicaEditAccountWidget(); virtual bool validateData() override; /** * Create a new account if we are in the 'add account wizard', * otherwise update the existing account. * @Return new or modified account. OR nullptr on failure. */ virtual Choqok::Account *apply() override; protected Q_SLOTS: // virtual void authorizeUser(); // void slotAuthMethodChanged(int); void slotCheckHostUrl(); // void getAccessToken(); protected: // virtual void getPinCode(); void loadTimelinesTableState(); void saveTimelinesTableState(); // void setAuthenticated(bool authenticated); void setTextLimit(); FriendicaMicroBlog *mBlog; GNUSocialApiAccount *mAccount; QProgressBar *progress; bool isAuthenticated; - - QByteArray token; - QByteArray tokenSecret; - QByteArray oauthConsumerKey; - QByteArray oauthConsumerSecret; - QOAuth::Interface *qoauth; }; #endif // FRIENDICAEDITACCOUNT_H diff --git a/microblogs/laconica/CMakeLists.txt b/microblogs/laconica/CMakeLists.txt index 94bba83e..398d7dea 100644 --- a/microblogs/laconica/CMakeLists.txt +++ b/microblogs/laconica/CMakeLists.txt @@ -1,34 +1,34 @@ include_directories( ${CHOQOK_INCLUDES} ) set(choqok_laconica_SRCS laconicadebug.cpp laconicaeditaccount.cpp laconicamicroblog.cpp ) ki18n_wrap_ui(choqok_laconica_SRCS laconicaeditaccount_base.ui) add_library(choqok_laconica MODULE ${choqok_laconica_SRCS}) kcoreaddons_desktop_to_json(choqok_laconica choqok_laconica.desktop) target_link_libraries(choqok_laconica PUBLIC Qt5::Core + Qt5::NetworkAuth Qt5::Widgets KF5::CoreAddons KF5::I18n KF5::KIOCore KF5::KIOWidgets choqok gnusocialapihelper - ${QTOAUTH_LIBRARY} ) install(TARGETS choqok_laconica DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES choqok_laconica.desktop DESTINATION ${SERVICES_INSTALL_DIR}) add_subdirectory(icons) diff --git a/microblogs/laconica/laconicaeditaccount.cpp b/microblogs/laconica/laconicaeditaccount.cpp index 9214f86d..d3ce22da 100644 --- a/microblogs/laconica/laconicaeditaccount.cpp +++ b/microblogs/laconica/laconicaeditaccount.cpp @@ -1,306 +1,303 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2008-2012 Mehrdad Momeny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #include "laconicaeditaccount.h" #include #include #include -#include -#include - #include "accountmanager.h" #include "choqoktools.h" #include "gnusocialapiaccount.h" #include "laconicadebug.h" #include "laconicamicroblog.h" LaconicaEditAccountWidget::LaconicaEditAccountWidget(LaconicaMicroBlog *microblog, GNUSocialApiAccount *account, QWidget *parent) : ChoqokEditAccountWidget(account, parent), mAccount(account), progress(0), isAuthenticated(false) { setupUi(this); // setAuthenticated(false); - oauthConsumerKey = "747d09d8e7b9417f5835f04510cb86ed";//Identi.ca tokens - oauthConsumerSecret = "57605f8507a041525a2d5c0abef15b20"; +// oauthConsumerKey = "747d09d8e7b9417f5835f04510cb86ed";//Identi.ca tokens +// oauthConsumerSecret = "57605f8507a041525a2d5c0abef15b20"; // connect(kcfg_authorize, SIGNAL(clicked(bool)), SLOT(authorizeUser())); // connect(kcfg_authMethod, SIGNAL(currentIndexChanged(int)), SLOT(slotAuthMethodChanged(int))); // slotAuthMethodChanged(kcfg_authMethod->currentIndex()); connect(kcfg_host, SIGNAL(editingFinished()), SLOT(slotCheckHostUrl())); if (mAccount) { kcfg_alias->setText(mAccount->alias()); kcfg_host->setText(mAccount->host()); kcfg_api->setText(mAccount->api()); // kcfg_oauthUsername->setText( mAccount->username() ); kcfg_basicUsername->setText(mAccount->username()); kcfg_basicPassword->setText(mAccount->password()); kcfg_changeExclamationMark->setChecked(mAccount->isChangeExclamationMark()); kcfg_changeToString->setText(mAccount->changeExclamationMarkToText()); // if(mAccount->usingOAuth()){ // if( !mAccount->oauthConsumerKey().isEmpty() && // !mAccount->oauthConsumerSecret().isEmpty() && // !mAccount->oauthToken().isEmpty() && // !mAccount->oauthTokenSecret().isEmpty() ) { // setAuthenticated(true); // oauthConsumerKey = mAccount->oauthConsumerKey(); // oauthConsumerSecret = mAccount->oauthConsumerSecret(); // token = mAccount->oauthToken(); // tokenSecret = mAccount->oauthTokenSecret(); // } else { // setAuthenticated(false); // } // kcfg_authMethod->setCurrentIndex(0); // } else { // kcfg_authMethod->setCurrentIndex(1); // } } else { // kcfg_authMethod->setCurrentIndex(0); QString newAccountAlias = microblog->serviceName(); QString servName = newAccountAlias; int counter = 1; while (Choqok::AccountManager::self()->findAccount(newAccountAlias)) { newAccountAlias = QStringLiteral("%1%2").arg(servName).arg(counter); counter++; } setAccount(mAccount = new GNUSocialApiAccount(microblog, newAccountAlias)); kcfg_alias->setText(newAccountAlias); const QRegExp userRegExp(QLatin1String("([a-z0-9]){1,64}"), Qt::CaseInsensitive); QValidator *userVal = new QRegExpValidator(userRegExp, 0); kcfg_basicUsername->setValidator(userVal); } loadTimelinesTableState(); kcfg_alias->setFocus(Qt::OtherFocusReason); } LaconicaEditAccountWidget::~LaconicaEditAccountWidget() { } bool LaconicaEditAccountWidget::validateData() { // if( kcfg_authMethod->currentIndex()==0 ) {//OAuth // if(kcfg_alias->text().isEmpty() || kcfg_oauthUsername->text().isEmpty() || !isAuthenticated) // return false; // } else {//Basic if (kcfg_alias->text().isEmpty() || kcfg_basicUsername->text().isEmpty() || kcfg_basicPassword->text().isEmpty()) { return false; } // } return true; } Choqok::Account *LaconicaEditAccountWidget::apply() { qCDebug(CHOQOK); /*if(kcfg_authMethod->currentIndex() == 0){ mAccount->setUsername( kcfg_oauthUsername->text() ); mAccount->setOauthToken( token ); mAccount->setOauthConsumerKey( oauthConsumerKey ); mAccount->setOauthConsumerSecret( oauthConsumerSecret ); mAccount->setOauthTokenSecret( tokenSecret ); mAccount->setUsingOAuth(true); } else*/ { mAccount->setUsername(kcfg_basicUsername->text()); mAccount->setPassword(kcfg_basicPassword->text()); mAccount->setUsingOAuth(false); } mAccount->setHost(kcfg_host->text()); mAccount->setApi(kcfg_api->text()); mAccount->setAlias(kcfg_alias->text()); mAccount->setChangeExclamationMark(kcfg_changeExclamationMark->isChecked()); mAccount->setChangeExclamationMarkToText(kcfg_changeToString->text()); saveTimelinesTableState(); setTextLimit(); mAccount->writeConfig(); return mAccount; } // void LaconicaEditAccountWidget::authorizeUser() // { // qCDebug(CHOQOK); // slotCheckHostUrl(); // if(QUrl(kcfg_host->text()).host()!="identi.ca"){ // KMessageBox::sorry(this, i18n("Sorry, OAuth Method just works with Identi.ca server. You have to use basic authentication for other StatusNet servers.")); // kcfg_authMethod->setCurrentIndex(1); // return; // } // qoauth = new QOAuth::Interface(new KIO::Integration::AccessManager(this), this); // //TODO change this to have support for self hosted StatusNets // qoauth->setConsumerKey( oauthConsumerKey ); // qoauth->setConsumerSecret( oauthConsumerSecret ); // qoauth->setRequestTimeout( 10000 ); // // // send a request for an unauthorized token // QString oauthReqTokenUrl = QString("%1/%2/oauth/request_token").arg(kcfg_host->text()).arg(kcfg_api->text()); // // qCDebug(CHOQOK)<requestToken( oauthReqTokenUrl, QOAuth::GET, QOAuth::HMAC_SHA1, params ); // setAuthenticated(false); // kcfg_authorize->setIcon(QIcon::fromTheme("object-locked")); // // // if no error occurred, read the received token and token secret // if ( qoauth->error() == QOAuth::NoError ) { // token = reply.value( QOAuth::tokenParameterName() ); // tokenSecret = reply.value( QOAuth::tokenSecretParameterName() ); // qCDebug(CHOQOK)<<"token: "<text()).arg(kcfg_api->text())); // url.addQueryItem( QOAuth::tokenParameterName(), token ); // url.addQueryItem( "oauth_token", token ); // Choqok::openUrl(url); // kcfg_authorize->setEnabled(false); // getPinCode(); // } else { // qCDebug(CHOQOK)<<"ERROR:" <error()<error()); // KMessageBox::detailedError(this, i18n("Authentication Error"), // Choqok::qoauthErrorText(qoauth->error())); // } // } // // void LaconicaEditAccountWidget::getPinCode() // { // isAuthenticated = false; // while(!isAuthenticated){ // QString verifier = KInputDialog::getText( i18n("Security code"), // i18nc("Security code recieved from StatusNet", // "Enter security code:")); // if(verifier.isEmpty()) // return; // QOAuth::ParamMap otherArgs; // otherArgs.insert( "oauth_verifier", verifier.toUtf8() ); // // QOAuth::ParamMap reply = // qoauth->accessToken( QString("%1/%2/oauth/access_token").arg(kcfg_host->text()).arg(kcfg_api->text()), // QOAuth::GET, token, tokenSecret, QOAuth::HMAC_SHA1, otherArgs ); // // if no error occurred, read the Access Token (and other arguments, if applicable) // if ( qoauth->error() == QOAuth::NoError ) { // sender()->deleteLater(); // kcfg_authorize->setEnabled(true); // token = reply.value( QOAuth::tokenParameterName() ); // tokenSecret = reply.value( QOAuth::tokenSecretParameterName() ); // qCDebug(CHOQOK)<<"token: "<error()<<' '<error()); // KMessageBox::detailedError(this, i18n("Authentication Error"), // Choqok::qoauthErrorText(qoauth->error())); // } // } // } void LaconicaEditAccountWidget::setTextLimit() { QString url = mAccount->host() + QLatin1Char('/') + mAccount->api() + QLatin1String("/statusnet/config.json"); KIO::StoredTransferJob *job = KIO::storedGet(QUrl(url), KIO::Reload, KIO::HideProgressInfo); job->exec(); if (job->error()) { qCCritical(CHOQOK) << "Job error:" << job->errorString(); return; } const QJsonDocument json = QJsonDocument::fromJson(job->data()); if (!json.isNull()) { const QVariantMap siteInfos = json.toVariant().toMap()[QLatin1String("site")].toMap(); bool ok; mAccount->setPostCharLimit(siteInfos[QLatin1String("textlimit")].toUInt(&ok)); if (!ok) { qCDebug(CHOQOK) << "Cannot parse text limit value"; mAccount->setPostCharLimit(140); } } else { qCDebug(CHOQOK) << "Cannot parse JSON reply"; } } void LaconicaEditAccountWidget::loadTimelinesTableState() { for (const QString &timeline: mAccount->microblog()->timelineNames()) { int newRow = timelinesTable->rowCount(); timelinesTable->insertRow(newRow); Choqok::TimelineInfo *info = mAccount->microblog()->timelineInfo(timeline); QTableWidgetItem *item = new QTableWidgetItem(info->name); item->setData(32, timeline); item->setToolTip(info->description); timelinesTable->setItem(newRow, 0, item); QCheckBox *enable = new QCheckBox(timelinesTable); enable->setChecked(mAccount->timelineNames().contains(timeline)); timelinesTable->setCellWidget(newRow, 1, enable); } } void LaconicaEditAccountWidget::saveTimelinesTableState() { QStringList timelines; int rowCount = timelinesTable->rowCount(); for (int i = 0; i < rowCount; ++i) { QCheckBox *enable = qobject_cast(timelinesTable->cellWidget(i, 1)); if (enable && enable->isChecked()) { timelines << timelinesTable->item(i, 0)->data(32).toString(); } } timelines.removeDuplicates(); mAccount->setTimelineNames(timelines); } // void LaconicaEditAccountWidget::slotAuthMethodChanged(int index) // { // if(index == 0){ // kcfg_BasicBox->hide(); // kcfg_OAuthBox->show(); // } else { // kcfg_BasicBox->show(); // kcfg_OAuthBox->hide(); // } // } // void LaconicaEditAccountWidget::setAuthenticated(bool authenticated) // { // isAuthenticated = authenticated; // if(authenticated){ // kcfg_authorize->setIcon(QIcon::fromTheme("object-unlocked")); // kcfg_authenticateLed->on(); // kcfg_authenticateStatus->setText(i18n("Authenticated")); // } else { // kcfg_authorize->setIcon(QIcon::fromTheme("object-locked")); // kcfg_authenticateLed->off(); // kcfg_authenticateStatus->setText(i18n("Not Authenticated")); // } // } void LaconicaEditAccountWidget::slotCheckHostUrl() { if (!kcfg_host->text().isEmpty() && !kcfg_host->text().startsWith(QLatin1String("http"), Qt::CaseInsensitive) && !kcfg_host->text().startsWith(QLatin1String("https"))) { kcfg_host->setText(kcfg_host->text().prepend(QLatin1String("https://"))); } } diff --git a/microblogs/laconica/laconicaeditaccount.h b/microblogs/laconica/laconicaeditaccount.h index 66235c3d..12c7c08f 100644 --- a/microblogs/laconica/laconicaeditaccount.h +++ b/microblogs/laconica/laconicaeditaccount.h @@ -1,90 +1,79 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2008-2012 Mehrdad Momeny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #ifndef LACONICAEDITACCOUNT_H #define LACONICAEDITACCOUNT_H #include #include "editaccountwidget.h" #include "ui_laconicaeditaccount_base.h" -namespace QOAuth -{ -class Interface; -} - class QProgressBar; class GNUSocialApiAccount; class LaconicaMicroBlog; /** @author Mehrdad Momeny \ */ class LaconicaEditAccountWidget : public ChoqokEditAccountWidget, public Ui::LaconicaEditAccountBase { Q_OBJECT public: LaconicaEditAccountWidget(LaconicaMicroBlog *microblog, GNUSocialApiAccount *account, QWidget *parent); /** * Destructor */ ~LaconicaEditAccountWidget(); virtual bool validateData() override; /** * Create a new account if we are in the 'add account wizard', * otherwise update the existing account. * @Return new or modified account. OR nullptr on failure. */ virtual Choqok::Account *apply() override; protected Q_SLOTS: // virtual void authorizeUser(); // void slotAuthMethodChanged(int); void slotCheckHostUrl(); // void getAccessToken(); protected: // virtual void getPinCode(); void loadTimelinesTableState(); void saveTimelinesTableState(); // void setAuthenticated(bool authenticated); void setTextLimit(); LaconicaMicroBlog *mBlog; GNUSocialApiAccount *mAccount; QProgressBar *progress; bool isAuthenticated; - - QByteArray token; - QByteArray tokenSecret; - QByteArray oauthConsumerKey; - QByteArray oauthConsumerSecret; - QOAuth::Interface *qoauth; }; #endif // LACONICAEDITACCOUNT_H diff --git a/microblogs/pumpio/CMakeLists.txt b/microblogs/pumpio/CMakeLists.txt index 6858b7b3..438a35a5 100644 --- a/microblogs/pumpio/CMakeLists.txt +++ b/microblogs/pumpio/CMakeLists.txt @@ -1,45 +1,46 @@ include_directories( ${CHOQOK_INCLUDES} ) set(choqok_pumpio_SRCS pumpioaccount.cpp pumpiocomposerwidget.cpp pumpiodebug.cpp pumpioeditaccountwidget.cpp pumpiomessagedialog.cpp pumpiomicroblog.cpp pumpiomicroblogwidget.cpp + pumpiooauth.cpp pumpiopost.cpp pumpiopostwidget.cpp pumpioshowthread.cpp ) ki18n_wrap_ui(choqok_pumpio_SRCS pumpioeditaccountwidget.ui pumpiomessagedialog.ui pumpioshowthread.ui ) add_library(choqok_pumpio MODULE ${choqok_pumpio_SRCS}) kcoreaddons_desktop_to_json(choqok_pumpio choqok_pumpio.desktop) target_link_libraries(choqok_pumpio PUBLIC Qt5::Core Qt5::Gui + Qt5::NetworkAuth Qt5::Widgets KF5::I18n KF5::KIOCore KF5::KIOWidgets KF5::WidgetsAddons qca-qt5 - ${QTOAUTH_LIBRARY} choqok ) install(TARGETS choqok_pumpio DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES choqok_pumpio.desktop DESTINATION ${SERVICES_INSTALL_DIR}) add_subdirectory(icons) diff --git a/microblogs/pumpio/pumpioaccount.cpp b/microblogs/pumpio/pumpioaccount.cpp index 63fa2d94..59d64072 100644 --- a/microblogs/pumpio/pumpioaccount.cpp +++ b/microblogs/pumpio/pumpioaccount.cpp @@ -1,196 +1,196 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2013 Andrea Scarpino This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #include "pumpioaccount.h" #include #include "passwordmanager.h" #include "pumpiomicroblog.h" class PumpIOAccount::Private { public: QString consumerKey; QString consumerSecret; QString host; QString token; QString tokenSecret; QStringList following; QVariantList lists; - QOAuth::Interface *oAuth; + PumpIOOAuth *oAuth; QStringList timelineNames; }; PumpIOAccount::PumpIOAccount(PumpIOMicroBlog *parent, const QString &alias): Account(parent, alias), d(new Private) { d->host = configGroup()->readEntry("Host", QString()); d->token = configGroup()->readEntry("Token", QString()); d->consumerKey = configGroup()->readEntry("ConsumerKey", QString()); d->consumerSecret = Choqok::PasswordManager::self()->readPassword(QStringLiteral("%1_consumerSecret").arg(alias)); d->tokenSecret = Choqok::PasswordManager::self()->readPassword(QStringLiteral("%1_tokenSecret").arg(alias)); - d->oAuth = new QOAuth::Interface(new KIO::AccessManager(this), this); - d->oAuth->setConsumerKey(d->consumerKey.toLocal8Bit()); - d->oAuth->setConsumerSecret(d->consumerSecret.toLocal8Bit()); + d->oAuth = new PumpIOOAuth(this); + d->oAuth->setToken(d->token); + d->oAuth->setTokenSecret(d->tokenSecret); d->following = configGroup()->readEntry("Following", QStringList()); d->lists = QVariantList(); d->timelineNames = configGroup()->readEntry("Timelines", QStringList()); if (d->timelineNames.isEmpty()) { d->timelineNames = microblog()->timelineNames(); } - parent->fetchFollowing(this); - parent->fetchLists(this); + if (!d->host.isEmpty()) { + parent->fetchFollowing(this); + parent->fetchLists(this); + } setPostCharLimit(0); } PumpIOAccount::~PumpIOAccount() { d->oAuth->deleteLater(); delete d; } QStringList PumpIOAccount::timelineNames() const { return d->timelineNames; } void PumpIOAccount::writeConfig() { configGroup()->writeEntry("Host", d->host); configGroup()->writeEntry("Token", d->token); configGroup()->writeEntry("ConsumerKey", d->consumerKey); Choqok::PasswordManager::self()->writePassword(QStringLiteral("%1_consumerSecret").arg(alias()), d->consumerSecret); Choqok::PasswordManager::self()->writePassword(QStringLiteral("%1_tokenSecret").arg(alias()), d->tokenSecret); configGroup()->writeEntry("Following", d->following); configGroup()->writeEntry("Timelines", d->timelineNames); //TODO: write accounts lists Choqok::Account::writeConfig(); } QString PumpIOAccount::host() { return d->host; } void PumpIOAccount::setHost(const QString &host) { d->host = host; } QString PumpIOAccount::consumerKey() { return d->consumerKey; } void PumpIOAccount::setConsumerKey(const QString &consumerKey) { d->consumerKey = consumerKey; - d->oAuth->setConsumerKey(consumerKey.toLocal8Bit()); } QString PumpIOAccount::consumerSecret() { return d->consumerSecret; } void PumpIOAccount::setConsumerSecret(const QString &consumerSecret) { d->consumerSecret = consumerSecret; - d->oAuth->setConsumerSecret(consumerSecret.toLocal8Bit()); } QString PumpIOAccount::token() { return d->token; } void PumpIOAccount::setToken(const QString &token) { d->token = token; } QString PumpIOAccount::tokenSecret() { return d->tokenSecret; } void PumpIOAccount::setTokenSecret(const QString &tokenSecret) { d->tokenSecret = tokenSecret; } QStringList PumpIOAccount::following() { return d->following; } void PumpIOAccount::setFollowing(const QStringList following) { d->following = following; d->following.sort(Qt::CaseInsensitive); writeConfig(); } QVariantList PumpIOAccount::lists() { return d->lists; } void PumpIOAccount::setLists(const QVariantList lists) { d->lists = lists; QVariantMap publicCollection; publicCollection.insert(QLatin1String("id"), PumpIOMicroBlog::PublicCollection); publicCollection.insert(QLatin1String("name"), QLatin1String("Public")); d->lists.append(publicCollection); QVariantMap followersCollection; followersCollection.insert(QLatin1String("id"), QString(d->host + QLatin1String("/api/user/") + username() + QLatin1String("/followers"))); followersCollection.insert(QLatin1String("name"), QLatin1String("Followers")); d->lists.append(followersCollection); } void PumpIOAccount::setTimelineNames(const QStringList &list) { d->timelineNames.clear(); for (const QString &name: list) { if (microblog()->timelineNames().contains(name)) { d->timelineNames.append(name); } } } QString PumpIOAccount::webfingerID() { return username() + QLatin1Char('@') + QString(d->host).remove(QLatin1String("https://")); } -QOAuth::Interface *PumpIOAccount::oAuth() +PumpIOOAuth *PumpIOAccount::oAuth() { return d->oAuth; } diff --git a/microblogs/pumpio/pumpioaccount.h b/microblogs/pumpio/pumpioaccount.h index a2a21cc6..2f3936f0 100644 --- a/microblogs/pumpio/pumpioaccount.h +++ b/microblogs/pumpio/pumpioaccount.h @@ -1,76 +1,76 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2013 Andrea Scarpino This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #ifndef PUMPIOACCOUNT_H #define PUMPIOACCOUNT_H -#include - #include "account.h" #include "choqoktypes.h" +#include "pumpiooauth.h" + class PumpIOMicroBlog; class PumpIOAccount : public Choqok::Account { Q_OBJECT public: explicit PumpIOAccount(PumpIOMicroBlog *parent, const QString &alias); ~PumpIOAccount(); virtual QStringList timelineNames() const override; virtual void writeConfig() override; QString host(); void setHost(const QString &host); QString consumerKey(); void setConsumerKey(const QString &consumerKey); QString consumerSecret(); void setConsumerSecret(const QString &consumerSecret); QString token(); void setToken(const QString &token); QString tokenSecret(); void setTokenSecret(const QString &tokenSecret); QStringList following(); void setFollowing(const QStringList following); QVariantList lists(); void setLists(const QVariantList lists); void setTimelineNames(const QStringList &list); QString webfingerID(); - QOAuth::Interface *oAuth(); + PumpIOOAuth *oAuth(); private: class Private; Private *d; }; #endif // PUMPIOACCOUNT_H diff --git a/microblogs/pumpio/pumpioeditaccountwidget.cpp b/microblogs/pumpio/pumpioeditaccountwidget.cpp index be380418..5743df8e 100644 --- a/microblogs/pumpio/pumpioeditaccountwidget.cpp +++ b/microblogs/pumpio/pumpioeditaccountwidget.cpp @@ -1,225 +1,220 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2013 Andrea Scarpino This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #include "pumpioeditaccountwidget.h" #include #include #include #include #include #include #include #include #include "choqoktools.h" #include "accountmanager.h" #include "pumpioaccount.h" #include "pumpiodebug.h" #include "pumpiomicroblog.h" +#include "pumpiooauth.h" PumpIOEditAccountWidget::PumpIOEditAccountWidget(PumpIOMicroBlog *microblog, PumpIOAccount *account, QWidget *parent): ChoqokEditAccountWidget(account, parent) , m_account(account) { setupUi(this); connect(kcfg_authorize, SIGNAL(clicked(bool)), SLOT(authorizeUser())); if (m_account) { kcfg_alias->setText(m_account->alias()); kcfg_webfingerid->setText(m_account->webfingerID()); - isAuthenticated(); + setAuthenticated(!m_account->token().isEmpty() && !m_account->tokenSecret().isEmpty()); } else { + setAuthenticated(false); QString newAccountAlias = microblog->serviceName(); const QString servName = newAccountAlias; int counter = 1; while (Choqok::AccountManager::self()->findAccount(newAccountAlias)) { newAccountAlias = QStringLiteral("%1%2").arg(servName).arg(counter); counter++; } m_account = new PumpIOAccount(microblog, newAccountAlias); setAccount(m_account); kcfg_alias->setText(newAccountAlias); } loadTimelinesTable(); } PumpIOEditAccountWidget::~PumpIOEditAccountWidget() { } Choqok::Account *PumpIOEditAccountWidget::apply() { m_account->setAlias(kcfg_alias->text()); m_account->setUsername(kcfg_webfingerid->text().split(QLatin1Char('@'))[0]); + m_account->setToken(m_account->oAuth()->token()); + m_account->setTokenSecret(m_account->oAuth()->tokenSecret()); m_account->writeConfig(); saveTimelinesTable(); return m_account; } void PumpIOEditAccountWidget::authorizeUser() { qCDebug(CHOQOK); if (kcfg_webfingerid->text().isEmpty() || !kcfg_webfingerid->text().contains(QLatin1Char('@'))) { return; } - m_qoauth = new QOAuth::Interface(new KIO::Integration::AccessManager(this), this); if (m_account->consumerKey().isEmpty() || m_account->consumerSecret().isEmpty()) { registerClient(); } - m_qoauth->setConsumerKey(m_account->consumerKey().toLocal8Bit()); - m_qoauth->setConsumerSecret(m_account->consumerSecret().toLocal8Bit()); - QOAuth::ParamMap oAuthParams; - oAuthParams.insert("oauth_callback", "oob"); - QOAuth::ParamMap oAuthRequest = m_qoauth->requestToken(m_account->host() + QLatin1String("/oauth/request_token"), - QOAuth::GET, QOAuth::HMAC_SHA1, oAuthParams); + m_account->oAuth()->grant(); - if (m_qoauth->error() == QOAuth::NoError) { - const QString token = QLatin1String(oAuthRequest.value(QOAuth::tokenParameterName())); - const QString tokenSecret = QLatin1String( oAuthRequest.value(QOAuth::tokenSecretParameterName())); - - QUrl oAuthAuthorizeURL(m_account->host() + QLatin1String("/oauth/authorize")); - QUrlQuery oAuthAuthorizeQuery; - oAuthAuthorizeQuery.addQueryItem(QLatin1String("oauth_token"), token); - oAuthAuthorizeURL.setQuery(oAuthAuthorizeQuery); + connect(m_account->oAuth(), &QAbstractOAuth::authorizeWithBrowser, &Choqok::openUrl); + connect(m_account->oAuth(), &QAbstractOAuth::statusChanged, this, &PumpIOEditAccountWidget::getPinCode); +} - Choqok::openUrl(oAuthAuthorizeURL); +void PumpIOEditAccountWidget::getPinCode() +{ + isAuthenticated = false; + if (m_account->oAuth()->status() == QAbstractOAuth::Status::TemporaryCredentialsReceived) { QString verifier = QInputDialog::getText(this, i18n("PIN"), i18n("Enter the verifier code received from %1", m_account->host())); - - QOAuth::ParamMap oAuthVerifierParams; - oAuthVerifierParams.insert("oauth_verifier", verifier.toUtf8()); - QOAuth::ParamMap oAuthVerifierRequest = m_qoauth->accessToken( - m_account->host() + QLatin1String("/oauth/access_token"), - QOAuth::POST, token.toLocal8Bit(), - tokenSecret.toLocal8Bit(), - QOAuth::HMAC_SHA1, oAuthVerifierParams); - if (m_qoauth->error() == QOAuth::NoError) { - m_account->setToken(QLatin1String(oAuthVerifierRequest.value(QOAuth::tokenParameterName()))); - m_account->setTokenSecret(QLatin1String(oAuthVerifierRequest.value(QOAuth::tokenSecretParameterName()))); - if (isAuthenticated()) { - KMessageBox::information(this, i18n("Choqok is authorized successfully."), - i18n("Authorized")); - } - } else { - qCDebug(CHOQOK) << QLatin1String("QOAuth error:") + Choqok::qoauthErrorText(m_qoauth->error()); + if (verifier.isEmpty()) { + return; } + + m_account->oAuth()->continueGrantWithVerifier(verifier); + } else if (m_account->oAuth()->status() == QAbstractOAuth::Status::Granted) { + setAuthenticated(true); + KMessageBox::information(this, i18n("Choqok is authorized successfully."), i18n("Authorized")); } else { - qCDebug(CHOQOK) << QLatin1String("QOAuth error:") + Choqok::qoauthErrorText(m_qoauth->error()); + KMessageBox::detailedError(this, i18n("Authorization Error"), i18n("OAuth authorization error")); } } bool PumpIOEditAccountWidget::validateData() { if (kcfg_alias->text().isEmpty() || kcfg_webfingerid->text().isEmpty() || !kcfg_webfingerid->text().contains(QLatin1Char('@')) || - !isAuthenticated()) { + !isAuthenticated) { return false; } else { return true; } } -bool PumpIOEditAccountWidget::isAuthenticated() +void PumpIOEditAccountWidget::setAuthenticated(bool authenticated) { - if (m_account->token().isEmpty() || m_account->tokenSecret().isEmpty()) { - return false; - } else { + isAuthenticated = authenticated; + if (authenticated) { kcfg_authorize->setIcon(QIcon::fromTheme(QLatin1String("object-unlocked"))); - kcfg_authenticateLed->setState(KLed::On); + kcfg_authenticateLed->on(); kcfg_authenticateStatus->setText(i18n("Authenticated")); - return true; + } else { + kcfg_authorize->setIcon(QIcon::fromTheme(QLatin1String("object-locked"))); + kcfg_authenticateLed->off(); + kcfg_authenticateStatus->setText(i18n("Not Authenticated")); } } void PumpIOEditAccountWidget::loadTimelinesTable() { for (const QString &timeline: m_account->microblog()->timelineNames()) { int newRow = timelinesTable->rowCount(); timelinesTable->insertRow(newRow); timelinesTable->setItem(newRow, 0, new QTableWidgetItem(timeline)); QCheckBox *enable = new QCheckBox(timelinesTable); enable->setChecked(m_account->timelineNames().contains(timeline)); timelinesTable->setCellWidget(newRow, 1, enable); } } void PumpIOEditAccountWidget::registerClient() { if (kcfg_webfingerid->text().contains(QLatin1Char('@'))) { m_account->setHost(QLatin1String("https://") + kcfg_webfingerid->text().split(QLatin1Char('@'))[1]); + + m_account->oAuth()->setTemporaryCredentialsUrl(QUrl(m_account->host() + QLatin1String("/oauth/request_token"))); + m_account->oAuth()->setAuthorizationUrl(QUrl(m_account->host() + QLatin1String("/oauth/authorize"))); + m_account->oAuth()->setTokenCredentialsUrl(QUrl(m_account->host() + QLatin1String("/oauth/access_token"))); + QUrl url(m_account->host() + QLatin1String("/api/client/register")); QByteArray data("{" " \"type\": \"client_associate\", " " \"application_type\": \"native\", " " \"application_name\": \"Choqok\" " "}"); KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo); if (!job) { qCDebug(CHOQOK) << "Cannot create an http POST request!"; return; } job->addMetaData(QLatin1String("content-type"), QLatin1String("Content-Type: application/json")); QEventLoop loop; connect(job, SIGNAL(result(KJob*)), &loop, SLOT(quit())); job->start(); loop.exec(); if (job->error()) { qCDebug(CHOQOK) << "An error occurred in Job"; return; } else { KIO::StoredTransferJob *stj = qobject_cast(job); const QJsonDocument json = QJsonDocument::fromJson(stj->data()); if (!json.isNull()) { const QVariantMap result = json.toVariant().toMap(); m_account->setConsumerKey(result[QLatin1String("client_id")].toString()); m_account->setConsumerSecret(result[QLatin1String("client_secret")].toString()); + m_account->oAuth()->setClientIdentifier(m_account->consumerKey()); + m_account->oAuth()->setClientSharedSecret(m_account->consumerSecret()); } else { qCDebug(CHOQOK) << "Cannot parse JSON reply"; } } } else { qCDebug(CHOQOK) << "webfingerID is not valid"; } } void PumpIOEditAccountWidget::saveTimelinesTable() { QStringList timelines; for (int i = 0; i < timelinesTable->rowCount(); ++i) { QCheckBox *enable = qobject_cast(timelinesTable->cellWidget(i, 1)); if (enable && enable->isChecked()) { timelines.append(timelinesTable->item(i, 0)->text()); } } m_account->setTimelineNames(timelines); } diff --git a/microblogs/pumpio/pumpioeditaccountwidget.h b/microblogs/pumpio/pumpioeditaccountwidget.h index 9982fdf1..5526cfff 100644 --- a/microblogs/pumpio/pumpioeditaccountwidget.h +++ b/microblogs/pumpio/pumpioeditaccountwidget.h @@ -1,62 +1,61 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2013 Andrea Scarpino This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #ifndef PUMPIOEDITACCOUNTWIDGET_H #define PUMPIOEDITACCOUNTWIDGET_H #include "editaccountwidget.h" #include -#include - #include "ui_pumpioeditaccountwidget.h" class PumpIOAccount; class PumpIOMicroBlog; class PumpIOEditAccountWidget : public ChoqokEditAccountWidget, Ui::PumpIOEditAccountWidget { Q_OBJECT public: explicit PumpIOEditAccountWidget(PumpIOMicroBlog *microblog, PumpIOAccount *account, QWidget *parent); ~PumpIOEditAccountWidget(); virtual Choqok::Account *apply() override; virtual bool validateData() override; private Q_SLOTS: void authorizeUser(); + void getPinCode(); private: - bool isAuthenticated(); + void setAuthenticated(bool authenticated); void loadTimelinesTable(); void registerClient(); void saveTimelinesTable(); PumpIOAccount *m_account; - QOAuth::Interface *m_qoauth; + bool isAuthenticated; }; #endif // PUMPIOEDITACCOUNTWIDGET_H diff --git a/microblogs/pumpio/pumpiomicroblog.cpp b/microblogs/pumpio/pumpiomicroblog.cpp index b077fdf2..c8bfded2 100644 --- a/microblogs/pumpio/pumpiomicroblog.cpp +++ b/microblogs/pumpio/pumpiomicroblog.cpp @@ -1,1365 +1,1374 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2013-2014 Andrea Scarpino Copyright (C) 2008-2012 Mehrdad Momeny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #include "pumpiomicroblog.h" #include #include #include #include #include #include #include #include #include "accountmanager.h" #include "application.h" #include "choqokbehaviorsettings.h" #include "notifymanager.h" #include "pumpioaccount.h" #include "pumpiocomposerwidget.h" #include "pumpiodebug.h" #include "pumpioeditaccountwidget.h" #include "pumpiomessagedialog.h" #include "pumpiomicroblogwidget.h" #include "pumpiopost.h" #include "pumpiopostwidget.h" class PumpIOMicroBlog::Private { public: Private(): countOfTimelinesToSave(0) {} int countOfTimelinesToSave; }; K_PLUGIN_FACTORY_WITH_JSON(PumpIOMicroBlogFactory, "choqok_pumpio.json", registerPlugin < PumpIOMicroBlog > ();) const QString PumpIOMicroBlog::inboxActivity(QLatin1String("/api/user/%1/inbox")); const QString PumpIOMicroBlog::outboxActivity(QLatin1String("/api/user/%1/feed")); const QString PumpIOMicroBlog::PublicCollection(QLatin1String("http://activityschema.org/collection/public")); PumpIOMicroBlog::PumpIOMicroBlog(QObject *parent, const QVariantList &args): MicroBlog(QStringLiteral("Pump.IO") , parent), d(new Private) { Q_UNUSED(args) setServiceName(QLatin1String("Pump.io")); setServiceHomepageUrl(QLatin1String("http://pump.io")); QStringList timelineNames; timelineNames << QLatin1String("Activity") << QLatin1String("Favorites") << QLatin1String("Inbox") << QLatin1String("Outbox"); setTimelineNames(timelineNames); setTimelinesInfo(); } PumpIOMicroBlog::~PumpIOMicroBlog() { qDeleteAll(m_timelinesInfos); delete d; } void PumpIOMicroBlog::abortAllJobs(Choqok::Account *theAccount) { for (KJob *job: m_accountJobs.keys(theAccount)) { job->kill(KJob::EmitResult); } } void PumpIOMicroBlog::abortCreatePost(Choqok::Account *theAccount, Choqok::Post *post) { if (m_createPostJobs.isEmpty()) { return; } if (post) { m_createPostJobs.key(post)->kill(KJob::EmitResult); return; } for (KJob *job: m_createPostJobs.keys()) { if (m_accountJobs[job] == theAccount) { job->kill(KJob::EmitResult); } } } void PumpIOMicroBlog::aboutToUnload() { for (Choqok::Account *acc: Choqok::AccountManager::self()->accounts()) { if (acc->microblog() == this) { d->countOfTimelinesToSave += acc->timelineNames().count(); } } Q_EMIT saveTimelines(); } QMenu *PumpIOMicroBlog::createActionsMenu(Choqok::Account *theAccount, QWidget *parent) { QMenu *menu = MicroBlog::createActionsMenu(theAccount, parent); QAction *directMessge = new QAction(QIcon::fromTheme(QLatin1String("mail-message-new")), i18n("Send Private Message..."), menu); directMessge->setData(theAccount->alias()); connect(directMessge, SIGNAL(triggered(bool)), this, SLOT(showDirectMessageDialog())); menu->addAction(directMessge); return menu; } Choqok::UI::MicroBlogWidget *PumpIOMicroBlog::createMicroBlogWidget(Choqok::Account *account, QWidget *parent) { return new PumpIOMicroBlogWidget(account, parent); } Choqok::UI::ComposerWidget *PumpIOMicroBlog::createComposerWidget(Choqok::Account *account, QWidget *parent) { return new PumpIOComposerWidget(account, parent); } ChoqokEditAccountWidget *PumpIOMicroBlog::createEditAccountWidget(Choqok::Account *account, QWidget *parent) { PumpIOAccount *acc = qobject_cast(account); if (acc || !account) { return new PumpIOEditAccountWidget(this, acc, parent); } else { qCDebug(CHOQOK) << "Account passed here was not a valid PumpIOAccount!"; return 0; } } Choqok::Account *PumpIOMicroBlog::createNewAccount(const QString &alias) { PumpIOAccount *acc = qobject_cast( Choqok::AccountManager::self()->findAccount(alias)); if (!acc) { return new PumpIOAccount(this, alias); } else { qCDebug(CHOQOK) << "Cannot create a new PumpIOAccount!"; return 0; } } void PumpIOMicroBlog::createPost(Choqok::Account *theAccount, Choqok::Post *post) { QVariantList to; QVariantMap thePublic; thePublic.insert(QLatin1String("objectType"), QLatin1String("collection")); thePublic.insert(QLatin1String("id"), PumpIOMicroBlog::PublicCollection); to.append(thePublic); createPost(theAccount, post, to); } void PumpIOMicroBlog::createPost(Choqok::Account *theAccount, Choqok::Post *post, const QVariantList &to, const QVariantList &cc) { if (!post || post->content.isEmpty()) { qCDebug(CHOQOK) << "ERROR: Status text is empty!"; Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::OtherError, i18n("Creating the new post failed. Text is empty."), MicroBlog::Critical); return; } PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { QVariantMap object; if (!post->postId.isEmpty()) { object.insert(QLatin1String("id"), post->postId); } if (post->type.isEmpty()) { post->type = QLatin1String("note"); } object.insert(QLatin1String("objectType"), post->type); //Convert URLs to href form post->content.replace(QRegExp(QLatin1String("((?:https?|ftp)://\\S+)")), QLatin1String("\\1")); object.insert(QLatin1String("content"), QUrl::toPercentEncoding(post->content)); QVariantMap item; item.insert(QLatin1String("verb"), QLatin1String("post")); item.insert(QLatin1String("object"), object); item.insert(QLatin1String("to"), to); item.insert(QLatin1String("cc"), cc); const QByteArray data = QJsonDocument::fromVariant(item).toJson(); QUrl url(acc->host()); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QLatin1Char('/') + (outboxActivity.arg(acc->username()))); KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo); job->addMetaData(QLatin1String("content-type"), QLatin1String("Content-Type: application/json")); - job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QOAuth::POST)); + job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QNetworkAccessManager::PostOperation)); if (!job) { qCDebug(CHOQOK) << "Cannot create an http POST request!"; return; } m_accountJobs[job] = acc; m_createPostJobs[job] = post; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotCreatePost(KJob*))); job->start(); } else { qCDebug(CHOQOK) << "theAccount is not a PumpIOAccount!"; } } void PumpIOMicroBlog::createReply(Choqok::Account *theAccount, PumpIOPost *post) { PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { post->type = QLatin1String("comment"); QVariantMap object; object.insert(QLatin1String("objectType"), post->type); //Convert URLs to href form post->content.replace(QRegExp(QLatin1String("((?:https?|ftp)://\\S+)")), QLatin1String("\\1")); object.insert(QLatin1String("content"), QUrl::toPercentEncoding(post->content)); if (!post->replyToPostId.isEmpty()) { QVariantMap inReplyTo; inReplyTo.insert(QLatin1String("id"), post->replyToPostId); inReplyTo.insert(QLatin1String("objectType"), post->replyToObjectType); object.insert(QLatin1String("inReplyTo"), inReplyTo); } QVariantMap item; item.insert(QLatin1String("verb"), QLatin1String("post")); item.insert(QLatin1String("object"), object); const QByteArray data = QJsonDocument::fromVariant(item).toJson(); QUrl url(acc->host()); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QLatin1Char('/') + (outboxActivity.arg(acc->username()))); KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo); job->addMetaData(QLatin1String("content-type"), QLatin1String("Content-Type: application/json")); - job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QOAuth::POST)); + job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QNetworkAccessManager::PostOperation)); if (!job) { qCDebug(CHOQOK) << "Cannot create an http POST request!"; return; } m_accountJobs[job] = acc; m_createPostJobs[job] = post; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotCreatePost(KJob*))); job->start(); } else { qCDebug(CHOQOK) << "theAccount is not a PumpIOAccount!"; } } void PumpIOMicroBlog::createPostWithMedia(Choqok::Account *theAccount, Choqok::Post *post, const QString &filePath) { PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { QFile media(filePath); QByteArray data; if (media.open(QIODevice::ReadOnly)) { data = media.readAll(); media.close(); } else { qCDebug(CHOQOK) << "Cannot read the file"; return; } const QMimeDatabase db; const QMimeType mimetype = db.mimeTypeForFileNameAndData(filePath, data); const QString mime = mimetype.name(); if (mime == QLatin1String("application/octet-stream")) { qCDebug(CHOQOK) << "Cannot retrieve file mimetype"; return; } QUrl url(acc->host()); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QStringLiteral("/api/user/%1/uploads").arg(acc->username())); KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo); job->addMetaData(QLatin1String("content-type"), QLatin1String("Content-Type: ") + mime); - job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QOAuth::POST)); + job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QNetworkAccessManager::PostOperation)); if (!job) { qCDebug(CHOQOK) << "Cannot create an http POST request!"; return; } m_accountJobs[job] = acc; m_uploadJobs[job] = post; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotUpload(KJob*))); job->start(); } else { qCDebug(CHOQOK) << "theAccount is not a PumpIOAccount!"; } } Choqok::UI::PostWidget *PumpIOMicroBlog::createPostWidget(Choqok::Account *account, Choqok::Post *post, QWidget *parent) { return new PumpIOPostWidget(account, post, parent); } void PumpIOMicroBlog::fetchPost(Choqok::Account *theAccount, Choqok::Post *post) { PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { if (!post->link.startsWith(acc->host())) { qCDebug(CHOQOK) << "You can only fetch posts from your host!"; return; } QUrl url(post->link); KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo); if (!job) { qCDebug(CHOQOK) << "Cannot create an http GET request!"; return; } - job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QOAuth::GET)); + job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QNetworkAccessManager::GetOperation)); m_accountJobs[job] = acc; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotFetchPost(KJob*))); job->start(); } else { qCDebug(CHOQOK) << "theAccount is not a PumpIOAccount!"; } } void PumpIOMicroBlog::removePost(Choqok::Account *theAccount, Choqok::Post *post) { PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { QVariantMap object; object.insert(QLatin1String("id"), post->postId); object.insert(QLatin1String("objectType"), post->type); QVariantMap item; item.insert(QLatin1String("verb"), QLatin1String("delete")); item.insert(QLatin1String("object"), object); const QByteArray data = QJsonDocument::fromVariant(item).toJson(); QUrl url(acc->host()); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QLatin1Char('/') + (outboxActivity.arg(acc->username()))); KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo); job->addMetaData(QLatin1String("content-type"), QLatin1String("Content-Type: application/json")); - job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QOAuth::POST)); + job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QNetworkAccessManager::PostOperation)); if (!job) { qCDebug(CHOQOK) << "Cannot create an http POST request!"; return; } m_accountJobs[job] = acc; m_removePostJobs[job] = post; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotRemovePost(KJob*))); job->start(); } else { qCDebug(CHOQOK) << "theAccount is not a PumpIOAccount!"; } } QList< Choqok::Post * > PumpIOMicroBlog::loadTimeline(Choqok::Account *account, const QString &timelineName) { QList< Choqok::Post * > list; const QString fileName = Choqok::AccountManager::generatePostBackupFileName(account->alias(), timelineName); const KConfig postsBackup(fileName, KConfig::NoGlobals, QStandardPaths::DataLocation); const QStringList tmpList = postsBackup.groupList(); // don't load old archives if (tmpList.isEmpty() || !(QDateTime::fromString(tmpList.first()).isValid())) { return list; } QList groupList; for (const QString &str: tmpList) { groupList.append(QDateTime::fromString(str)); } qSort(groupList); PumpIOPost *st; for (const QDateTime &datetime: groupList) { st = new PumpIOPost; KConfigGroup grp(&postsBackup, datetime.toString()); st->creationDateTime = grp.readEntry("creationDateTime", QDateTime::currentDateTime()); st->postId = grp.readEntry("postId", QString()); st->link = grp.readEntry("link", QString()); st->content = grp.readEntry("content", QString()); st->source = grp.readEntry("source", QString()); st->isFavorited = grp.readEntry("favorited", false); st->author.userId = grp.readEntry("authorId", QString()); st->author.userName = grp.readEntry("authorUserName", QString()); st->author.realName = grp.readEntry("authorRealName", QString()); st->author.location = grp.readEntry("authorLocation", QString()); st->author.description = grp.readEntry("authorDescription" , QString()); st->author.profileImageUrl = grp.readEntry("authorProfileImageUrl", QString()); st->author.homePageUrl = grp.readEntry("authorHomePageUrl", QString()); st->type = grp.readEntry("type", QString()); st->media = grp.readEntry("media"), QString(); st->isRead = grp.readEntry("isRead", true); st->conversationId = grp.readEntry("conversationId", QString()); st->to = grp.readEntry("to", QStringList()); st->cc = grp.readEntry("cc", QStringList()); st->shares = grp.readEntry("shares", QStringList()); st->replies = grp.readEntry("replies", QString()); st->replyToPostId = grp.readEntry("replyToPostId", QString()); st->replyToUserName = grp.readEntry("replyToUserName", QString()); st->replyToObjectType = grp.readEntry("replyToObjectType", QString()); list.append(st); } if (!list.isEmpty()) { setLastTimelineId(account, timelineName, list.last()->conversationId); } return list; } QString PumpIOMicroBlog::postUrl(Choqok::Account *account, const QString &username, const QString &postId) const { Q_UNUSED(account); return QString(postId).replace(QLatin1String("/api/"), QLatin1Char('/') + username + QLatin1Char('/')); } QString PumpIOMicroBlog::profileUrl(Choqok::Account *account, const QString &username) const { Q_UNUSED(account) if (username.contains(QLatin1String("acct:"))) { return QStringLiteral("https://%1/%2").arg(hostFromAcct(username)).arg(userNameFromAcct(username)); } else { return username; } } void PumpIOMicroBlog::saveTimeline(Choqok::Account *account, const QString &timelineName, const QList< Choqok::UI::PostWidget * > &timeline) { const QString fileName = Choqok::AccountManager::generatePostBackupFileName(account->alias(), timelineName); KConfig postsBackup(fileName, KConfig::NoGlobals, QStandardPaths::DataLocation); ///Clear previous data: for (const QString &group: postsBackup.groupList()) { postsBackup.deleteGroup(group); } for (Choqok::UI::PostWidget *wd: timeline) { PumpIOPost *post = dynamic_cast(wd->currentPost()); KConfigGroup grp(&postsBackup, post->creationDateTime.toString()); grp.writeEntry("creationDateTime", post->creationDateTime); grp.writeEntry("postId", post->postId); grp.writeEntry("link", post->link); grp.writeEntry("content", post->content); grp.writeEntry("source", post->source); grp.writeEntry("favorited", post->isFavorited); grp.writeEntry("authorId", post->author.userId); grp.writeEntry("authorRealName", post->author.realName); grp.writeEntry("authorUserName", post->author.userName); grp.writeEntry("authorLocation", post->author.location); grp.writeEntry("authorDescription", post->author.description); grp.writeEntry("authorProfileImageUrl", post->author.profileImageUrl); grp.writeEntry("authorHomePageUrl", post->author.homePageUrl); grp.writeEntry("type", post->type); grp.writeEntry("media", post->media); grp.writeEntry("isRead", post->isRead); grp.writeEntry("conversationId", post->conversationId); grp.writeEntry("to", post->to); grp.writeEntry("cc", post->cc); grp.writeEntry("shares", post->shares); grp.writeEntry("replies", post->replies); grp.writeEntry("replyToPostId", post->replyToPostId); grp.writeEntry("replyToUserName", post->replyToUserName); grp.writeEntry("replyToObjectType", post->replyToObjectType); } postsBackup.sync(); if (Choqok::Application::isShuttingDown()) { --d->countOfTimelinesToSave; if (d->countOfTimelinesToSave < 1) { Q_EMIT readyForUnload(); } } } Choqok::TimelineInfo *PumpIOMicroBlog::timelineInfo(const QString &timelineName) { return m_timelinesInfos.value(timelineName); } void PumpIOMicroBlog::updateTimelines(Choqok::Account *theAccount) { PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { for (const QString &timeline: acc->timelineNames()) { QUrl url(acc->host()); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QLatin1Char('/') + (m_timelinesPaths[timeline].arg(acc->username()))); + QUrlQuery query; - QOAuth::ParamMap oAuthParams; + QVariantMap oAuthParams; const QString lastActivityId(lastTimelineId(theAccount, timeline)); if (!lastActivityId.isEmpty()) { - oAuthParams.insert("count", QByteArray::number(200)); - oAuthParams.insert("since", QUrl::toPercentEncoding(lastActivityId)); + oAuthParams.insert(QLatin1String("count"), QByteArray::number(200)); + query.addQueryItem(QLatin1String("count"), QString::number(200)); + oAuthParams.insert(QLatin1String("since"), QUrl::toPercentEncoding(lastActivityId)); + query.addQueryItem(QLatin1String("since"), lastActivityId); } else { - oAuthParams.insert("count", QByteArray::number(Choqok::BehaviorSettings::countOfPosts())); + oAuthParams.insert(QLatin1String("count"), QByteArray::number(Choqok::BehaviorSettings::countOfPosts())); + query.addQueryItem(QLatin1String("count"), QString::number(Choqok::BehaviorSettings::countOfPosts())); } + url.setQuery(query); KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo); if (!job) { qCDebug(CHOQOK) << "Cannot create an http GET request!"; continue; } - job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QOAuth::GET, + job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QNetworkAccessManager::GetOperation, oAuthParams)); m_timelinesRequests[job] = timeline; m_accountJobs[job] = acc; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotUpdateTimeline(KJob*))); job->start(); } } else { qCDebug(CHOQOK) << "theAccount is not a PumpIOAccount!"; } } void PumpIOMicroBlog::fetchFollowing(Choqok::Account *theAccount) { PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { QUrl url(acc->host()); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QStringLiteral("/api/user/%1/following").arg(acc->username())); + QUrlQuery query; - QOAuth::ParamMap oAuthParams; - oAuthParams.insert("count", QByteArray::number(200)); + QVariantMap oAuthParams; + oAuthParams.insert(QLatin1String("count"), QByteArray::number(200)); + query.addQueryItem(QLatin1String("count"), QString::number(200)); if (!acc->following().isEmpty()) { - oAuthParams.insert("since", QUrl::toPercentEncoding(acc->following().last())); + oAuthParams.insert(QLatin1String("since"), QUrl::toPercentEncoding(acc->following().last())); + query.addQueryItem(QLatin1String("since"), acc->following().last()); } + url.setQuery(query); KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo); if (!job) { qCDebug(CHOQOK) << "Cannot create an http GET request!"; return; } - job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QOAuth::GET, + job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QNetworkAccessManager::GetOperation, oAuthParams)); m_accountJobs[job] = acc; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotFollowing(KJob*))); job->start(); } else { qCDebug(CHOQOK) << "theAccount is not a PumpIOAccount!"; } } void PumpIOMicroBlog::fetchLists(Choqok::Account *theAccount) { PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { QUrl url(acc->host()); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QStringLiteral("/api/user/%1/lists/person").arg(acc->username())); + QUrlQuery query; - QOAuth::ParamMap oAuthParams; - oAuthParams.insert("count", QByteArray::number(200)); + QVariantMap oAuthParams; + oAuthParams.insert(QLatin1String("count"), QByteArray::number(200)); + query.addQueryItem(QLatin1String("count"), QString::number(200)); + + url.setQuery(query); KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo); if (!job) { qCDebug(CHOQOK) << "Cannot create an http GET request!"; return; } - job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QOAuth::GET, + job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QNetworkAccessManager::GetOperation, oAuthParams)); m_accountJobs[job] = acc; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotLists(KJob*))); job->start(); } else { qCDebug(CHOQOK) << "theAccount is not a PumpIOAccount!"; } } void PumpIOMicroBlog::share(Choqok::Account *theAccount, Choqok::Post *post) { PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { QVariantMap object; object.insert(QLatin1String("objectType"), post->type); object.insert(QLatin1String("id"), post->postId); QVariantMap item; item.insert(QLatin1String("verb"), QLatin1String("share")); item.insert(QLatin1String("object"), object); const QByteArray data = QJsonDocument::fromVariant(item).toJson(); QUrl url(acc->host()); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QLatin1Char('/') + (outboxActivity.arg(acc->username()))); KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo); job->addMetaData(QLatin1String("content-type"), QLatin1String("Content-Type: application/json")); - job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QOAuth::POST)); + job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QNetworkAccessManager::PostOperation)); if (!job) { qCDebug(CHOQOK) << "Cannot create an http POST request!"; return; } m_accountJobs[job] = acc; m_shareJobs[job] = post; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotShare(KJob*))); job->start(); } else { qCDebug(CHOQOK) << "theAccount is not a PumpIOAccount!"; } } void PumpIOMicroBlog::toggleFavorite(Choqok::Account *theAccount, Choqok::Post *post) { PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { QVariantMap object; object.insert(QLatin1String("objectType"), post->type); object.insert(QLatin1String("id"), post->postId); QVariantMap item; item.insert(QLatin1String("verb"), post->isFavorited ? QLatin1String("unfavorite") : QLatin1String("favorite")); item.insert(QLatin1String("object"), object); const QByteArray data = QJsonDocument::fromVariant(item).toJson(); QUrl url(acc->host()); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QLatin1Char('/') + (outboxActivity.arg(acc->username()))); KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo); job->addMetaData(QLatin1String("content-type"), QLatin1String("Content-Type: application/json")); - job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QOAuth::POST)); + job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QNetworkAccessManager::PostOperation)); if (!job) { qCDebug(CHOQOK) << "Cannot create an http POST request!"; return; } m_accountJobs[job] = acc; m_favoriteJobs[job] = post; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotFavorite(KJob*))); job->start(); } else { qCDebug(CHOQOK) << "theAccount is not a PumpIOAccount!"; } } void PumpIOMicroBlog::showDirectMessageDialog() { qCDebug(CHOQOK); const QString alias = qobject_cast(sender())->data().toString(); PumpIOAccount *theAccount = qobject_cast(Choqok::AccountManager::self()->findAccount(alias)); PumpIOMessageDialog *msg = new PumpIOMessageDialog(theAccount, Choqok::UI::Global::mainWindow()); msg->show(); } void PumpIOMicroBlog::slotCreatePost(KJob *job) { qCDebug(CHOQOK); if (!job) { qCDebug(CHOQOK) << "Job is null pointer"; return; } Choqok::Post *post = m_createPostJobs.take(job); Choqok::Account *theAccount = m_accountJobs.take(job); if (!post || !theAccount) { qCDebug(CHOQOK) << "Account or Post is NULL pointer"; return; } int ret = 1; if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); } else { KIO::StoredTransferJob *j = qobject_cast(job); const QJsonDocument json = QJsonDocument::fromJson(j->data()); if (!json.isNull()) { const QVariantMap reply = json.toVariant().toMap(); if (!reply[QLatin1String("object")].toMap().value(QLatin1String("id")).toString().isEmpty()) { Choqok::NotifyManager::success(i18n("New post submitted successfully")); ret = 0; Q_EMIT postCreated(theAccount, post); } } else { qCDebug(CHOQOK) << "Cannot parse JSON reply"; } } if (ret) { Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::CommunicationError, i18n("Creating the new post failed. %1", job->errorString()), MicroBlog::Critical); } } void PumpIOMicroBlog::slotFavorite(KJob *job) { qCDebug(CHOQOK); if (!job) { qCDebug(CHOQOK) << "Job is null pointer"; return; } Choqok::Post *post = m_favoriteJobs.take(job); Choqok::Account *theAccount = m_accountJobs.take(job); if (!post || !theAccount) { qCDebug(CHOQOK) << "Account or Post is NULL pointer"; return; } if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT error(theAccount, Choqok::MicroBlog::CommunicationError, i18n("Cannot set/unset the post as favorite. %1", job->errorString())); } else { post->isFavorited = !post->isFavorited; Q_EMIT favorite(theAccount, post); } } void PumpIOMicroBlog::slotFetchPost(KJob *job) { qCDebug(CHOQOK); if (!job) { qCDebug(CHOQOK) << "Job is null pointer"; return; } Choqok::Account *theAccount = m_accountJobs.take(job); if (!theAccount) { qCDebug(CHOQOK) << "Account or postId is NULL pointer"; return; } int ret = 1; if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); } else { KIO::StoredTransferJob *j = qobject_cast(job); const QJsonDocument json = QJsonDocument::fromJson(j->data()); if (!json.isNull()) { const QVariantMap reply = json.toVariant().toMap(); PumpIOPost *post = new PumpIOPost; readPost(reply, post); ret = 0; Q_EMIT postFetched(theAccount, post); } else { qCDebug(CHOQOK) << "Cannot parse JSON reply"; } } if (ret) { Q_EMIT error(theAccount, Choqok::MicroBlog::CommunicationError, i18n("Cannot fetch post. %1", job->errorString()), MicroBlog::Critical); } } void PumpIOMicroBlog::slotFetchReplies(KJob *job) { qCDebug(CHOQOK); if (!job) { qCDebug(CHOQOK) << "Job is null pointer"; return; } Choqok::Account *theAccount = m_accountJobs.take(job); if (!theAccount) { qCDebug(CHOQOK) << "Account or postId is NULL pointer"; return; } int ret = 1; if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); } else { KIO::StoredTransferJob *j = qobject_cast(job); const QJsonDocument json = QJsonDocument::fromJson(j->data()); if (!json.isNull()) { const QVariantMap reply = json.toVariant().toMap(); const QVariantList items = reply[QLatin1String("items")].toList(); for (int i = items.size() - 1; i >= 0; i--) { QVariantMap item = items.at(i).toMap(); PumpIOPost *r = new PumpIOPost; readPost(item, r); r->replyToPostId = reply[QLatin1String("url")].toString().remove(QLatin1String("/replies")); Q_EMIT postFetched(theAccount, r); } ret = 0; } else { qCDebug(CHOQOK) << "Cannot parse JSON reply"; } } if (ret) { Q_EMIT error(theAccount, Choqok::MicroBlog::CommunicationError, i18n("Cannot fetch replies. %1", job->errorString()), MicroBlog::Critical); } } void PumpIOMicroBlog::slotFollowing(KJob *job) { qCDebug(CHOQOK); if (!job) { qCDebug(CHOQOK) << "Job is null pointer"; return; } Choqok::Account *theAccount = m_accountJobs.take(job); if (!theAccount) { qCDebug(CHOQOK) << "Account is NULL pointer"; return; } if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); } bool ret = 1; PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { Choqok::UI::Global::mainWindow()->showStatusMessage( i18n("Following list for account %1 has been updated.", acc->username())); KIO::StoredTransferJob *j = qobject_cast(job); const QJsonDocument json = QJsonDocument::fromJson(j->data()); if (!json.isNull()) { const QVariantList items = json.toVariant().toMap().value(QLatin1String("items")).toList(); QStringList following; for (const QVariant &element: items) { following.append(element.toMap().value(QLatin1String("id")).toString()); } acc->setFollowing(following); ret = 0; Q_EMIT followingFetched(acc); } else { qCDebug(CHOQOK) << "Cannot parse JSON reply"; } } else { qCDebug(CHOQOK) << "theAccount is not a PumpIOAccount!"; } if (ret) { Q_EMIT error(theAccount, Choqok::MicroBlog::CommunicationError, i18n("Cannot retrieve the following list. %1", job->errorString())); } } void PumpIOMicroBlog::slotLists(KJob *job) { qCDebug(CHOQOK); if (!job) { qCDebug(CHOQOK) << "Job is null pointer"; return; } Choqok::Account *theAccount = m_accountJobs.take(job); if (!theAccount) { qCDebug(CHOQOK) << "Account is NULL pointer"; return; } if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); } bool ret = 1; PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { Choqok::UI::Global::mainWindow()->showStatusMessage( i18n("Lists for account %1 has been updated.", acc->username())); KIO::StoredTransferJob *j = qobject_cast(job); const QJsonDocument json = QJsonDocument::fromJson(j->data()); if (!json.isNull()) { const QVariantList items = json.toVariant().toMap().value(QLatin1String("items")).toList(); QVariantList lists; for (const QVariant &element: items) { QVariantMap e = element.toMap(); QVariantMap list; list.insert(QLatin1String("id"), e.value(QLatin1String("id")).toString()); list.insert(QLatin1String("name"), e.value(QLatin1String("displayName")).toString()); lists.append(list); } acc->setLists(lists); ret = 0; Q_EMIT listsFetched(acc); } else { qCDebug(CHOQOK) << "Cannot parse JSON reply"; } } else { qCDebug(CHOQOK) << "theAccount is not a PumpIOAccount!"; } if (ret) { Q_EMIT error(theAccount, Choqok::MicroBlog::CommunicationError, i18n("Cannot retrieve the lists. %1", job->errorString())); } } void PumpIOMicroBlog::slotShare(KJob *job) { qCDebug(CHOQOK); if (!job) { qCDebug(CHOQOK) << "Job is null pointer"; return; } Choqok::Post *post = m_shareJobs.take(job); Choqok::Account *theAccount = m_accountJobs.take(job); if (!post || !theAccount) { qCDebug(CHOQOK) << "Account or Post is NULL pointer"; return; } int ret = 1; if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); } else { Choqok::UI::Global::mainWindow()->showStatusMessage( i18n("The post has been shared.")); KIO::StoredTransferJob *j = qobject_cast(job); const QJsonDocument json = QJsonDocument::fromJson(j->data()); if (!json.isNull()) { const QVariantMap object = json.toVariant().toMap().value(QLatin1String("object")).toMap(); ret = 0; } else { qCDebug(CHOQOK) << "Cannot parse JSON reply"; } } if (ret) { Q_EMIT error(theAccount, Choqok::MicroBlog::CommunicationError, i18n("Cannot share the post. %1", job->errorString())); } } void PumpIOMicroBlog::slotRemovePost(KJob *job) { qCDebug(CHOQOK); if (!job) { qCDebug(CHOQOK) << "Job is null pointer"; return; } Choqok::Post *post = m_removePostJobs.take(job); Choqok::Account *theAccount = m_accountJobs.take(job); if (!post || !theAccount) { qCDebug(CHOQOK) << "Account or Post is NULL pointer"; return; } int ret = 1; if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); } else { KIO::StoredTransferJob *j = qobject_cast(job); const QJsonDocument json = QJsonDocument::fromJson(j->data()); if (!json.isNull()) { const QVariantMap object = json.toVariant().toMap().value(QLatin1String("object")).toMap(); if (!object[QLatin1String("deleted")].toString().isEmpty()) { Choqok::NotifyManager::success(i18n("Post removed successfully")); ret = 0; Q_EMIT postRemoved(theAccount, post); } } else { qCDebug(CHOQOK) << "Cannot parse JSON reply"; } } if (ret) { Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::CommunicationError, i18n("Removing the post failed. %1", job->errorString()), MicroBlog::Critical); } } void PumpIOMicroBlog::slotUpdatePost(KJob *job) { qCDebug(CHOQOK); if (!job) { qCDebug(CHOQOK) << "Job is null pointer"; return; } Choqok::Post *post = m_updateJobs.take(job); Choqok::Account *account = m_accountJobs.take(job); if (!post || !account) { qCDebug(CHOQOK) << "Account or Post is NULL pointer"; return; } int ret = 1; if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); } else { KIO::StoredTransferJob *j = qobject_cast(job); const QJsonDocument json = QJsonDocument::fromJson(j->data()); if (!json.isNull()) { ret = 0; createPost(account, post); } else { qCDebug(CHOQOK) << "Cannot parse JSON reply"; } } if (ret) { Q_EMIT error(account, Choqok::MicroBlog::CommunicationError, i18n("An error occurred when updating the post")); } } void PumpIOMicroBlog::slotUpdateTimeline(KJob *job) { qCDebug(CHOQOK); if (!job) { qCDebug(CHOQOK) << "Job is null pointer"; return; } Choqok::Account *account = m_accountJobs.take(job); if (!account) { qCDebug(CHOQOK) << "Account or Post is NULL pointer"; return; } if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT error(account, Choqok::MicroBlog::CommunicationError, i18n("An error occurred when fetching the timeline")); } else { KIO::StoredTransferJob *j = qobject_cast(job); const QList list = readTimeline(j->data()); const QString timeline(m_timelinesRequests.take(job)); if (!list.isEmpty()) { setLastTimelineId(account, timeline, list.last()->conversationId); } Q_EMIT timelineDataReceived(account, timeline, list); } } void PumpIOMicroBlog::slotUpload(KJob *job) { qCDebug(CHOQOK); if (!job) { qCDebug(CHOQOK) << "Job is null pointer"; return; } Choqok::Post *post = m_uploadJobs.take(job); Choqok::Account *account = m_accountJobs.take(job); if (!post || !account) { qCDebug(CHOQOK) << "Account or Post is NULL pointer"; return; } int ret = 1; if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); } else { KIO::StoredTransferJob *j = qobject_cast(job); const QJsonDocument json = QJsonDocument::fromJson(j->data()); if (!json.isNull()) { const QVariantMap reply = json.toVariant().toMap(); const QString id = reply[QLatin1String("id")].toString(); if (!id.isEmpty()) { post->postId = id; post->type = reply[QLatin1String("objectType")].toString(); ret = 0; updatePost(account, post); } } else { qCDebug(CHOQOK) << "Cannot parse JSON reply"; } } if (ret) { Q_EMIT error(account, Choqok::MicroBlog::CommunicationError, i18n("An error occurred when uploading the media")); } } QString PumpIOMicroBlog::authorizationMetaData(PumpIOAccount *account, const QUrl &url, - const QOAuth::HttpMethod &method, - const QOAuth::ParamMap ¶mMap) const + QNetworkAccessManager::Operation method, + const QVariantMap &map) const { - const QByteArray authorization = account->oAuth()->createParametersString(url.url(), - method, account->token().toLocal8Bit(), - account->tokenSecret().toLocal8Bit(), - QOAuth::HMAC_SHA1, paramMap, - QOAuth::ParseForHeaderArguments); + const QByteArray authorization = account->oAuth()->authorizationHeader(url, method, map); return QStringLiteral("Authorization: ") + QLatin1String(authorization); } void PumpIOMicroBlog::fetchReplies(Choqok::Account *theAccount, const QString &url) { PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { if (!url.startsWith(acc->host())) { qCDebug(CHOQOK) << "You can only fetch replies from your host!"; return; } QUrl u(url); KIO::StoredTransferJob *job = KIO::storedGet(u, KIO::Reload, KIO::HideProgressInfo); if (!job) { qCDebug(CHOQOK) << "Cannot create an http GET request!"; return; } - job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, u, QOAuth::GET)); + job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, u, QNetworkAccessManager::GetOperation)); m_accountJobs[job] = acc; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotFetchReplies(KJob*))); job->start(); } else { qCDebug(CHOQOK) << "theAccount is not a PumpIOAccount!"; } } QString PumpIOMicroBlog::lastTimelineId(Choqok::Account *theAccount, const QString &timeline) const { qCDebug(CHOQOK) << "Latest ID for timeline " << timeline << m_timelinesLatestIds[theAccount][timeline]; return m_timelinesLatestIds[theAccount][timeline]; } Choqok::Post *PumpIOMicroBlog::readPost(const QVariantMap &var, Choqok::Post *post) { PumpIOPost *p = dynamic_cast< PumpIOPost * >(post); if (p) { QVariantMap object; if (var.value(QLatin1String("verb")).toString() == QLatin1String("post") || var.value(QLatin1String("verb")).toString() == QLatin1String("share")) { object = var[QLatin1String("object")].toMap(); } else { object = var; } QTextDocument content; if (!object[QLatin1String("displayName")].isNull()) { content.setHtml(object[QLatin1String("displayName")].toString()); p->content = content.toPlainText().trimmed(); p->content += QLatin1Char('\n'); } content.setHtml(object[QLatin1String("content")].toString()); p->content += content.toPlainText().trimmed(); if (!object[QLatin1String("fullImage")].isNull()) { const QVariantMap fullImage = object[QLatin1String("fullImage")].toMap(); if (!fullImage.isEmpty()) { p->media = fullImage[QLatin1String("url")].toString(); } } p->creationDateTime = QDateTime::fromString(var[QLatin1String("published")].toString(), Qt::ISODate); p->creationDateTime.setTimeSpec(Qt::UTC); if (object[QLatin1String("pump_io")].isNull()) { p->link = object[QLatin1String("id")].toString(); } else { p->link = object[QLatin1String("pump_io")].toMap().value(QLatin1String("proxyURL")).toString(); } p->type = object[QLatin1String("objectType")].toString(); p->isFavorited = object[QLatin1String("liked")].toBool(); if (p->isFavorited) { p->isRead = true; } p->postId = object[QLatin1String("id")].toString(); p->conversationId = var[QLatin1String("id")].toString(); QString author; var[QLatin1String("author")].isNull() ? author = QLatin1String("actor") : author = QLatin1String("author"); QVariantMap actor; if (var.value(QLatin1String("verb")).toString() == QLatin1String("share")) { actor = object[QLatin1String("author")].toMap(); const QVariantList shares = object[QLatin1String("shares")].toMap().value(QLatin1String("items")).toList(); for (const QVariant &element: shares) { p->shares.append(element.toMap().value(QLatin1String("id")).toString()); } } else { actor = var[author].toMap(); } const QString userId = actor[QLatin1String("id")].toString(); const QString homePageUrl = actor[QLatin1String("url")].toString(); p->author.userId = userId; p->author.userName = actor[QLatin1String("preferredUsername")].toString(); p->author.realName = actor[QLatin1String("displayName")].toString(); p->author.homePageUrl = homePageUrl; p->author.location = actor[QLatin1String("location")].toMap().value(QLatin1String("displayName")).toString(); p->author.description = actor[QLatin1String("summary")].toString(); const QString profileImageUrl = actor[QLatin1String("image")].toMap().value(QLatin1String("url")).toString(); if (!profileImageUrl.isEmpty()) { p->author.profileImageUrl = profileImageUrl; } else if (actor[QLatin1String("objectType")].toString() == QLatin1String("service")) { p->author.profileImageUrl = homePageUrl + QLatin1String("images/default.png"); } else { p->author.profileImageUrl = QStringLiteral("https://%1/images/default.png").arg(hostFromAcct(userId)); } if (!var[QLatin1String("generator")].isNull()) { p->source = var[QLatin1String("generator")].toMap().value(QLatin1String("displayName")).toString(); } const QVariantList to = var[QLatin1String("to")].toList(); for (const QVariant &element: to) { QVariantMap toElementMap = element.toMap(); QString toElementType = toElementMap.value(QLatin1String("objectType")).toString(); if (toElementType == QLatin1String("person") || toElementType == QLatin1String("collection")) { const QString toId = toElementMap.value(QLatin1String("id")).toString(); if (toId.compare(QLatin1String("acct:")) != 0) { p->to.append(toId); } } } const QVariantList cc = var[QLatin1String("cc")].toList(); for (const QVariant &element: cc) { QVariantMap ccElementMap = element.toMap(); QString ccElementType = ccElementMap.value(QLatin1String("objectType")).toString(); if (ccElementType == QLatin1String("person") || ccElementType == QLatin1String("collection")) { const QString ccId = ccElementMap.value(QLatin1String("id")).toString(); if (ccId.compare(QLatin1String("acct:")) != 0) { p->cc.append(ccId); } } } const QVariantMap replies = object[QLatin1String("replies")].toMap(); if (replies.value(QLatin1String("pump_io")).isNull()) { p->replies = replies[QLatin1String("url")].toString(); } else { p->replies = replies[QLatin1String("pump_io")].toMap().value(QLatin1String("proxyURL")).toString(); } return p; } else { qCDebug(CHOQOK) << "post is not a PumpIOPost!"; return post; } } QList< Choqok::Post * > PumpIOMicroBlog::readTimeline(const QByteArray &buffer) { QList posts; const QJsonDocument json = QJsonDocument::fromJson(buffer); if (!json.isNull()) { const QVariantList list = json.toVariant().toMap().value(QLatin1String("items")).toList(); for (const QVariant &element: list) { const QVariantMap elementMap = element.toMap(); if (!elementMap[QLatin1String("object")].toMap().value(QLatin1String("deleted")).isNull()) { // Skip deleted posts continue; } posts.prepend(readPost(elementMap, new PumpIOPost)); } } else { qCDebug(CHOQOK) << "Cannot parse JSON reply"; } return posts; } void PumpIOMicroBlog::setLastTimelineId(Choqok::Account *theAccount, const QString &timeline, const QString &id) { m_timelinesLatestIds[theAccount][timeline] = id; } void PumpIOMicroBlog::setTimelinesInfo() { Choqok::TimelineInfo *t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "Activity"); t->description = i18nc("Timeline description", "You and people you follow"); t->icon = QLatin1String("user-home"); m_timelinesInfos[QLatin1String("Activity")] = t; m_timelinesPaths[QLatin1String("Activity")] = inboxActivity + QLatin1String("/major"); t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "Favorites"); t->description = i18nc("Timeline description", "Posts you favorited"); t->icon = QLatin1String("favorites"); m_timelinesInfos[QLatin1String("Favorites")] = t; m_timelinesPaths[QLatin1String("Favorites")] = QLatin1String("/api/user/%1/favorites"); t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "Inbox"); t->description = i18nc("Timeline description", "Posts sent to you"); t->icon = QLatin1String("mail-folder-inbox"); m_timelinesInfos[QLatin1String("Inbox")] = t; m_timelinesPaths[QLatin1String("Inbox")] = inboxActivity + QLatin1String("/direct/major/"); t = new Choqok::TimelineInfo; t->name = i18nc("Timeline Name", "Outbox"); t->description = i18nc("Timeline description", "Posts you sent"); t->icon = QLatin1String("mail-folder-outbox"); m_timelinesInfos[QLatin1String("Outbox")] = t; m_timelinesPaths[QLatin1String("Outbox")] = outboxActivity + QLatin1String("/major/"); } void PumpIOMicroBlog::updatePost(Choqok::Account *theAccount, Choqok::Post *post) { PumpIOAccount *acc = qobject_cast(theAccount); if (acc) { QVariantMap object; object.insert(QLatin1String("id"), post->postId); object.insert(QLatin1String("objectType"), post->type); object.insert(QLatin1String("content"), QUrl::toPercentEncoding(post->content)); // https://github.com/e14n/pump.io/issues/885 QVariantList to; QVariantMap thePublic; thePublic.insert(QLatin1String("objectType"), QLatin1String("collection")); thePublic.insert(QLatin1String("id"), PumpIOMicroBlog::PublicCollection); to.append(thePublic); QVariantMap item; item.insert(QLatin1String("verb"), QLatin1String("update")); item.insert(QLatin1String("object"), object); item.insert(QLatin1String("to"), to); const QByteArray data = QJsonDocument::fromVariant(item).toJson(); QUrl url(acc->host()); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QLatin1Char('/') + (outboxActivity.arg(acc->username()))); KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo); job->addMetaData(QLatin1String("content-type"), QLatin1String("Content-Type: application/json")); - job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QOAuth::POST)); + job->addMetaData(QLatin1String("customHTTPHeader"), authorizationMetaData(acc, url, QNetworkAccessManager::PostOperation)); if (!job) { qCDebug(CHOQOK) << "Cannot create an http POST request!"; return; } m_accountJobs[job] = acc; m_updateJobs[job] = post; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotUpdatePost(KJob*))); job->start(); } else { qCDebug(CHOQOK) << "theAccount is not a PumpIOAccount!"; } } QString PumpIOMicroBlog::hostFromAcct(const QString &acct) { if (acct.contains(QLatin1String("acct:"))) { return acct.split(QLatin1Char(':'))[1].split(QLatin1Char('@'))[1]; } return acct; } QString PumpIOMicroBlog::userNameFromAcct(const QString &acct) { if (acct.contains(QLatin1String("acct:"))) { return acct.split(QLatin1Char(':'))[1].split(QLatin1Char('@'))[0]; } return acct; } #include "pumpiomicroblog.moc" diff --git a/microblogs/pumpio/pumpiomicroblog.h b/microblogs/pumpio/pumpiomicroblog.h index fc10063b..ade3cf4e 100644 --- a/microblogs/pumpio/pumpiomicroblog.h +++ b/microblogs/pumpio/pumpiomicroblog.h @@ -1,165 +1,165 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2013-2014 Andrea Scarpino This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #ifndef PUMPIOMICROBLOG_H #define PUMPIOMICROBLOG_H -#include "QtOAuth/qoauth_namespace.h" +#include #include "microblog.h" class QUrl; class KJob; class PumpIOAccount; class PumpIOPost; class PumpIOMicroBlog : public Choqok::MicroBlog { Q_OBJECT public: explicit PumpIOMicroBlog(QObject *parent, const QVariantList &args); virtual ~PumpIOMicroBlog(); virtual void abortAllJobs(Choqok::Account *theAccount) override; virtual void abortCreatePost(Choqok::Account *theAccount, Choqok::Post *post = 0) override; virtual void aboutToUnload() override; virtual QMenu *createActionsMenu(Choqok::Account *theAccount, QWidget *parent = Choqok::UI::Global::mainWindow()) override; virtual Choqok::UI::ComposerWidget *createComposerWidget(Choqok::Account *account, QWidget *parent) override; virtual ChoqokEditAccountWidget *createEditAccountWidget(Choqok::Account *account, QWidget *parent) override; virtual Choqok::UI::MicroBlogWidget *createMicroBlogWidget(Choqok::Account *account, QWidget *parent) override; virtual Choqok::Account *createNewAccount(const QString &alias) override; virtual void createPost(Choqok::Account *theAccount, Choqok::Post *post) override; virtual Choqok::UI::PostWidget *createPostWidget(Choqok::Account *account, Choqok::Post *post, QWidget *parent) override; virtual void fetchPost(Choqok::Account *theAccount, Choqok::Post *post) override; virtual QList loadTimeline(Choqok::Account *account, const QString &timelineName) override; virtual void removePost(Choqok::Account *theAccount, Choqok::Post *post) override; virtual QString postUrl(Choqok::Account *account, const QString &username, const QString &postId) const override; virtual QString profileUrl(Choqok::Account *account, const QString &username) const override; virtual void saveTimeline(Choqok::Account *account, const QString &timelineName, const QList< Choqok::UI::PostWidget * > &timeline) override; virtual Choqok::TimelineInfo *timelineInfo(const QString &timelineName) override; virtual void updateTimelines(Choqok::Account *theAccount) override; void createPost(Choqok::Account *theAccount, Choqok::Post *post, const QVariantList &to, const QVariantList &cc = QVariantList()); void createPostWithMedia(Choqok::Account *theAccount, Choqok::Post *post, const QString &filePath); void createReply(Choqok::Account *theAccount, PumpIOPost *post); void fetchFollowing(Choqok::Account *theAccount); void fetchLists(Choqok::Account *theAccount); void share(Choqok::Account *theAccount, Choqok::Post *post); void toggleFavorite(Choqok::Account *theAccount, Choqok::Post *post); void fetchReplies(Choqok::Account *theAccount, const QString &url); static QString userNameFromAcct(const QString &acct); static QString hostFromAcct(const QString &acct); static const QString PublicCollection; Q_SIGNALS: void favorite(Choqok::Account *, Choqok::Post *); void followingFetched(Choqok::Account *); void listsFetched(Choqok::Account *); protected Q_SLOTS: void showDirectMessageDialog(); void slotCreatePost(KJob *job); void slotFavorite(KJob *job); void slotFetchPost(KJob *job); void slotFetchReplies(KJob *job); void slotFollowing(KJob *job); void slotLists(KJob *job); void slotRemovePost(KJob *job); void slotShare(KJob *job); void slotUpdatePost(KJob *job); void slotUpdateTimeline(KJob *job); void slotUpload(KJob *job); protected: static const QString inboxActivity; static const QString outboxActivity; QString authorizationMetaData(PumpIOAccount *account, const QUrl &url, - const QOAuth::HttpMethod &method, - const QOAuth::ParamMap &map = QOAuth::ParamMap()) const; + QNetworkAccessManager::Operation method, + const QVariantMap &map = QVariantMap()) const; QString lastTimelineId(Choqok::Account *theAccount, const QString &timeline) const; Choqok::Post *readPost(const QVariantMap &var, Choqok::Post *post); QList readTimeline(const QByteArray &buffer); void setLastTimelineId(Choqok::Account *theAccount, const QString &timeline, const QString &id); void setTimelinesInfo(); void updatePost(Choqok::Account *theAccount, Choqok::Post *post); QMap m_accountJobs; QMap m_createPostJobs; QMap m_favoriteJobs; QMap m_removePostJobs; QMap m_shareJobs; QMap m_uploadJobs; QMap m_updateJobs; QMap m_timelinesInfos; QHash > m_timelinesLatestIds; QHash m_timelinesPaths; QMap m_timelinesRequests; private: class Private; Private *const d; }; #endif // PUMPIOMICROBLOG_H diff --git a/microblogs/pumpio/pumpiooauth.cpp b/microblogs/pumpio/pumpiooauth.cpp new file mode 100644 index 00000000..362df4fa --- /dev/null +++ b/microblogs/pumpio/pumpiooauth.cpp @@ -0,0 +1,92 @@ +/* + This file is part of Choqok, the KDE micro-blogging client + + Copyright (C) 2017 Andrea Scarpino + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License or (at your option) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see http://www.gnu.org/licenses/ + +*/ + +#include "pumpiooauth.h" + +#include +#include + +#include + +#include "choqokoauthreplyhandler.h" + +#include "pumpioaccount.h" +#include "pumpiodebug.h" + +PumpIOOAuth::PumpIOOAuth(PumpIOAccount *account) + : QOAuth1(account), m_replyHandler(0), m_networkAccessManager(0) +{ + qCDebug(CHOQOK); + + m_replyHandler = new ChoqokOAuthReplyHandler(this); + setReplyHandler(m_replyHandler); + + m_networkAccessManager = new KIO::AccessManager(this); + setNetworkAccessManager(m_networkAccessManager); + + setClientIdentifier(account->consumerKey()); + setClientSharedSecret(account->consumerSecret()); + + setSignatureMethod(SignatureMethod::Hmac_Sha1); + + setTemporaryCredentialsUrl(QUrl(account->host() + QLatin1String("/oauth/request_token"))); + setAuthorizationUrl(QUrl(account->host() + QLatin1String("/oauth/authorize"))); + setTokenCredentialsUrl(QUrl(account->host() + QLatin1String("/oauth/access_token"))); +} + +PumpIOOAuth::~PumpIOOAuth() +{ + m_replyHandler->deleteLater(); + m_networkAccessManager->deleteLater(); +} + +QByteArray PumpIOOAuth::authorizationHeader(const QUrl &requestUrl, QNetworkAccessManager::Operation operation, + const QVariantMap &signingParameters) +{ + QVariantMap oauthParams; + QVariantMap otherParams = signingParameters; + // Adding parameters located in the query + { + auto queryItems = QUrlQuery(requestUrl.query()).queryItems(); + for (auto it = queryItems.begin(), end = queryItems.end(); it != end; ++it) + otherParams.insert(it->first, it->second); + } + + const auto currentDateTime = QDateTime::currentDateTimeUtc(); + + oauthParams.insert(QStringLiteral("oauth_consumer_key"), clientIdentifier()); + oauthParams.insert(QStringLiteral("oauth_version"), QStringLiteral("1.0")); + oauthParams.insert(QStringLiteral("oauth_token"), token()); + oauthParams.insert(QStringLiteral("oauth_signature_method"), QStringLiteral("HMAC-SHA1")); + oauthParams.insert(QStringLiteral("oauth_nonce"), QOAuth1::nonce()); + oauthParams.insert(QStringLiteral("oauth_timestamp"), QString::number(currentDateTime.toTime_t())); + + // Add signature parameter + { + const auto parameters = QVariantMap(oauthParams).unite(signingParameters); + oauthParams.insert(QStringLiteral("oauth_signature"), signature(parameters, requestUrl, + operation, clientSharedSecret(), tokenSecret())); + } + + return generateAuthorizationHeader(oauthParams); +} diff --git a/libchoqok/choqoktools.h b/microblogs/pumpio/pumpiooauth.h similarity index 59% copy from libchoqok/choqoktools.h copy to microblogs/pumpio/pumpiooauth.h index e0bbbb4d..dcf2615f 100644 --- a/libchoqok/choqoktools.h +++ b/microblogs/pumpio/pumpiooauth.h @@ -1,43 +1,51 @@ /* This file is part of Choqok, the KDE micro-blogging client - Copyright (C) 2008-2012 Mehrdad Momeny + Copyright (C) 2017 Andrea Scarpino This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ -#ifndef CHOQOK_CHOQOKTOOLS_H -#define CHOQOK_CHOQOKTOOLS_H +#ifndef PUMPIOOAUTH_H +#define PUMPIOOAUTH_H -#include -#include +#include -#include "choqok_export.h" +class ChoqokOAuthReplyHandler; +class PumpIOAccount; -namespace Choqok -{ - -void CHOQOK_EXPORT openUrl(const QUrl &url); +namespace KIO { +class AccessManager; +} -QString CHOQOK_EXPORT qoauthErrorText(int code); +class PumpIOOAuth : public QOAuth1 +{ + Q_OBJECT +public: + explicit PumpIOOAuth(PumpIOAccount *account); + ~PumpIOOAuth(); -QString CHOQOK_EXPORT getColorString(const QColor &color); + QByteArray authorizationHeader(const QUrl &requestUrl, QNetworkAccessManager::Operation method, + const QVariantMap ¶ms = QVariantMap()); -} +private: + ChoqokOAuthReplyHandler *m_replyHandler; + KIO::AccessManager *m_networkAccessManager; +}; -#endif // CHOQOK_CHOQOKTOOLS_H +#endif // PUMPIOOAUTH_H diff --git a/microblogs/twitter/CMakeLists.txt b/microblogs/twitter/CMakeLists.txt index 2621e18b..f7cc92f6 100644 --- a/microblogs/twitter/CMakeLists.txt +++ b/microblogs/twitter/CMakeLists.txt @@ -1,45 +1,45 @@ include_directories( ${CHOQOK_INCLUDES} ) set(choqok_twitter_SRCS twittercomposerwidget.cpp twitteraccount.cpp twitterdebug.cpp twitterdmessagedialog.cpp twittermicroblog.cpp twitterpostwidget.cpp twittersearch.cpp twittertextedit.cpp twittereditaccount.cpp twitterlistdialog.cpp twittertimelinewidget.cpp ) ki18n_wrap_ui(choqok_twitter_SRCS twittereditaccount_base.ui twitterlistdialog_base.ui ) add_library(choqok_twitter MODULE ${choqok_twitter_SRCS}) kcoreaddons_desktop_to_json(choqok_twitter choqok_twitter.desktop) target_link_libraries(choqok_twitter Qt5::Core + Qt5::NetworkAuth Qt5::Widgets KF5::CoreAddons KF5::I18n KF5::KIOCore KF5::KIOWidgets KF5::WidgetsAddons - ${QTOAUTH_LIBRARY} qca-qt5 choqok twitterapihelper ) install(TARGETS choqok_twitter DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES choqok_twitter.desktop DESTINATION ${SERVICES_INSTALL_DIR}) add_subdirectory(icons) diff --git a/microblogs/twitter/twitteraccount.cpp b/microblogs/twitter/twitteraccount.cpp index 80ed3b97..17c06a81 100644 --- a/microblogs/twitter/twitteraccount.cpp +++ b/microblogs/twitter/twitteraccount.cpp @@ -1,115 +1,122 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2008-2012 Mehrdad Momeny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #include "twitteraccount.h" #include "twitterdebug.h" #include "twittermicroblog.h" class TwitterAccount::Private { public: QString uploadHost; QUrl uploadUrl; // QStringList lists; }; +const char *twitterConsumerKey = "VyXMf0O7CvciiUQjliYtYg"; +const char *twitterConsumerSecret = "uD2HvsOBjzt1Vs6SnouFtuxDeHmvOOVwmn3fBVyCw0"; + TwitterAccount::TwitterAccount(TwitterMicroBlog *parent, const QString &alias) : TwitterApiAccount(parent, alias), d(new Private) { setHost(QLatin1String("https://api.twitter.com")); setUploadHost(QLatin1String("https://api.twitter.com")); setApi(QLatin1String("1.1")); qCDebug(CHOQOK) << "Set API version to 1.1"; + setOauthConsumerKey(twitterConsumerKey); + setOauthConsumerSecret(twitterConsumerSecret); + setUsingOAuth(true); + // d->lists = configGroup()->readEntry("lists", QStringList()); QStringList lists; for (const QString &tm: timelineNames()) { if (tm.startsWith(QLatin1Char('@'))) { lists.append(tm); } } if (!lists.isEmpty()) { parent->setListTimelines(this, lists); } } TwitterAccount::~TwitterAccount() { delete d; } void TwitterAccount::setApi(const QString &api) { TwitterApiAccount::setApi(api); generateUploadUrl(); } QUrl TwitterAccount::uploadUrl() const { return d->uploadUrl; } void TwitterAccount::setUploadUrl(const QUrl &url) { d->uploadUrl = url; } QString TwitterAccount::uploadHost() const { return d->uploadHost; } void TwitterAccount::setUploadHost(const QString &uploadHost) { d->uploadHost = uploadHost; } void TwitterAccount::generateUploadUrl() { if (!uploadHost().startsWith(QLatin1String("http"))) { //NOTE: This is for compatibility by prev versions. remove it after 1.0 release setUploadHost(uploadHost().prepend(QLatin1String("http://"))); } QUrl url(uploadHost()); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QLatin1Char('/') + (api())); setUploadUrl(url); } /* void TwitterAccount::writeConfig() { qCDebug(CHOQOK)<lists; configGroup()->writeEntry("lists", d->lists); TwitterApiAccount::writeConfig(); } void TwitterAccount::addList(const QString& name) { d->lists << name; } void TwitterAccount::removeList(const QString& name) { d->lists.removeOne(name); }*/ diff --git a/microblogs/twitter/twitterdmessagedialog.cpp b/microblogs/twitter/twitterdmessagedialog.cpp index 8d261dc4..6e536499 100644 --- a/microblogs/twitter/twitterdmessagedialog.cpp +++ b/microblogs/twitter/twitterdmessagedialog.cpp @@ -1,78 +1,78 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2015 Andrea Scarpino This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #include "twitterdmessagedialog.h" #include #include #include "choqoktextedit.h" #include "twitterapiaccount.h" #include "twitterapidebug.h" #include "twitterapimicroblog.h" TwitterDMessageDialog::TwitterDMessageDialog(TwitterApiAccount *theAccount, QWidget *parent, Qt::WindowFlags flags) : TwitterApiDMessageDialog(theAccount, parent, flags) { fetchTextLimit(); } TwitterDMessageDialog::~TwitterDMessageDialog() { } void TwitterDMessageDialog::fetchTextLimit() { QUrl url = account()->apiUrl(); url.setPath(url.path() + QLatin1String("/help/configuration.json")); KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo); if (!job) { qCDebug(CHOQOK) << "Cannot create an http GET request!"; return; } TwitterApiMicroBlog *mBlog = qobject_cast(account()->microblog()); job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + - QLatin1String(mBlog->authorizationHeader(account(), url, QOAuth::GET))); + QLatin1String(mBlog->authorizationHeader(account(), url, QNetworkAccessManager::GetOperation))); connect(job, SIGNAL(result(KJob*)), this, SLOT(slotTextLimit(KJob*))); job->start(); } void TwitterDMessageDialog::slotTextLimit(KJob *job) { if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); } else { KIO::StoredTransferJob *j = qobject_cast(job); const QJsonDocument json = QJsonDocument::fromJson(j->data()); if (!json.isNull()) { const int textLimit = json.toVariant().toMap().value(QLatin1String("dm_text_character_limit")).toInt(); editor()->setCharLimit(textLimit); } else { qCDebug(CHOQOK) << "Cannot parse JSON reply"; } } } diff --git a/microblogs/twitter/twittereditaccount.cpp b/microblogs/twitter/twittereditaccount.cpp index f6746947..8c5bc739 100644 --- a/microblogs/twitter/twittereditaccount.cpp +++ b/microblogs/twitter/twittereditaccount.cpp @@ -1,215 +1,161 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2008-2012 Mehrdad Momeny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #include "twittereditaccount.h" #include #include #include #include -#include -#include - #include "accountmanager.h" #include "choqoktools.h" #include "twitteraccount.h" #include "twitterdebug.h" #include "twittermicroblog.h" - -const char *twitterConsumerKey = "VyXMf0O7CvciiUQjliYtYg"; -const char *twitterConsumerSecret = "uD2HvsOBjzt1Vs6SnouFtuxDeHmvOOVwmn3fBVyCw0"; +#include "twitterapioauth.h" TwitterEditAccountWidget::TwitterEditAccountWidget(TwitterMicroBlog *microblog, TwitterAccount *account, QWidget *parent) : ChoqokEditAccountWidget(account, parent), mAccount(account) { setupUi(this); kcfg_basicAuth->hide(); connect(kcfg_authorize, SIGNAL(clicked(bool)), SLOT(authorizeUser())); if (mAccount) { kcfg_alias->setText(mAccount->alias()); - if (mAccount->oauthToken().isEmpty() || mAccount->oauthTokenSecret().isEmpty()) { - setAuthenticated(false); - } else { - setAuthenticated(true); - token = mAccount->oauthToken(); - tokenSecret = mAccount->oauthTokenSecret(); - username = mAccount->username(); - } + username = mAccount->username(); + setAuthenticated(!mAccount->oauthToken().isEmpty() && !mAccount->oauthTokenSecret().isEmpty()); } else { setAuthenticated(false); QString newAccountAlias = microblog->serviceName(); QString servName = newAccountAlias; int counter = 1; while (Choqok::AccountManager::self()->findAccount(newAccountAlias)) { newAccountAlias = QStringLiteral("%1%2").arg(servName).arg(counter); counter++; } setAccount(mAccount = new TwitterAccount(microblog, newAccountAlias)); kcfg_alias->setText(newAccountAlias); } loadTimelinesTableState(); kcfg_alias->setFocus(Qt::OtherFocusReason); } TwitterEditAccountWidget::~TwitterEditAccountWidget() { } bool TwitterEditAccountWidget::validateData() { if (kcfg_alias->text().isEmpty() || !isAuthenticated) { return false; } else { return true; } } Choqok::Account *TwitterEditAccountWidget::apply() { qCDebug(CHOQOK); mAccount->setAlias(kcfg_alias->text()); mAccount->setUsername(username); - mAccount->setOauthToken(token); - mAccount->setOauthTokenSecret(tokenSecret); - mAccount->setOauthConsumerKey(twitterConsumerKey); - mAccount->setOauthConsumerSecret(twitterConsumerSecret); - mAccount->setUsingOAuth(true); + mAccount->setOauthToken(mAccount->oauthInterface()->token().toLatin1()); + mAccount->setOauthTokenSecret(mAccount->oauthInterface()->tokenSecret().toLatin1()); saveTimelinesTableState(); mAccount->writeConfig(); return mAccount; } void TwitterEditAccountWidget::authorizeUser() { qCDebug(CHOQOK); - qoauth = new QOAuth::Interface(new KIO::Integration::AccessManager(this), this); - // set the consumer key and secret - qoauth->setConsumerKey(twitterConsumerKey); - qoauth->setConsumerSecret(twitterConsumerSecret); - // set a timeout for requests (in msecs) - qoauth->setRequestTimeout(20000); - qoauth->setIgnoreSslErrors(true); - - QOAuth::ParamMap otherArgs; - - // send a request for an unauthorized token - QOAuth::ParamMap reply = - qoauth->requestToken(QLatin1String("https://twitter.com/oauth/request_token"), - QOAuth::GET, QOAuth::HMAC_SHA1); - - // if no error occurred, read the received token and token secret - if (qoauth->error() == QOAuth::NoError) { - token = reply.value(QOAuth::tokenParameterName()); - tokenSecret = reply.value(QOAuth::tokenSecretParameterName()); - qCDebug(CHOQOK) << "token:" << token; - QUrl url(QLatin1String("https://twitter.com/oauth/authorize")); - QUrlQuery urlQuery; - urlQuery.addQueryItem(QLatin1String("oauth_token"), QLatin1String(token)); - urlQuery.addQueryItem(QLatin1String("oauth_callback"), QLatin1String("oob")); - url.setQuery(urlQuery); - Choqok::openUrl(url); - getPinCode(); - } else { - qCDebug(CHOQOK) << "ERROR:" << qoauth->error() << Choqok::qoauthErrorText(qoauth->error()); - KMessageBox::detailedError(this, i18n("Authorization Error"), - Choqok::qoauthErrorText(qoauth->error())); - } + + mAccount->oauthInterface()->grant(); + + connect(mAccount->oauthInterface(), &QAbstractOAuth::authorizeWithBrowser, &Choqok::openUrl); + connect(mAccount->oauthInterface(), &QAbstractOAuth::statusChanged, this, &TwitterEditAccountWidget::getPinCode); } void TwitterEditAccountWidget::getPinCode() { isAuthenticated = false; - while (!isAuthenticated) { + if (mAccount->oauthInterface()->status() == QAbstractOAuth::Status::TemporaryCredentialsReceived) { QString verifier = QInputDialog::getText(this, i18n("PIN"), i18n("Enter the PIN received from Twitter:")); if (verifier.isEmpty()) { return; } - QOAuth::ParamMap otherArgs; - otherArgs.insert("oauth_verifier", verifier.toUtf8()); - - // send a request to exchange Request Token for an Access Token - QOAuth::ParamMap reply = - qoauth->accessToken(QLatin1String("https://twitter.com/oauth/access_token"), QOAuth::POST, token, - tokenSecret, QOAuth::HMAC_SHA1, otherArgs); - // if no error occurred, read the Access Token (and other arguments, if applicable) - if (qoauth->error() == QOAuth::NoError) { - username = QLatin1String(reply.value("screen_name")); - token = reply.value(QOAuth::tokenParameterName()); - tokenSecret = reply.value(QOAuth::tokenSecretParameterName()); - setAuthenticated(true); - KMessageBox::information(this, i18n("Choqok is authorized successfully."), - i18n("Authorized")); - } else { - qCDebug(CHOQOK) << "ERROR:" << qoauth->error() << Choqok::qoauthErrorText(qoauth->error()); - KMessageBox::detailedError(this, i18n("Authorization Error"), - Choqok::qoauthErrorText(qoauth->error())); - } + + mAccount->oauthInterface()->continueGrantWithVerifier(verifier); + } else if (mAccount->oauthInterface()->status() == QAbstractOAuth::Status::Granted) { + setAuthenticated(true); + KMessageBox::information(this, i18n("Choqok is authorized successfully."), i18n("Authorized")); + } else { + KMessageBox::detailedError(this, i18n("Authorization Error"), i18n("OAuth authorization error")); } } void TwitterEditAccountWidget::setAuthenticated(bool authenticated) { isAuthenticated = authenticated; if (authenticated) { kcfg_authorize->setIcon(QIcon::fromTheme(QLatin1String("object-unlocked"))); kcfg_authenticateLed->on(); kcfg_authenticateStatus->setText(i18n("Authenticated")); } else { kcfg_authorize->setIcon(QIcon::fromTheme(QLatin1String("object-locked"))); kcfg_authenticateLed->off(); kcfg_authenticateStatus->setText(i18n("Not Authenticated")); } } void TwitterEditAccountWidget::loadTimelinesTableState() { for (const QString &timeline: mAccount->microblog()->timelineNames()) { int newRow = timelinesTable->rowCount(); timelinesTable->insertRow(newRow); timelinesTable->setItem(newRow, 0, new QTableWidgetItem(timeline)); QCheckBox *enable = new QCheckBox(timelinesTable); enable->setChecked(mAccount->timelineNames().contains(timeline)); timelinesTable->setCellWidget(newRow, 1, enable); } } void TwitterEditAccountWidget::saveTimelinesTableState() { QStringList timelines; int rowCount = timelinesTable->rowCount(); for (int i = 0; i < rowCount; ++i) { QCheckBox *enable = qobject_cast(timelinesTable->cellWidget(i, 1)); if (enable && enable->isChecked()) { timelines << timelinesTable->item(i, 0)->text(); } } timelines.removeDuplicates(); mAccount->setTimelineNames(timelines); } diff --git a/microblogs/twitter/twittereditaccount.h b/microblogs/twitter/twittereditaccount.h index f51df639..9ee8d184 100644 --- a/microblogs/twitter/twittereditaccount.h +++ b/microblogs/twitter/twittereditaccount.h @@ -1,81 +1,73 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2008-2012 Mehrdad Momeny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #ifndef TWITTEREDITACCOUNT_H #define TWITTEREDITACCOUNT_H #include "editaccountwidget.h" #include "ui_twittereditaccount_base.h" -namespace QOAuth -{ -class Interface; -} - class QProgressBar; class TwitterAccount; class TwitterMicroBlog; /** @author Mehrdad Momeny \ */ class TwitterEditAccountWidget : public ChoqokEditAccountWidget, public Ui::TwitterEditAccountBase { Q_OBJECT public: TwitterEditAccountWidget(TwitterMicroBlog *microblog, TwitterAccount *account, QWidget *parent); /** * Destructor */ ~TwitterEditAccountWidget(); virtual bool validateData() override; /** * Create a new account if we are in the 'add account wizard', * otherwise update the existing account. * @Return new or modified account. OR nullptr on failure. */ virtual Choqok::Account *apply() override; protected Q_SLOTS: void authorizeUser(); protected: void loadTimelinesTableState(); void saveTimelinesTableState(); virtual void getPinCode(); void setAuthenticated(bool authenticated); bool isAuthenticated; TwitterMicroBlog *mBlog; TwitterAccount *mAccount; QProgressBar *progress; QString username; - QByteArray token; - QByteArray tokenSecret; - QOAuth::Interface *qoauth; }; #endif diff --git a/microblogs/twitter/twittermicroblog.cpp b/microblogs/twitter/twittermicroblog.cpp index 093c7e7e..ac1833e3 100644 --- a/microblogs/twitter/twittermicroblog.cpp +++ b/microblogs/twitter/twittermicroblog.cpp @@ -1,442 +1,442 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2008-2012 Mehrdad Momeny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #include "twittermicroblog.h" #include #include #include #include #include #include #include #include #include "account.h" #include "accountmanager.h" #include "choqokappearancesettings.h" #include "choqoktypes.h" #include "composerwidget.h" #include "editaccountwidget.h" #include "mediamanager.h" #include "postwidget.h" #include "timelinewidget.h" #include "twitterapimicroblogwidget.h" #include "twitteraccount.h" #include "twittercomposerwidget.h" #include "twitterdebug.h" #include "twitterdmessagedialog.h" #include "twittereditaccount.h" #include "twitterlistdialog.h" #include "twitterpostwidget.h" #include "twittersearch.h" #include "twittertimelinewidget.h" K_PLUGIN_FACTORY_WITH_JSON(TwitterMicroBlogFactory, "choqok_twitter.json", registerPlugin < TwitterMicroBlog > ();) TwitterMicroBlog::TwitterMicroBlog(QObject *parent, const QVariantList &) : TwitterApiMicroBlog(QLatin1String("choqok_twitter"), parent) { qCDebug(CHOQOK); setServiceName(QLatin1String("Twitter")); setServiceHomepageUrl(QLatin1String("https://twitter.com/")); timelineApiPath[QLatin1String("Reply")] = QLatin1String("/statuses/mentions_timeline.%1"); setTimelineInfos(); } void TwitterMicroBlog::setTimelineInfos() { // hange description of replies to mentions Choqok::TimelineInfo *t = mTimelineInfos[QLatin1String("Reply")]; t->name = i18nc("Timeline Name", "Mentions"); t->description = i18nc("Timeline description", "Mentions of you"); } TwitterMicroBlog::~TwitterMicroBlog() { qCDebug(CHOQOK); } Choqok::Account *TwitterMicroBlog::createNewAccount(const QString &alias) { TwitterAccount *acc = qobject_cast(Choqok::AccountManager::self()->findAccount(alias)); if (!acc) { return new TwitterAccount(this, alias); } else { return 0; } } ChoqokEditAccountWidget *TwitterMicroBlog::createEditAccountWidget(Choqok::Account *account, QWidget *parent) { qCDebug(CHOQOK); TwitterAccount *acc = qobject_cast(account); if (acc || !account) { return new TwitterEditAccountWidget(this, acc, parent); } else { qCDebug(CHOQOK) << "Account passed here is not a TwitterAccount!"; return nullptr; } } Choqok::UI::MicroBlogWidget *TwitterMicroBlog::createMicroBlogWidget(Choqok::Account *account, QWidget *parent) { return new TwitterApiMicroBlogWidget(account, parent); } Choqok::UI::TimelineWidget *TwitterMicroBlog::createTimelineWidget(Choqok::Account *account, const QString &timelineName, QWidget *parent) { return new TwitterTimelineWidget(account, timelineName, parent); } Choqok::UI::PostWidget *TwitterMicroBlog::createPostWidget(Choqok::Account *account, Choqok::Post *post, QWidget *parent) { return new TwitterPostWidget(account, post, parent); } Choqok::UI::ComposerWidget *TwitterMicroBlog::createComposerWidget(Choqok::Account *account, QWidget *parent) { return new TwitterComposerWidget(account, parent); } QString TwitterMicroBlog::profileUrl(Choqok::Account *, const QString &username) const { return QStringLiteral("https://twitter.com/#!/%1").arg(username); } QString TwitterMicroBlog::postUrl(Choqok::Account *, const QString &username, const QString &postId) const { return QStringLiteral("https://twitter.com/%1/status/%2").arg(username).arg(postId); } TwitterApiSearch *TwitterMicroBlog::searchBackend() { if (!mSearchBackend) { mSearchBackend = new TwitterSearch(this); } return mSearchBackend; } void TwitterMicroBlog::createPostWithAttachment(Choqok::Account *theAccount, Choqok::Post *post, const QString &mediumToAttach) { if (mediumToAttach.isEmpty()) { TwitterApiMicroBlog::createPost(theAccount, post); } else { const QUrl picUrl = QUrl::fromUserInput(mediumToAttach); KIO::StoredTransferJob *picJob = KIO::storedGet(picUrl, KIO::Reload, KIO::HideProgressInfo); picJob->exec(); if (picJob->error()) { qCCritical(CHOQOK) << "Job error:" << picJob->errorString(); KMessageBox::detailedError(Choqok::UI::Global::mainWindow(), i18n("Uploading medium failed: cannot read the medium file."), picJob->errorString()); return; } const QByteArray picData = picJob->data(); if (picData.count() == 0) { qCCritical(CHOQOK) << "Cannot read the media file, please check if it exists."; KMessageBox::error(Choqok::UI::Global::mainWindow(), i18n("Uploading medium failed: cannot read the medium file.")); return; } TwitterAccount *account = qobject_cast(theAccount); QUrl url = account->uploadUrl(); url.setPath(url.path() + QStringLiteral("/statuses/update_with_media.%1").arg(format)); const QMimeDatabase db; QByteArray fileContentType = db.mimeTypeForUrl(picUrl).name().toUtf8(); QMap formdata; formdata[QLatin1String("status")] = post->content.toUtf8(); if (!post->replyToPostId.isEmpty()) { formdata[QLatin1String("in_reply_to_status_id")] = post->replyToPostId.toLatin1(); } formdata[QLatin1String("source")] = QCoreApplication::applicationName().toLatin1(); QMap mediafile; mediafile[QLatin1String("name")] = "media[]"; mediafile[QLatin1String("filename")] = picUrl.fileName().toUtf8(); mediafile[QLatin1String("mediumType")] = fileContentType; mediafile[QLatin1String("medium")] = picData; QList< QMap > listMediafiles; listMediafiles.append(mediafile); QByteArray data = Choqok::MediaManager::createMultipartFormData(formdata, listMediafiles); KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo) ; if (!job) { qCCritical(CHOQOK) << "Cannot create a http POST request!"; return; } job->addMetaData(QStringLiteral("content-type"), QStringLiteral("Content-Type: multipart/form-data; boundary=AaB03x")); job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + - QLatin1String(authorizationHeader(account, url, QOAuth::POST))); + QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation))); mCreatePostMap[ job ] = post; mJobsAccount[job] = theAccount; connect(job, SIGNAL(result(KJob*)), SLOT(slotCreatePost(KJob*))); job->start(); } } void TwitterMicroBlog::showDirectMessageDialog(TwitterApiAccount *theAccount, const QString &toUsername) { qCDebug(CHOQOK); if (!theAccount) { QAction *act = qobject_cast(sender()); theAccount = qobject_cast( Choqok::AccountManager::self()->findAccount(act->data().toString())); } TwitterDMessageDialog *dmsg = new TwitterDMessageDialog(theAccount, Choqok::UI::Global::mainWindow()); if (!toUsername.isEmpty()) { dmsg->setTo(toUsername); } dmsg->show(); } QString TwitterMicroBlog::generateRepeatedByUserTooltip(const QString &username) { if (Choqok::AppearanceSettings::showRetweetsInChoqokWay()) { return i18n("Retweet of %1", username); } else { return i18n("Retweeted by %1", username); } } QString TwitterMicroBlog::repeatQuestion() { return i18n("Retweet to your followers?"); } QMenu *TwitterMicroBlog::createActionsMenu(Choqok::Account *theAccount, QWidget *parent) { QMenu *menu = TwitterApiMicroBlog::createActionsMenu(theAccount, parent); QAction *lists = new QAction(i18n("Add User List..."), menu); lists->setData(theAccount->alias()); connect(lists, SIGNAL(triggered(bool)), SLOT(showListDialog())); menu->addAction(lists); return menu; } void TwitterMicroBlog::showListDialog(TwitterApiAccount *theAccount) { if (!theAccount) { QAction *act = qobject_cast(sender()); theAccount = qobject_cast( Choqok::AccountManager::self()->findAccount(act->data().toString())); } QPointer listDlg = new TwitterListDialog(theAccount, Choqok::UI::Global::mainWindow()); listDlg->show(); } void TwitterMicroBlog::fetchUserLists(TwitterAccount *theAccount, const QString &username) { qCDebug(CHOQOK); if (!theAccount) { return; } QUrl url = theAccount->apiUrl(); url.setPath(url.path() + QStringLiteral("/lists/ownerships.%1").arg(format)); QUrl url_for_oauth(url);//we need base URL (without params) to make OAuth signature with it! QUrlQuery urlQuery; urlQuery.addQueryItem(QLatin1String("screen_name"), username); url.setQuery(urlQuery); - QOAuth::ParamMap params; - params.insert("screen_name", username.toLatin1()); + QVariantMap params; + params.insert(QLatin1String("screen_name"), username.toLocal8Bit()); KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo) ; if (!job) { qCCritical(CHOQOK) << "TwitterMicroBlog::loadUserLists: Cannot create an http GET request!"; return; } job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + - QLatin1String(authorizationHeader(theAccount, url_for_oauth, QOAuth::GET, params))); + QLatin1String(authorizationHeader(theAccount, url_for_oauth, QNetworkAccessManager::GetOperation, params))); mFetchUsersListMap[ job ] = username; mJobsAccount[ job ] = theAccount; connect(job, SIGNAL(result(KJob*)), this, SLOT(slotFetchUserLists(KJob*))); job->start(); } void TwitterMicroBlog::slotFetchUserLists(KJob *job) { qCDebug(CHOQOK); if (!job) { qCWarning(CHOQOK) << "NULL Job returned"; return; } QString username = mFetchUsersListMap.take(job); Choqok::Account *theAccount = mJobsAccount.take(job); if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); Q_EMIT error(theAccount, Choqok::MicroBlog::CommunicationError, i18n("Fetching %1's lists failed. %2", username, job->errorString()), Critical); } else { KIO::StoredTransferJob *stj = qobject_cast (job); QByteArray buffer = stj->data(); QList list = readUserListsFromJson(theAccount, buffer); if (list.isEmpty()) { qCDebug(CHOQOK) << buffer; QString errorMsg; errorMsg = checkForError(buffer); if (errorMsg.isEmpty()) { KMessageBox::information(choqokMainWindow, i18n("There is no list record for user %1", username)); } else { Q_EMIT error(theAccount, ServerError, errorMsg, Critical); } } else { Q_EMIT userLists(theAccount, username, list); } } } Choqok::Post *TwitterMicroBlog::readDirectMessage(Choqok::Account *theAccount, const QVariantMap &var) { qCDebug(CHOQOK); Choqok::Post *post = TwitterApiMicroBlog::readDirectMessage(theAccount, var); if (!post) { qCCritical(CHOQOK) << "post is NULL!"; return 0; } post->postId = var[QLatin1String("id_str")].toString(); return post; } void TwitterMicroBlog::addListTimeline(TwitterAccount *theAccount, const QString &username, const QString &listname) { qCDebug(CHOQOK); QStringList tms = theAccount->timelineNames(); QString name = QStringLiteral("@%1/%2").arg(username).arg(listname); tms.append(name); addTimelineName(name); theAccount->setTimelineNames(tms); theAccount->writeConfig(); QString url = QLatin1String("/lists/statuses"); timelineApiPath[name] = url + QLatin1String(".%1"); updateTimelines(theAccount); } // TODO: Change to new API void TwitterMicroBlog::setListTimelines(TwitterAccount *theAccount, const QStringList &lists) { qCDebug(CHOQOK) << lists; QStringList tms = theAccount->timelineNames(); for (const QString &name: lists) { tms.append(name); addTimelineName(name); QString url = QLatin1String("/lists/statuses"); timelineApiPath[name] = url + QLatin1String(".%1"); } tms.removeDuplicates(); theAccount->setTimelineNames(tms); } Choqok::TimelineInfo *TwitterMicroBlog::timelineInfo(const QString &timelineName) { if (timelineName.startsWith(QLatin1Char('@'))) { if (mListsInfo.contains(timelineName)) { return mListsInfo.value(timelineName); } else { Choqok::TimelineInfo *info = new Choqok::TimelineInfo; info->description = info->name = timelineName; info->icon = QLatin1String("format-list-unordered"); mListsInfo.insert(timelineName, info); return info; } } else { return TwitterApiMicroBlog::timelineInfo(timelineName); } } QList< Twitter::List > TwitterMicroBlog::readUserListsFromJson(Choqok::Account *theAccount, QByteArray buffer) { QList twitterList; const QJsonDocument json = QJsonDocument::fromJson(buffer); if (!json.isNull()) { const QVariantMap map = json.toVariant().toMap(); if (map.contains(QLatin1String("lists"))) { for (const QVariant &list: map[QLatin1String("lists")].toList()) { twitterList.append(readListFromJsonMap(theAccount, list.toMap())); } } } return twitterList; } Twitter::List TwitterMicroBlog::readListFromJsonMap(Choqok::Account *theAccount, QVariantMap map) { Twitter::List l; l.author = readUser(theAccount, map[QLatin1String("user")].toMap()); l.description = map[QLatin1String("description")].toString(); l.fullname = map[QLatin1String("full_name")].toString(); l.isFollowing = map[QLatin1String("following")].toBool(); l.listId = map[QLatin1String("id")].toString(); l.memberCount = map[QLatin1String("member_count")].toInt(); l.mode = (map[QLatin1String("mode")].toString() == QLatin1String("public") ? Twitter::Public : Twitter::Private); l.name = map[QLatin1String("name")].toString(); l.slug = map[QLatin1String("slug")].toString(); l.subscriberCount = map[QLatin1String("subscriber_count")].toInt(); l.uri = map[QLatin1String("uri")].toString(); return l; } Choqok::Post *TwitterMicroBlog::readPost(Choqok::Account *account, const QVariantMap &var, Choqok::Post *post) { if (!post) { qCCritical(CHOQOK) << "TwitterMicroBlog::readPost: post is NULL!"; return 0; } post = TwitterApiMicroBlog::readPost(account, var, post); post->postId = var[QLatin1String("id_str")].toString(); post->replyToPostId = var[QLatin1String("in_reply_to_status_id_str")].toString(); post->replyToUserId = var[QLatin1String("in_reply_to_user_id_str")].toString(); //postId is changed, regenerate link url post->link = postUrl(account, post->author.userName, post->postId); QVariantMap userMap = var[QLatin1String("user")].toMap(); post->author.userId = userMap[QLatin1String("id_str")].toString(); return post; } #include "twittermicroblog.moc" diff --git a/microblogs/twitter/twittersearch.cpp b/microblogs/twitter/twittersearch.cpp index d3939128..a191da8f 100644 --- a/microblogs/twitter/twittersearch.cpp +++ b/microblogs/twitter/twittersearch.cpp @@ -1,201 +1,199 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2008-2012 Mehrdad Momeny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #include "twittersearch.h" #include #include #include -#include - #include "twitterapimicroblog.h" #include "twitteraccount.h" #include "twitterdebug.h" const QRegExp TwitterSearch::m_rId(QLatin1String("tag:search.twitter.com,[0-9]+:([0-9]+)")); TwitterSearch::TwitterSearch(QObject *parent): TwitterApiSearch(parent) { qCDebug(CHOQOK); mSearchCode[CustomSearch].clear(); mSearchCode[ToUser] = QLatin1String("to:"); mSearchCode[FromUser] = QLatin1String("from:"); mSearchCode[ReferenceUser] = QLatin1Char('@'); mSearchCode[ReferenceHashtag] = QLatin1Char('#'); mI18nSearchCode[CustomSearch].clear(); mI18nSearchCode[ReferenceUser] = QLatin1Char('@'); mI18nSearchCode[ReferenceHashtag] = QLatin1Char('#'); mI18nSearchCode[ToUser] = i18nc("Posts sent to user", "To:"); mI18nSearchCode[FromUser] = i18nc("Posts from user, Sent by user", "From:"); mSearchTypes[CustomSearch].first = i18n("Custom Search"); mSearchTypes[CustomSearch].second = true; mSearchTypes[ToUser].first = i18nc("Tweets are Twitter posts", "Tweets To This User"); mSearchTypes[ToUser].second = true; mSearchTypes[FromUser].first = i18nc("Tweets are Twitter posts", "Tweets From This User"); mSearchTypes[FromUser].second = true; mSearchTypes[ReferenceUser].first = i18nc("Tweets are Twitter posts", "Tweets Including This Username"); mSearchTypes[ReferenceUser].second = true; mSearchTypes[ReferenceHashtag].first = i18nc("Tweets are Twitter posts", "Tweets Including This Hashtag"); mSearchTypes[ReferenceHashtag].second = true; } void TwitterSearch::requestSearchResults(const SearchInfo &searchInfo, const QString &sinceStatusId, uint count, uint page) { Q_UNUSED(page) qCDebug(CHOQOK); TwitterAccount *account = qobject_cast< TwitterAccount * >(searchInfo.account); QUrl url = account->apiUrl(); QUrlQuery urlQuery; - QOAuth::ParamMap param; + QVariantMap param; const QString query = searchInfo.query; if (searchInfo.option == TwitterSearch::FromUser) { url.setPath(url.path() + QLatin1String("/statuses/user_timeline.json")); urlQuery.addQueryItem(QLatin1String("screen_name"), query); - param.insert("screen_name", query.toLatin1()); + param.insert(QLatin1String("screen_name"), query.toLocal8Bit()); } else { url.setPath(url.path() + QLatin1String("/search/tweets.json")); const QByteArray formattedQuery(QUrl::toPercentEncoding(mSearchCode[searchInfo.option] + query)); urlQuery.addQueryItem(QLatin1String("q"), QString::fromLatin1(formattedQuery)); - param.insert("q", formattedQuery); + param.insert(QLatin1String("q"), formattedQuery); } if (!sinceStatusId.isEmpty()) { urlQuery.addQueryItem(QLatin1String("since_id"), sinceStatusId); - param.insert("since_id", sinceStatusId.toLatin1()); + param.insert(QLatin1String("since_id"), sinceStatusId.toLocal8Bit()); } int cntStr; if (count && count <= 100) { // Twitter API specifies a max count of 100 cntStr = count; } else { cntStr = 100; } urlQuery.addQueryItem(QLatin1String("count"), QString::number(cntStr)); - param.insert("count", QString::number(cntStr).toLatin1()); + param.insert(QLatin1String("count"), QString::number(cntStr).toLocal8Bit()); const QUrl tmpUrl(url); url.setQuery(urlQuery); qCDebug(CHOQOK) << url; KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo); if (!job) { qCCritical(CHOQOK) << "Cannot create an http GET request!"; return; } TwitterApiMicroBlog *microblog = qobject_cast(account->microblog()); job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + - QLatin1String(microblog->authorizationHeader(account, tmpUrl, QOAuth::GET, param))); + QLatin1String(microblog->authorizationHeader(account, tmpUrl, QNetworkAccessManager::GetOperation, param))); mSearchJobs[job] = searchInfo; connect(job, SIGNAL(result(KJob*)), this, SLOT(searchResultsReturned(KJob*))); job->start(); } void TwitterSearch::searchResultsReturned(KJob *job) { qCDebug(CHOQOK); if (!job) { qCDebug(CHOQOK) << "job is a null pointer"; Q_EMIT error(i18n("Unable to fetch search results.")); return; } const SearchInfo info = mSearchJobs.take(job); QList postsList; if (job->error()) { qCCritical(CHOQOK) << "Error:" << job->errorString(); Q_EMIT error(i18n("Unable to fetch search results: %1", job->errorString())); } else { KIO::StoredTransferJob *jj = qobject_cast(job); const QJsonDocument json = QJsonDocument::fromJson(jj->data()); if (!json.isNull()) { if (info.option == TwitterSearch::FromUser) { for (const QVariant elem: json.toVariant().toList()) { postsList.prepend(readStatusesFromJsonMap(elem.toMap())); } } else { const QVariantMap map = json.toVariant().toMap(); if (map.contains(QLatin1String("statuses"))) { for (const QVariant elem: map[QLatin1String("statuses")].toList()) { postsList.prepend(readStatusesFromJsonMap(elem.toMap())); } } } } } Q_EMIT searchResultsReceived(info, postsList); } Choqok::Post *TwitterSearch::readStatusesFromJsonMap(const QVariantMap &var) { Choqok::Post *post = new Choqok::Post; post->content = var[QLatin1String("text")].toString(); //%*s %s %d %d:%d:%d %d %d post->creationDateTime = dateFromString(var[QLatin1String("created_at")].toString()); post->postId = var[QLatin1String("id")].toString(); post->source = var[QLatin1String("source")].toString(); QVariantMap userMap = var[QLatin1String("user")].toMap(); post->author.realName = userMap[QLatin1String("name")].toString(); post->author.userName = userMap[QLatin1String("screen_name")].toString(); post->author.profileImageUrl = userMap[QLatin1String("profile_image_url")].toString(); post->isPrivate = false; post->isFavorited = false; post->replyToPostId = var[QLatin1String("in_reply_to_status_id_str")].toString(); post->replyToUserName = var[QLatin1String("in_reply_to_screen_name")].toString(); post->link = QStringLiteral("https://twitter.com/%1/status/%2").arg(post->author.userName).arg(post->postId); return post; } QString TwitterSearch::optionCode(int option) { return mI18nSearchCode[option]; } TwitterSearch::~TwitterSearch() { } diff --git a/microblogs/twitter/twittertextedit.cpp b/microblogs/twitter/twittertextedit.cpp index ae3b67e2..e83bc61f 100644 --- a/microblogs/twitter/twittertextedit.cpp +++ b/microblogs/twitter/twittertextedit.cpp @@ -1,149 +1,149 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2017 Andrea Scarpino This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #include "twittertextedit.h" #include #include #include #include "urlutils.h" #include "twitterapiaccount.h" #include "twitterapimicroblog.h" #include "twitterdebug.h" class TwitterTextEdit::Private { public: Private(Choqok::Account *theAccount) : acc(theAccount), tCoMaximumLength(0), tCoMaximumLengthHttps(0) {} Choqok::Account *acc; int tCoMaximumLength; int tCoMaximumLengthHttps; }; TwitterTextEdit::TwitterTextEdit(Choqok::Account *theAccount, QWidget *parent) : TwitterApiTextEdit(theAccount, parent), d(new Private(theAccount)) { qCDebug(CHOQOK); fetchTCoMaximumLength(); } TwitterTextEdit::~TwitterTextEdit() { delete d; } -void TwitterApiTextEdit::updateRemainingCharsCount() +void TwitterTextEdit::updateRemainingCharsCount() { QString txt = this->toPlainText(); int count = txt.count(); if (count) { lblRemainChar->show(); if (charLimit()) { int remain = charLimit() - count; for (const QString &url: UrlUtils::detectUrls(txt)) { // Twitter does not wrapps urls with login informations if (!url.contains(QLatin1Char('@'))) { int diff = -1; if (url.startsWith(QLatin1String("http://"))) { diff = url.length() - d->tCoMaximumLength; } else if (url.startsWith(QLatin1String("https://"))) { diff = url.length() - d->tCoMaximumLengthHttps; } if (diff > 0) { remain += diff; } } } if (remain < 0) { lblRemainChar->setStyleSheet(QLatin1String("QLabel {color: red;}")); } else if (remain < 30) { lblRemainChar->setStyleSheet(QLatin1String("QLabel {color: rgb(242, 179, 19);}")); } else { lblRemainChar->setStyleSheet(QLatin1String("QLabel {color: green;}")); } lblRemainChar->setText(QString::number(remain)); } else { lblRemainChar->setText(QString::number(count)); lblRemainChar->setStyleSheet(QLatin1String(QLatin1String("QLabel {color: blue;}"))); } txt.remove(QRegExp(QLatin1String("@([^\\s\\W]+)"))); txt = txt.trimmed(); if (firstChar() != txt[0]) { setFirstChar(txt[0]); txt.prepend(QLatin1Char(' ')); QTextBlockFormat f; f.setLayoutDirection((Qt::LayoutDirection) txt.isRightToLeft()); textCursor().mergeBlockFormat(f); } } else { lblRemainChar->hide(); } } -void TwitterApiTextEdit::fetchTCoMaximumLength() +void TwitterTextEdit::fetchTCoMaximumLength() { TwitterApiAccount *acc = qobject_cast(d->acc); if (acc) { QUrl url = acc->apiUrl(); url.setPath(url.path() + QLatin1String("/help/configuration.json")); KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo); if (!job) { qCDebug(CHOQOK) << "Cannot create an http GET request!"; return; } TwitterApiMicroBlog *mBlog = qobject_cast(acc->microblog()); job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("Authorization: ") + - QLatin1String(mBlog->authorizationHeader(acc, url, QOAuth::GET))); + QLatin1String(mBlog->authorizationHeader(acc, url, QNetworkAccessManager::GetOperation))); connect(job, SIGNAL(result(KJob*)), this, SLOT(slotTCoMaximumLength(KJob*))); job->start(); } else { qCDebug(CHOQOK) << "the account is not a TwitterAPIAccount!"; } } -void TwitterApiTextEdit::slotTCoMaximumLength(KJob *job) +void TwitterTextEdit::slotTCoMaximumLength(KJob *job) { if (job->error()) { qCDebug(CHOQOK) << "Job Error:" << job->errorString(); } else { KIO::StoredTransferJob *j = qobject_cast(job); const QJsonDocument json = QJsonDocument::fromJson(j->data()); if (!json.isNull()) { const QVariantMap reply = json.toVariant().toMap(); d->tCoMaximumLength = reply[QLatin1String("short_url_length")].toInt(); d->tCoMaximumLengthHttps = reply[QLatin1String("short_url_length_https")].toInt(); } else { qCDebug(CHOQOK) << "Cannot parse JSON reply"; } } } diff --git a/plugins/uploaders/mobypicture/CMakeLists.txt b/plugins/uploaders/mobypicture/CMakeLists.txt index 2834e70b..8b08f600 100644 --- a/plugins/uploaders/mobypicture/CMakeLists.txt +++ b/plugins/uploaders/mobypicture/CMakeLists.txt @@ -1,55 +1,55 @@ include_directories( ${CHOQOK_INCLUDES} ) set(choqok_mobypicture_PART_SRCS mobypicture.cpp ) kconfig_add_kcfg_files(choqok_mobypicture_PART_SRCS mobypicturesettings.kcfgc) add_library(choqok_mobypicture MODULE ${choqok_mobypicture_PART_SRCS}) kcoreaddons_desktop_to_json(choqok_mobypicture choqok_mobypicture.desktop) target_link_libraries(choqok_mobypicture PUBLIC Qt5::Core + Qt5::NetworkAuth KF5::CoreAddons KF5::KIOCore - ${QTOAUTH_LIBRARY} choqok twitterapihelper ) install(TARGETS choqok_mobypicture DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES choqok_mobypicture.desktop DESTINATION ${SERVICES_INSTALL_DIR}) set(kcm_choqok_mobypicture_PART_SRCS mobypictureconfig.cpp ) ki18n_wrap_ui(kcm_choqok_mobypicture_PART_SRCS mobypictureprefs.ui) kconfig_add_kcfg_files(kcm_choqok_mobypicture_PART_SRCS mobypicturesettings.kcfgc) add_library(kcm_choqok_mobypicture MODULE ${kcm_choqok_mobypicture_PART_SRCS}) kcoreaddons_desktop_to_json(kcm_choqok_mobypicture choqok_mobypicture_config.desktop) target_link_libraries(kcm_choqok_mobypicture PUBLIC Qt5::Widgets KF5::ConfigWidgets KF5::CoreAddons KF5::I18n KF5::KIOCore KF5::WidgetsAddons choqok ) install(TARGETS kcm_choqok_mobypicture DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES mobypicturesettings.kcfg DESTINATION ${KCFG_INSTALL_DIR}) install(FILES choqok_mobypicture_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}) add_subdirectory(icons) diff --git a/plugins/uploaders/mobypicture/mobypicture.cpp b/plugins/uploaders/mobypicture/mobypicture.cpp index d4533501..934e9a1c 100644 --- a/plugins/uploaders/mobypicture/mobypicture.cpp +++ b/plugins/uploaders/mobypicture/mobypicture.cpp @@ -1,174 +1,167 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2010-2012 Andrey Esin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #include "mobypicture.h" #include #include #include #include -#include - #include "accountmanager.h" #include "mediamanager.h" #include "passwordmanager.h" #include "twitterapiaccount.h" #include "twitterapimicroblog.h" #include "mobypicturesettings.h" K_PLUGIN_FACTORY_WITH_JSON(MobypictureFactory, "choqok_mobypicture.json", registerPlugin < Mobypicture > ();) Mobypicture::Mobypicture(QObject *parent, const QList &) : Choqok::Uploader(QLatin1String("choqok_mobypicture"), parent) { } Mobypicture::~Mobypicture() { } void Mobypicture::upload(const QUrl &localUrl, const QByteArray &medium, const QByteArray &mediumType) { MobypictureSettings::self()->load(); KIO::StoredTransferJob *job = 0; QByteArray apiKey = "85LUKv3w6luUF6Pa"; if (MobypictureSettings::oauth()) { QString alias = MobypictureSettings::alias(); if (alias.isEmpty()) { qCritical() << "No account to use"; Q_EMIT uploadingFailed(localUrl, i18n("There is no Twitter account configured to use.")); return; } TwitterApiAccount *acc = qobject_cast (Choqok::AccountManager::self()->findAccount(alias)); if (!acc) { return; } QUrl url(QLatin1String("https://api.mobypicture.com/2.0/upload")); QMap formdata; formdata[QLatin1String("key")] = apiKey; formdata[QLatin1String("message")] = QString().toUtf8(); QMap mediafile; mediafile[QLatin1String("name")] = "media"; mediafile[QLatin1String("filename")] = localUrl.fileName().toUtf8(); mediafile[QLatin1String("mediumType")] = mediumType; mediafile[QLatin1String("medium")] = medium; QList< QMap > listMediafiles; listMediafiles.append(mediafile); QByteArray data = Choqok::MediaManager::createMultipartFormData(formdata, listMediafiles); - job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo) ; - QOAuth::ParamMap params; - QString requrl = QLatin1String("https://api.twitter.com/1/account/verify_credentials.json"); - QByteArray credentials = acc->oauthInterface()->createParametersString(requrl, - QOAuth::GET, acc->oauthToken(), - acc->oauthTokenSecret(), - QOAuth::HMAC_SHA1, - params, QOAuth::ParseForHeaderArguments); + job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo); + QUrl requrl(QLatin1String("https://api.twitter.com/1/account/verify_credentials.json")); + QByteArray credentials = acc->oauthInterface()->authorizationHeader(requrl, QNetworkAccessManager::GetOperation); QString cHeader = QLatin1String("X-Verify-Credentials-Authorization: ") + QLatin1String(credentials) + QLatin1String("\r\n"); cHeader.append(QLatin1String("X-Auth-Service-Provider: https://api.twitter.com/1/account/verify_credentials.json")); job->addMetaData(QLatin1String("customHTTPHeader"), cHeader); } else if (MobypictureSettings::basic()) { QUrl url(QLatin1String("https://api.mobypicture.com")); QString login = MobypictureSettings::login(); QString pass = Choqok::PasswordManager::self()->readPassword(QStringLiteral("mobypicture_%1") .arg(MobypictureSettings::login())); QMap formdata; formdata[QLatin1String("k")] = apiKey; formdata[QLatin1String("u")] = login.toUtf8(); formdata[QLatin1String("p")] = pass.toUtf8(); formdata[QLatin1String("s")] = "none"; formdata[QLatin1String("format")] = "json"; QMap mediafile; mediafile[QLatin1String("name")] = "i"; mediafile[QLatin1String("filename")] = localUrl.fileName().toUtf8(); mediafile[QLatin1String("mediumType")] = mediumType; mediafile[QLatin1String("medium")] = medium; QList< QMap > listMediafiles; listMediafiles.append(mediafile); QByteArray data = Choqok::MediaManager::createMultipartFormData(formdata, listMediafiles); job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo) ; job->addMetaData(QLatin1String("Authorization"), QLatin1String("Basic ") + QLatin1String(QStringLiteral("%1:%2").arg(login).arg(pass).toUtf8().toBase64())); } if (!job) { qCritical() << "Cannot create a http POST request!"; return; } job->addMetaData(QLatin1String("content-type"), QLatin1String("Content-Type: multipart/form-data; boundary=AaB03x")); mUrlMap[job] = localUrl; connect(job, SIGNAL(result(KJob*)), SLOT(slotUpload(KJob*))); job->start(); } void Mobypicture::slotUpload(KJob *job) { QUrl localUrl = mUrlMap.take(job); if (job->error()) { qCritical() << "Job Error:" << job->errorString(); Q_EMIT uploadingFailed(localUrl, job->errorString()); return; } else { KIO::StoredTransferJob *stj = qobject_cast(job); //qDebug() << stj->data(); const QJsonDocument json = QJsonDocument::fromJson(stj->data()); if (!json.isNull()) { const QVariantMap map = json.toVariant().toMap(); if (MobypictureSettings::oauth()) { if (map.contains(QLatin1String("errors"))) { QVariantMap err = map.value(QLatin1String("errors")).toMap(); Q_EMIT uploadingFailed(localUrl, err.value(QLatin1String("message")).toString()); } else if (map.contains(QLatin1String("media"))) { QVariantMap media = map.value(QLatin1String("media")).toMap(); Q_EMIT mediumUploaded(localUrl, media.value(QLatin1String("mediaurl")).toString()); } } if (MobypictureSettings::basic()) { if (map.value(QLatin1String("result")) == QLatin1String("0") && map.contains(QLatin1String("url"))) { Q_EMIT mediumUploaded(localUrl, map.value(QLatin1String("url")).toString()); } else { Q_EMIT uploadingFailed(localUrl, map.value(QLatin1String("message")).toString()); } } } else { Q_EMIT uploadingFailed(localUrl, i18n("Malformed response")); qWarning() << "Parse error:" << stj->data(); } } } #include "mobypicture.moc" diff --git a/plugins/uploaders/posterous/CMakeLists.txt b/plugins/uploaders/posterous/CMakeLists.txt index 418e8af8..257dd343 100644 --- a/plugins/uploaders/posterous/CMakeLists.txt +++ b/plugins/uploaders/posterous/CMakeLists.txt @@ -1,54 +1,54 @@ include_directories( ${CHOQOK_INCLUDES} ) set(choqok_posterous_PART_SRCS posterous.cpp ) kconfig_add_kcfg_files(choqok_posterous_PART_SRCS posteroussettings.kcfgc) add_library(choqok_posterous MODULE ${choqok_posterous_PART_SRCS}) kcoreaddons_desktop_to_json(choqok_posterous choqok_posterous.desktop) target_link_libraries(choqok_posterous PUBLIC Qt5::Core + Qt5::NetworkAuth KF5::CoreAddons KF5::KIOCore - ${QTOAUTH_LIBRARY} choqok twitterapihelper ) install(TARGETS choqok_posterous DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES choqok_posterous.desktop DESTINATION ${SERVICES_INSTALL_DIR}) set(kcm_choqok_posterous_PART_SRCS posterousconfig.cpp ) ki18n_wrap_ui(kcm_choqok_posterous_PART_SRCS posterousprefs.ui) kconfig_add_kcfg_files(kcm_choqok_posterous_PART_SRCS posteroussettings.kcfgc) add_library(kcm_choqok_posterous MODULE ${kcm_choqok_posterous_PART_SRCS}) kcoreaddons_desktop_to_json(kcm_choqok_posterous choqok_posterous_config.desktop) target_link_libraries(kcm_choqok_posterous PUBLIC Qt5::Widgets KF5::ConfigWidgets KF5::CoreAddons KF5::I18n KF5::WidgetsAddons choqok ) install(TARGETS kcm_choqok_posterous DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES posteroussettings.kcfg DESTINATION ${KCFG_INSTALL_DIR}) install(FILES choqok_posterous_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}) add_subdirectory(icons) diff --git a/plugins/uploaders/posterous/posterous.cpp b/plugins/uploaders/posterous/posterous.cpp index 890fc200..ac02c5df 100644 --- a/plugins/uploaders/posterous/posterous.cpp +++ b/plugins/uploaders/posterous/posterous.cpp @@ -1,198 +1,191 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2010-2012 Andrey Esin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #include "posterous.h" #include #include #include #include -#include - #include "accountmanager.h" #include "mediamanager.h" #include "passwordmanager.h" #include "twitterapiaccount.h" #include "twitterapimicroblog.h" #include "posteroussettings.h" K_PLUGIN_FACTORY_WITH_JSON(PosterousFactory, "choqok_posterous.json", registerPlugin < Posterous > ();) Posterous::Posterous(QObject *parent, const QList &) : Choqok::Uploader(QLatin1String("choqok_posterous"), parent) { } Posterous::~Posterous() { } QString Posterous::getAuthToken(const QUrl &localUrl) { QUrl url(QLatin1String("http://posterous.com/api/2/auth/token")); QString login = PosterousSettings::login(); QString pass = Choqok::PasswordManager::self()->readPassword(QStringLiteral("posterous_%1").arg(PosterousSettings::login())); KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo); job->addMetaData(QLatin1String("customHTTPHeader"), QLatin1String("Authorization: Basic ") + QLatin1String(QStringLiteral("%1:%2").arg(login).arg(pass).toUtf8().toBase64())); job->exec(); if (!job->error()) { const QByteArray data = job->data(); const QJsonDocument json = QJsonDocument::fromJson(data); if (!json.isNull()) { QVariantMap map = json.toVariant().toMap(); if (map.contains(QLatin1String("api_token"))) { QString tkn = map.value(QLatin1String("api_token")).toString(); return tkn; } else { Q_EMIT uploadingFailed(localUrl, map.value(QLatin1String("error")).toString()); qWarning() << "Parse error:" << data; } } } else { qCritical() << "Job error:" << job->errorString(); } return QString(); } void Posterous::upload(const QUrl &localUrl, const QByteArray &medium, const QByteArray &mediumType) { PosterousSettings::self()->load(); KIO::StoredTransferJob *job = 0; if (PosterousSettings::basic()) { QString login = PosterousSettings::login(); QString pass = Choqok::PasswordManager::self()->readPassword(QStringLiteral("posterous_%1").arg(PosterousSettings::login())); QString token = getAuthToken(localUrl); if (!token.isEmpty()) { QUrl url(QLatin1String("http://posterous.com/api/2/users/me/sites/primary/posts")); QMap formdata; formdata[QLatin1String("post[title]")] = QByteArray(); formdata[QLatin1String("post[body]")] = QByteArray(); formdata[QLatin1String("autopost")] = "0"; formdata[QLatin1String("source")] = "Choqok"; formdata[QLatin1String("api_token")] = token.toUtf8(); QMap mediafile; mediafile[QLatin1String("name")] = "media"; mediafile[QLatin1String("filename")] = localUrl.fileName().toUtf8(); mediafile[QLatin1String("mediumType")] = mediumType; mediafile[QLatin1String("medium")] = medium; QList< QMap > listMediafiles; listMediafiles.append(mediafile); QByteArray data = Choqok::MediaManager::createMultipartFormData(formdata, listMediafiles); job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo); job->addMetaData(QLatin1String("customHTTPHeader"), QLatin1String("Authorization: Basic ") + QLatin1String(QStringLiteral("%1:%2").arg(login).arg(pass).toUtf8().toBase64())); } } else if (PosterousSettings::oauth()) { QString alias = PosterousSettings::alias(); if (alias.isEmpty()) { qCritical() << "No account to use"; Q_EMIT uploadingFailed(localUrl, i18n("There is no Twitter account configured to use.")); return; } TwitterApiAccount *acc = qobject_cast (Choqok::AccountManager::self()->findAccount(alias)); if (!acc) { return; } QUrl url(QLatin1String("http://posterous.com/api2/upload.json")); QMap formdata; formdata[QLatin1String("source")] = "Choqok"; formdata[QLatin1String("sourceLink")] = "http://choqok.gnufolks.org/"; QMap mediafile; mediafile[QLatin1String("name")] = "media"; mediafile[QLatin1String("filename")] = localUrl.fileName().toUtf8(); mediafile[QLatin1String("mediumType")] = mediumType; mediafile[QLatin1String("medium")] = medium; QList< QMap > listMediafiles; listMediafiles.append(mediafile); QByteArray data = Choqok::MediaManager::createMultipartFormData(formdata, listMediafiles); - KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo) ; - QOAuth::ParamMap params; - QString requrl = QLatin1String("https://api.twitter.com/1/account/verify_credentials.json"); - QByteArray credentials = acc->oauthInterface()->createParametersString(requrl, - QOAuth::GET, acc->oauthToken(), - acc->oauthTokenSecret(), - QOAuth::HMAC_SHA1, - params, QOAuth::ParseForHeaderArguments); + KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo); + QUrl requrl(QLatin1String("https://api.twitter.com/1/account/verify_credentials.json")); + QByteArray credentials = acc->oauthInterface()->authorizationHeader(requrl, QNetworkAccessManager::GetOperation); QString cHeader = QLatin1String("X-Verify-Credentials-Authorization: ") + QLatin1String(credentials) + QLatin1String("\r\n"); cHeader.append(QLatin1String("X-Auth-Service-Provider: https://api.twitter.com/1/account/verify_credentials.json")); job->addMetaData(QLatin1String("customHTTPHeader"), cHeader); } if (!job) { qCritical() << "Cannot create a http POST request!"; return; } job->addMetaData(QStringLiteral("content-type"), QStringLiteral("Content-Type: multipart/form-data; boundary=AaB03x")); mUrlMap[job] = localUrl; connect(job, SIGNAL(result(KJob*)), SLOT(slotUpload(KJob*))); job->start(); } void Posterous::slotUpload(KJob *job) { QUrl localUrl = mUrlMap.take(job); if (job->error()) { qCritical() << "Job Error:" << job->errorString(); Q_EMIT uploadingFailed(localUrl, job->errorString()); return; } else { KIO::StoredTransferJob *stj = qobject_cast(job); //qDebug() << stj->data(); const QJsonDocument json = QJsonDocument::fromJson(stj->data()); if (!json.isNull()) { const QVariantMap map = json.toVariant().toMap(); if (map.contains(QLatin1String("error"))) { Q_EMIT uploadingFailed(localUrl, map.value(QLatin1String("error")).toString()); } else { if (PosterousSettings::oauth()) { Q_EMIT mediumUploaded(localUrl, map.value(QLatin1String("url")).toString()); } if (PosterousSettings::basic()) { Q_EMIT mediumUploaded(localUrl, map.value(QLatin1String("full_url")).toString()); } } } else { Q_EMIT uploadingFailed(localUrl, i18n("Malformed response")); qWarning() << "Parse error:" << stj->data(); } } } #include "posterous.moc" diff --git a/plugins/uploaders/twitgoo/CMakeLists.txt b/plugins/uploaders/twitgoo/CMakeLists.txt index 6ba46786..6e0cf007 100644 --- a/plugins/uploaders/twitgoo/CMakeLists.txt +++ b/plugins/uploaders/twitgoo/CMakeLists.txt @@ -1,53 +1,53 @@ include_directories( ${CHOQOK_INCLUDES} ) set(choqok_twitgoo_PART_SRCS twitgoo.cpp ) kconfig_add_kcfg_files(choqok_twitgoo_PART_SRCS twitgoosettings.kcfgc) add_library(choqok_twitgoo MODULE ${choqok_twitgoo_PART_SRCS}) kcoreaddons_desktop_to_json(choqok_twitgoo choqok_twitgoo.desktop) target_link_libraries(choqok_twitgoo PUBLIC Qt5::Core + Qt5::NetworkAuth KF5::CoreAddons KF5::KIOCore - ${QTOAUTH_LIBRARY} choqok twitterapihelper ) install(TARGETS choqok_twitgoo DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES choqok_twitgoo.desktop DESTINATION ${SERVICES_INSTALL_DIR}) set(kcm_choqok_twitgoo_PART_SRCS twitgooconfig.cpp ) ki18n_wrap_ui(kcm_choqok_twitgoo_PART_SRCS twitgooprefs.ui) kconfig_add_kcfg_files(kcm_choqok_twitgoo_PART_SRCS twitgoosettings.kcfgc) add_library(kcm_choqok_twitgoo MODULE ${kcm_choqok_twitgoo_PART_SRCS}) kcoreaddons_desktop_to_json(kcm_choqok_twitgoo choqok_twitgoo_config.desktop) target_link_libraries(kcm_choqok_twitgoo PUBLIC Qt5::Widgets KF5::CoreAddons KF5::I18n KF5::WidgetsAddons choqok ) install(TARGETS kcm_choqok_twitgoo DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES twitgoosettings.kcfg DESTINATION ${KCFG_INSTALL_DIR}) install(FILES choqok_twitgoo_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}) add_subdirectory(icons) diff --git a/plugins/uploaders/twitgoo/twitgoo.cpp b/plugins/uploaders/twitgoo/twitgoo.cpp index 158fe810..97aa7178 100644 --- a/plugins/uploaders/twitgoo/twitgoo.cpp +++ b/plugins/uploaders/twitgoo/twitgoo.cpp @@ -1,137 +1,130 @@ /* This file is part of Choqok, the KDE micro-blogging client Copyright (C) 2010-2012 Andrey Esin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses/ */ #include "twitgoo.h" #include #include #include #include #include "accountmanager.h" #include "mediamanager.h" #include "passwordmanager.h" #include "twitterapiaccount.h" #include "twitterapimicroblog.h" -#include - #include "twitgoosettings.h" K_PLUGIN_FACTORY_WITH_JSON(TwitgooFactory, "choqok_twitgoo.json", registerPlugin < Twitgoo > ();) Twitgoo::Twitgoo(QObject *parent, const QList &) : Choqok::Uploader(QLatin1String("choqok_twitgoo"), parent) { } Twitgoo::~Twitgoo() { } void Twitgoo::upload(const QUrl &localUrl, const QByteArray &medium, const QByteArray &mediumType) { TwitgooSettings::self()->load(); QString alias = TwitgooSettings::alias(); if (alias.isEmpty()) { qCritical() << "No account to use"; Q_EMIT uploadingFailed(localUrl, i18n("There is no Twitter account configured to use.")); return; } TwitterApiAccount *acc = qobject_cast (Choqok::AccountManager::self()->findAccount(alias)); if (!acc) { return; } QUrl url(QLatin1String("http://twitgoo.com/api/upload")); QMap formdata; formdata[QLatin1String("source")] = "Choqok"; formdata[QLatin1String("format")] = "json"; QMap mediafile; mediafile[QLatin1String("name")] = "media"; mediafile[QLatin1String("filename")] = localUrl.fileName().toUtf8(); mediafile[QLatin1String("mediumType")] = mediumType; mediafile[QLatin1String("medium")] = medium; QList< QMap > listMediafiles; listMediafiles.append(mediafile); QByteArray data = Choqok::MediaManager::createMultipartFormData(formdata, listMediafiles); KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo) ; job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("X-Auth-Service-Provider: https://api.twitter.com/1/account/verify_credentials.json")); - QOAuth::ParamMap params; - QString requrl = QLatin1String("https://api.twitter.com/1/account/verify_credentials.json"); - QByteArray credentials = acc->oauthInterface()->createParametersString(requrl, - QOAuth::GET, acc->oauthToken(), - acc->oauthTokenSecret(), - QOAuth::HMAC_SHA1, - params, QOAuth::ParseForHeaderArguments); + QUrl requrl(QLatin1String("https://api.twitter.com/1/account/verify_credentials.json")); + QByteArray credentials = acc->oauthInterface()->authorizationHeader(requrl, QNetworkAccessManager::GetOperation); job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("X-Verify-Credentials-Authorization: ") + QLatin1String(credentials)); if (!job) { qCritical() << "Cannot create a http POST request!"; return; } job->addMetaData(QStringLiteral("content-type"), QStringLiteral("Content-Type: multipart/form-data; boundary=AaB03x")); mUrlMap[job] = localUrl; connect(job, SIGNAL(result(KJob*)), SLOT(slotUpload(KJob*))); job->start(); } void Twitgoo::slotUpload(KJob *job) { QUrl localUrl = mUrlMap.take(job); if (job->error()) { qCritical() << "Job Error:" << job->errorString(); Q_EMIT uploadingFailed(localUrl, job->errorString()); return; } else { KIO::StoredTransferJob *stj = qobject_cast(job); //qDebug() << stj->data(); const QJsonDocument json = QJsonDocument::fromJson(stj->data()); if (!json.isNull()) { QVariantMap map = json.toVariant().toMap(); if (map.value(QLatin1String("status")) == QLatin1String("fail")) { QVariantMap err = map.value(QLatin1String("err")).toMap(); Q_EMIT uploadingFailed(localUrl, err.value(QLatin1String("err_msg")).toString()); } else if (map.value(QLatin1String("status")) == QLatin1String("ok")) { TwitgooSettings::self()->load(); QString val = TwitgooSettings::directLink() ? QLatin1String("imageurl") : QLatin1String("mediaurl"); Q_EMIT mediumUploaded(localUrl, map.value(val).toString()); } } else { Q_EMIT uploadingFailed(localUrl, i18n("Malformed response")); qWarning() << "Parse error:" << stj->data(); } } } -#include "twitgoo.moc" \ No newline at end of file +#include "twitgoo.moc" diff --git a/plugins/uploaders/twitpic/CMakeLists.txt b/plugins/uploaders/twitpic/CMakeLists.txt index 919c60ca..9f8e9de8 100644 --- a/plugins/uploaders/twitpic/CMakeLists.txt +++ b/plugins/uploaders/twitpic/CMakeLists.txt @@ -1,50 +1,50 @@ include_directories( ${CHOQOK_INCLUDES} ) set(choqok_twitpic_PART_SRCS twitpic.cpp ) # ki18n_wrap_ui(choqok_twitpic_PART_SRCS twitpicuploadimage_base.ui) kconfig_add_kcfg_files(choqok_twitpic_PART_SRCS twitpicsettings.kcfgc) add_library(choqok_twitpic MODULE ${choqok_twitpic_PART_SRCS}) kcoreaddons_desktop_to_json(choqok_twitpic choqok_twitpic.desktop) target_link_libraries(choqok_twitpic PUBLIC Qt5::Core + Qt5::NetworkAuth KF5::CoreAddons KF5::I18n KF5::KIOCore - ${QTOAUTH_LIBRARY} choqok twitterapihelper ) install(TARGETS choqok_twitpic DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES choqok_twitpic.desktop DESTINATION ${SERVICES_INSTALL_DIR}) set(kcm_choqok_twitpic_PART_SRCS twitpicconfig.cpp ) ki18n_wrap_ui(kcm_choqok_twitpic_PART_SRCS twitpicprefs.ui) kconfig_add_kcfg_files(kcm_choqok_twitpic_PART_SRCS twitpicsettings.kcfgc ) add_library(kcm_choqok_twitpic MODULE ${kcm_choqok_twitpic_PART_SRCS}) kcoreaddons_desktop_to_json(kcm_choqok_twitpic choqok_twitpic_config.desktop) target_link_libraries(kcm_choqok_twitpic choqok) install(TARGETS kcm_choqok_twitpic DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES twitpicsettings.kcfg DESTINATION ${KCFG_INSTALL_DIR}) install(FILES choqok_twitpic_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}) # install( FILES twitpicui.rc DESTINATION ${DATA_INSTALL_DIR}/choqok_twitpic ) -add_subdirectory(icons) \ No newline at end of file +add_subdirectory(icons)