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