source: SMSSender/frontend/gui/ui/importcontacts/pages/google_page_config.cpp @ 416:4ada10a14951

Last change on this file since 416:4ada10a14951 was 416:4ada10a14951, checked in by Sämy Zehnder <saemy.zehnder@…>, 6 years ago
  • Fixes static initialization problems.
File size: 6.9 KB
Line 
1/*
2 smssender - A frontend for fast and easy SMS sending over different gateways.
3 Copyright (C) 2007-2014, gorrión. See http://smssender.gorrion.ch
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18#include "google_page_config.h"
19
20#include <boost/bind.hpp>
21#include <qjson/parser.h>
22
23#include <QDesktopServices>
24
25#include "common/network/snetworkhelper.h"
26#include "common/network/snetworkreply.h"
27#include "frontend/gui/ui/exceptionmessagebox.h"
28
29namespace UI {
30namespace GCI {
31
32PageConfig::PageConfig(OAuth2::AccessToken *accessToken,
33                       Persistence::IMainStorage *mainStorage)
34    : QWizardPage()
35    , kRequiredScopes(QSet<QString>() << OAuth2::Scope::Contacts
36                                      << OAuth2::Scope::Plus::Me)
37    , m_accessToken(accessToken)
38
39    , m_credentialStore(new Persistence::OAuth2::CredentialStore(mainStorage))
40    , m_authorizationFlow(new OAuth2::AuthorizationFlow(m_credentialStore.data()))
41{
42        ui.setupUi(this);
43
44    connect(m_authorizationFlow.data(), SIGNAL(accessTokenFetched(const OAuth2::AuthorizationFlow::AccessTokenFetchResult &)),
45            this, SLOT(accessTokenFetched(const OAuth2::AuthorizationFlow::AccessTokenFetchResult &)));
46
47    connect(ui.btn_newConnection, SIGNAL(clicked()),
48            this, SLOT(connectNewAccount()));
49    connect(ui.sel_existing, SIGNAL(currentIndexChanged(int)),
50            this, SLOT(dataChanged()));
51
52    ui.sel_existing->clear();
53    ui.sel_existing->hide();
54}
55
56void PageConfig::initializePage() {
57    // Starts fetching access tokens for the already paired accounts.
58    QStringList pairedAccounts =
59            m_credentialStore->pairedAccounts(kRequiredScopes);
60    foreach(const QString &pairedAccount, pairedAccounts) {
61        m_authorizationFlow->startFetchAccessToken(
62                    pairedAccount, kRequiredScopes,
63                    OAuth2::AuthorizationFlow::UrlToUserCallback());
64    }
65}
66
67void PageConfig::dataChanged() {
68    *m_accessToken =
69            m_pairedAccounts.value(ui.sel_existing->currentIndex()).accessToken;
70
71    emit completeChanged();
72}
73
74bool PageConfig::isComplete() const {
75    return ui.sel_existing->currentIndex() >= 0;
76}
77
78void PageConfig::connectNewAccount() {
79    m_authorizationFlow->startFetchAccessToken(
80                "", kRequiredScopes,
81                boost::bind(&PageConfig::showUrlToUser, this, _1));
82}
83
84void PageConfig::showUrlToUser(const QString &url) {
85    bool ok = QDesktopServices::openUrl(url);
86
87    QMessageBox msg(QMessageBox::Question, tr("Action required"), "",
88                    QMessageBox::Ok, this);
89    if (ok) {
90        msg.setText(tr("Please see your browser to grant smssender access to your Google account."));
91    } else {
92        msg.setText(tr("Please open the following link in your browser to grant smssender access to your Google account:\n<a href='%1'>%1</a>").arg(url));
93    }
94    msg.exec();
95}
96
97// We need access to the https://www.googleapis.com/auth/plus.me scope.
98bool PageConfig::fetchAccountInfo(const OAuth2::AccessToken &accessToken,
99                                  QString *name, QIcon *image) {
100    QScopedPointer<SNetworkHelper> http(new SNetworkHelper);
101    http->addTrustedCA(":/certs/Equifax_Secure_Certificate_Authority.crt");
102    http->defaultHeaders().insert("Authorization",
103                                  QString("%1 %2")
104                                      .arg(accessToken.tokenType)
105                                      .arg(accessToken.accessToken)
106                                      .toUtf8());
107    SNetworkReply reply;
108    try {
109        reply = http->syncGet(QString("https://www.googleapis.com/plus/v1/people/me?"
110                                      "fields=displayName,image/url"));
111    } catch (const EException &e) {
112        qWarning() << "Failed to load Google account information: "
113                   << e.chainedWhat();
114        return false;
115    }
116
117    // Parses the reply.
118    // Can be replaced by QJson in Qt5.
119    QJson::Parser parser;
120    bool ok;
121    QByteArray data = reply->readAll();
122    QVariantMap result = parser.parse(data, &ok).toMap();
123    if (!ok) {
124        return false;
125    }
126
127    // Reads out the account data.
128    *name = result["displayName"].toString();
129    QString imageUrl = result["image"].toMap()["url"].toString();
130    if (name->isEmpty() || imageUrl.isEmpty()) {
131        return false;
132    }
133
134    QByteArray imageData = http->syncGet(imageUrl)->readAll();
135    *image = QPixmap::fromImage(QImage::fromData(imageData));
136
137    return ok;
138}
139
140/**
141 * Gets called if either a connection already existed and we got a refreshed
142 * access token or the user initiated a new connection. In both cases, there
143 * does not yet exist an item in the selection box -> we fetch the required
144 * information from the account and create a new entry.
145 *
146 * @param account
147 * @param fetchResult
148 */
149void PageConfig::accessTokenFetched(
150        const OAuth2::AuthorizationFlow::AccessTokenFetchResult &fetchResult) {
151    try {
152        if (fetchResult.isError) {
153            throw QString("Could not fetch the access token: %2")
154                    .arg(fetchResult.errorMessage);
155        }
156
157        // Checks if the given account is already paired.
158        for (int i = 0; i < m_pairedAccounts.size(); ++i) {
159            Account &account = m_pairedAccounts[i];
160            if (account.id == fetchResult.accountId) {
161                account.accessToken = fetchResult.accessToken;
162                return;
163            }
164        }
165
166        // Creates a new account entry.
167        m_pairedAccounts.append(Account());
168        Account &pairedAccount = m_pairedAccounts.last();
169        pairedAccount.id = fetchResult.accountId;
170        pairedAccount.accessToken = fetchResult.accessToken;
171
172        // Fetches the account email and image.
173        bool ok = fetchAccountInfo(pairedAccount.accessToken, &pairedAccount.name,
174                                   &pairedAccount.image);
175        if (!ok) {
176            throw QString("Errors in the reply from Google.");
177        }
178
179        // Inserts the account into the paired list.
180        ui.sel_existing->addItem(pairedAccount.image, pairedAccount.name);
181        if (ui.sel_existing->currentIndex() < 0) {
182            ui.sel_existing->setCurrentIndex(0);
183        }
184        ui.sel_existing->show();
185    } catch (QString &error) {
186        ExceptionMessageBox msg(
187                    tr("An error occured while connecting to the Google account."),
188                    EException(error), this);
189        msg.exec();
190    }
191}
192
193} // namespace GCI
194} // namespace UI
Note: See TracBrowser for help on using the repository browser.