Added settings load and save methods to Engine and MainWindow.
[situare] / src / facebookservice / facebookauthentication.cpp
1 /*
2    Situare - A location system for Facebook
3    Copyright (C) 2010  Ixonos Plc. Authors:
4
5        Ville Tiensuu - ville.tiensuu@ixonos.com
6        Kaj Wallin - kaj.wallin@ixonos.com
7        Henri Lampela - henri.lampela@ixonos.com
8
9    Situare is free software; you can redistribute it and/or
10    modify it under the terms of the GNU General Public License
11    version 2 as published by the Free Software Foundation.
12
13    Situare is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with Situare; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
21    USA.
22 */
23
24 #include <QtGui>
25 #include <QtDebug>
26 #include <QDateTime>
27
28 #ifdef Q_WS_MAEMO_5
29 #include <QMaemo5InformationBox>
30 #endif // Q_WS_MAEMO_5
31
32 #include "situarecommon.h"
33 #include "facebookauthentication.h"
34 #include "facebookcommon.h"
35 #include "parser.h"
36
37 FacebookAuthentication::FacebookAuthentication(QWidget *parent)
38     : QMainWindow(parent),
39     m_email(),
40     m_loginAttempts(0),
41     m_password(),
42     m_refresh(0),
43     m_webView(0)
44 {
45     qDebug() << __PRETTY_FUNCTION__;
46
47     m_mainlayout = new QHBoxLayout;
48
49     connect(this, SIGNAL(loginFailure()),
50             this, SLOT(loginFailed()));
51
52     readCredentials(m_loginCredentials);
53 }
54
55 FacebookAuthentication::~FacebookAuthentication()
56 {
57     qDebug() << __PRETTY_FUNCTION__;
58
59     if(m_webView)
60         delete m_webView;
61
62     delete m_mainlayout;
63 }
64
65 void FacebookAuthentication::loginDialogDone(const QString &email, const QString &password)
66 {
67     qDebug() << __PRETTY_FUNCTION__;
68
69     m_email = email;
70     m_password = password;
71 }
72
73 void FacebookAuthentication::start()
74 {
75     qDebug() << __PRETTY_FUNCTION__;
76
77     if (!verifyCredentials(m_loginCredentials)) {
78
79         m_webView = new QWebView;
80         m_loginDialog = new LoginDialog(this);
81
82         connect(m_webView, SIGNAL(urlChanged(const QUrl &)),
83                 this, SLOT(updateCredentials(const QUrl &)));
84         connect(m_webView, SIGNAL(loadFinished(bool)),
85                 this, SLOT(loadDone(bool)));
86
87         connect(m_loginDialog, SIGNAL(loginDialogDone(QString,QString)),
88                 this, SLOT(loginDialogDone(QString,QString)));
89
90         if(m_loginDialog->exec() != QDialog::Accepted) {
91             // if login dialog was canceled we need to stop processing webview
92             // stop and disconnect m_webView;
93             m_webView->stop();
94             disconnect(m_webView, SIGNAL(loadFinished(bool)),
95                        this, SLOT(loadDone(bool)));
96             disconnect(m_webView, SIGNAL(urlChanged(const QUrl &)),
97                        this, SLOT(updateCredentials(const QUrl &)));
98
99             emit quitSituare();
100         }
101         QStringList list;
102         list.append(FACEBOOK_LOGINBASE);
103         list.append(SITUARE_PUBLIC_FACEBOOKAPI_KEY);
104         list.append(INTERVAL1);
105         list.append(SITUARE_LOGIN_SUCCESS);
106         list.append(INTERVAL2);
107         list.append(SITUARE_LOGIN_FAILURE);
108         list.append(FACEBOOK_LOGIN_ENDING);
109
110         m_webView->load(formLoginPageUrl(list));
111         toggleProgressIndicator(true);
112         m_refresh = true;
113         setCentralWidget(m_webView);
114         m_webView->hide();
115         this->show();
116     }
117     else
118         emit credentialsReady(m_loginCredentials);
119 }
120
121 void FacebookAuthentication::loadDone(bool done)
122 {
123     qDebug() << __PRETTY_FUNCTION__;
124
125     // for the first time the login page is opened, we need to refresh it to get cookies working
126     if(m_refresh) {
127         QStringList list;
128         list.append(FACEBOOK_LOGINBASE);
129         list.append(SITUARE_PUBLIC_FACEBOOKAPI_KEY);
130         list.append(INTERVAL1);
131         list.append(SITUARE_LOGIN_SUCCESS);
132         list.append(INTERVAL2);
133         list.append(SITUARE_LOGIN_FAILURE);
134         list.append(FACEBOOK_LOGIN_ENDING);
135
136         m_webView->load(formLoginPageUrl(list));
137         m_refresh = false;
138     }
139
140     if (done)
141     {
142         QWebFrame* frame = m_webView->page()->currentFrame();
143         if (frame!=NULL)
144         {
145             // set email box
146             QWebElementCollection emailCollection = frame->findAllElements("input[name=email]");
147
148             foreach (QWebElement element, emailCollection) {
149                 element.setAttribute("value", m_email.toAscii());
150             }
151             // set password box
152             QWebElementCollection passwordCollection = frame->findAllElements("input[name=pass]");
153             foreach (QWebElement element, passwordCollection) {
154                 element.setAttribute("value", m_password.toAscii());
155             }
156             // find connect button
157             QWebElementCollection buttonCollection = frame->findAllElements("input[name=login]");
158             foreach (QWebElement element, buttonCollection)
159             {
160                 QPoint pos(element.geometry().center());
161
162                 // send a mouse click event to the web page
163                 QMouseEvent event0(QEvent::MouseButtonPress, pos, Qt::LeftButton, Qt::LeftButton,
164                                    Qt::NoModifier);
165                 QApplication::sendEvent(m_webView->page(), &event0);
166                 QMouseEvent event1(QEvent::MouseButtonRelease, pos, Qt::LeftButton, Qt::LeftButton,
167                                    Qt::NoModifier);
168                 QApplication::sendEvent(m_webView->page(), &event1);
169             }
170         }
171     }
172 }
173
174 void FacebookAuthentication::loginFailed()
175 {
176     qDebug() << __PRETTY_FUNCTION__;
177
178     m_email.clear();
179     m_password.clear();
180
181     toggleProgressIndicator(false);
182
183 #ifdef Q_WS_MAEMO_5
184     QMaemo5InformationBox::information(this, tr("Invalid E-mail address or password"),
185                                        QMaemo5InformationBox::NoTimeout);
186
187 #endif // Q_WS_MAEMO_5
188
189     if(m_loginDialog->exec() != QDialog::Accepted) {
190         // if login dialog was canceled we need to stop processing webview        
191         // stop and disconnect m_webView;
192         m_webView->stop();
193         disconnect(m_webView, SIGNAL(loadFinished(bool)),
194                    this, SLOT(loadDone(bool)));
195         disconnect(m_webView, SIGNAL(urlChanged(const QUrl &)),
196                    this, SLOT(updateCredentials(const QUrl &)));
197
198         emit quitSituare();
199     }
200     else {
201         // re-load login page for webview
202         toggleProgressIndicator(true);
203         QStringList list;
204         list.append(FACEBOOK_LOGINBASE);
205         list.append(SITUARE_PUBLIC_FACEBOOKAPI_KEY);
206         list.append(INTERVAL1);
207         list.append(SITUARE_LOGIN_SUCCESS);
208         list.append(INTERVAL2);
209         list.append(SITUARE_LOGIN_FAILURE);
210         list.append(FACEBOOK_LOGIN_ENDING);
211
212         m_webView->load(formLoginPageUrl(list));
213     }
214 }
215
216 bool FacebookAuthentication::updateCredentials(const QUrl &url)
217 {
218     qDebug() << __PRETTY_FUNCTION__;
219
220     bool found = false;
221
222     if (url.isValid()){
223          qDebug() << "url is valid";
224
225         QString callbackUrl = url.toString();
226         qDebug() << "callbackUrl:  " << callbackUrl.toAscii();
227
228         if (callbackUrl.indexOf(LOGIN_SUCCESS_REPLY) == 0) {
229             qDebug() << "login success";
230
231             disconnect(m_webView, SIGNAL(loadFinished(bool)),
232                        this, SLOT(loadDone(bool)));
233             disconnect(m_webView, SIGNAL(urlChanged(const QUrl &)),
234                        this, SLOT(updateCredentials(const QUrl &)));
235
236             // let's find out session credentials
237             if(callbackUrl.contains(SESSION_KEY)) {
238
239                 QJson::Parser parser;
240                 bool ok;
241
242                 // split string into string part and json part
243                 QStringList list = url.toString().split("=");
244
245                 for(int i=0;i<list.count();i++) {
246                     // if string starts with json item
247                     if(list.at(i).startsWith("{")) {
248                         QByteArray jsonString = list.at(i).toAscii();
249                         QVariantMap result = parser.parse (jsonString, &ok).toMap();
250                         if (!ok) {
251
252                             qFatal("An error occurred during parsing");
253                             exit (1);
254                         }
255                         qDebug() << "Session Key" << result[SESSION_KEY].toString();
256                         m_loginCredentials.setSessionKey(result[SESSION_KEY].toString());
257
258                         qDebug() << "userID" << result[USER_ID].toString();
259                         m_loginCredentials.setUserID(result[USER_ID].toString());
260
261                         qDebug() << "Expires" << result[EXPIRES].toString();
262                         m_loginCredentials.setExpires(result[EXPIRES].toString());
263
264                         qDebug() << "Session Secret" << result[SESSION_SECRET].toString();
265                         m_loginCredentials.setSessionSecret(result[SESSION_SECRET].toString());
266
267                         qDebug() << "Signature" << result[SIGNATURE].toString();
268                         m_loginCredentials.setSig(result[SIGNATURE].toString());
269                     }
270                 }
271                 found = true;
272             }
273             writeCredentials(m_loginCredentials);
274             emit credentialsReady(m_loginCredentials);
275         }
276         else if ( callbackUrl.indexOf(LOGIN_FAILURE_REPLY) == 0){
277             qWarning() << "login failure" << endl;
278             qDebug() << callbackUrl;
279             ++m_loginAttempts;
280             /* emit loginFailure for every second login attemps, since webview loads login
281                error page (loadingDone() signal is emitted) and we need to avoid that because
282                at this point we don't have new login parameters */
283             if(m_loginAttempts % 2) {
284                 emit loginFailure();
285             }
286         }
287         else if(callbackUrl.indexOf(LOGIN_PAGE) == 0) {
288             qDebug() << "correct loginPage";
289         }
290         else {
291             qDebug() << "totally wrong webPage";
292             // we should not get a wrong page at this point
293             emit loginFailure();
294         }
295     }
296     else {
297         qDebug() << " Loading of page failed invalid URL" << endl;
298         // we should not get a wrong page at this point
299         emit loginFailure();
300         return false;
301     }
302     return found;
303 }
304
305 void FacebookAuthentication::writeCredentials(const FacebookCredentials &credentials)
306 {
307     qDebug() << __PRETTY_FUNCTION__;
308     QSettings settings(DIRECTORY_NAME, FILE_NAME);
309
310     settings.setValue(SESSION_KEY, credentials.sessionKey());
311     settings.setValue(USER_ID, credentials.userID());
312     settings.setValue(EXPIRES, credentials.expires());
313     settings.setValue(SESSION_SECRET, credentials.sessionSecret());
314     settings.setValue(SIGNATURE, credentials.sig());
315 }
316
317 void FacebookAuthentication::readCredentials(FacebookCredentials &credentialsFromFile)
318 {
319     qDebug() << __PRETTY_FUNCTION__;
320
321     QSettings settings(DIRECTORY_NAME, FILE_NAME);
322
323     credentialsFromFile.setSessionKey(settings.value(SESSION_KEY, ERROR).toString());
324     credentialsFromFile.setUserID(settings.value(USER_ID, ERROR).toString());
325     credentialsFromFile.setExpires(settings.value(EXPIRES, ERROR).toString());
326     credentialsFromFile.setSessionSecret(settings.value(SESSION_SECRET, ERROR).toString());
327     credentialsFromFile.setSig(settings.value(SIGNATURE, ERROR).toString());
328 }
329
330  FacebookCredentials FacebookAuthentication::loginCredentials() const
331  {
332      qDebug() << __PRETTY_FUNCTION__;
333      return m_loginCredentials;
334  }
335
336  bool FacebookAuthentication::verifyCredentials(const FacebookCredentials &credentials) const
337  {
338      qDebug() << __PRETTY_FUNCTION__;
339
340      // if expires value is 0, then credentials are valid forever
341      if(credentials.expires() == "0") {
342          return true;
343      }
344      else {
345          const QString dateTimeFormat = "dd.MM.yyyy  hh:mm:ss";
346          QString expires = credentials.expires();
347          QDateTime expireTime;
348          expireTime.setTime_t(expires.toInt());
349          QString expiresString = expireTime.toString(dateTimeFormat);
350          qDebug() << expiresString.toAscii();
351
352          QDateTime currentTime;
353          currentTime = QDateTime::currentDateTime();
354          QString currentTimeString = currentTime.toString(dateTimeFormat);
355          qDebug() << currentTimeString.toAscii();
356
357          return currentTime < expireTime;
358      }
359  }
360
361  QUrl FacebookAuthentication::formLoginPageUrl(const QStringList &urlParts) const
362  {
363     qDebug() << __PRETTY_FUNCTION__;
364
365     return QUrl(urlParts.join(EMPTY));
366  }
367
368  void FacebookAuthentication::toggleProgressIndicator(bool value)
369  {
370      qDebug() << __PRETTY_FUNCTION__;
371  #ifdef Q_WS_MAEMO_5
372      setAttribute(Qt::WA_Maemo5ShowProgressIndicator, value);
373  #else
374      Q_UNUSED(value);
375  #endif // Q_WS_MAEMO_5
376  }