File indexing completed on 2025-01-19 03:55:40

0001 #include <QTcpSocket>
0002 #include <QPair>
0003 #include <QTimer>
0004 #include <QStringList>
0005 #include <QUrl>
0006 #include <QDebug>
0007 #if QT_VERSION >= 0x050000
0008 #include <QUrlQuery>
0009 #endif
0010 
0011 #include "o0globals.h"
0012 #include "o2replyserver.h"
0013 
0014 O2ReplyServer::O2ReplyServer(QObject *parent): QTcpServer(parent),
0015   timeout_(15), maxtries_(3), tries_(0) {
0016     qDebug() << "O2ReplyServer: Starting";
0017     connect(this, SIGNAL(newConnection()), this, SLOT(onIncomingConnection()));
0018     replyContent_ = "<HTML></HTML>";
0019 }
0020 
0021 void O2ReplyServer::onIncomingConnection() {
0022     qDebug() << "O2ReplyServer::onIncomingConnection: Receiving...";
0023     QTcpSocket *socket = nextPendingConnection();
0024     connect(socket, SIGNAL(readyRead()), this, SLOT(onBytesReady()), Qt::UniqueConnection);
0025     connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));
0026 
0027     // Wait for a bit *after* first response, then close server if no useable data has arrived
0028     // Helps with implicit flow, where a URL fragment may need processed by local user-agent and
0029     // sent as secondary query string callback, or additional requests make it through first,
0030     // like for favicons, etc., before such secondary callbacks are fired
0031     QTimer *timer = new QTimer(socket);
0032     timer->setObjectName("timeoutTimer");
0033     connect(timer, SIGNAL(timeout()), this, SLOT(closeServer()));
0034     timer->setSingleShot(true);
0035     timer->setInterval(timeout() * 1000);
0036     connect(socket, SIGNAL(readyRead()), timer, SLOT(start()));
0037 }
0038 
0039 void O2ReplyServer::onBytesReady() {
0040     if (!isListening()) {
0041         // server has been closed, stop processing queued connections
0042         return;
0043     }
0044     qDebug() << "O2ReplyServer::onBytesReady: Processing request";
0045     // NOTE: on first call, the timeout timer is started
0046     QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());
0047     if (!socket) {
0048         qWarning() << "O2ReplyServer::onBytesReady: No socket available";
0049         return;
0050     }
0051     QByteArray reply;
0052     reply.append("HTTP/1.0 200 OK \r\n");
0053     reply.append("Content-Type: text/html; charset=\"utf-8\"\r\n");
0054     reply.append(QString("Content-Length: %1\r\n\r\n").arg(replyContent_.size()).toLatin1());
0055     reply.append(replyContent_);
0056     socket->write(reply);
0057     qDebug() << "O2ReplyServer::onBytesReady: Sent reply";
0058 
0059     QByteArray data = socket->readAll();
0060     QMap<QString, QString> queryParams = parseQueryParams(&data);
0061     if (queryParams.isEmpty()) {
0062         if (tries_ < maxtries_ ) {
0063             qDebug() << "O2ReplyServer::onBytesReady: No query params found, waiting for more callbacks";
0064             ++tries_;
0065             return;
0066         } else {
0067             tries_ = 0;
0068             qWarning() << "O2ReplyServer::onBytesReady: No query params found, maximum callbacks received";
0069             closeServer(socket, false);
0070             return;
0071         }
0072     }
0073     if (!uniqueState_.isEmpty() && !queryParams.contains(QString(O2_OAUTH2_STATE))) {
0074         qDebug() << "O2ReplyServer::onBytesReady: Malicious or service request";
0075         closeServer(socket, true);
0076         return; // Malicious or service (e.g. favicon.ico) request
0077     }
0078     qDebug() << "O2ReplyServer::onBytesReady: Query params found, closing server";
0079     closeServer(socket, true);
0080     Q_EMIT verificationReceived(queryParams);
0081 }
0082 
0083 QMap<QString, QString> O2ReplyServer::parseQueryParams(QByteArray *data) {
0084     qDebug() << "O2ReplyServer::parseQueryParams";
0085 
0086     //qDebug() << QString("O2ReplyServer::parseQueryParams data:\n%1").arg(QString(*data));
0087 
0088     QString splitGetLine = QString(*data).split("\r\n").first();
0089     splitGetLine.remove("GET ");
0090     splitGetLine.remove("HTTP/1.1");
0091     splitGetLine.remove("\r\n");
0092     splitGetLine.prepend("http://localhost");
0093     QUrl getTokenUrl(splitGetLine);
0094 
0095     QList< QPair<QString, QString> > tokens;
0096 #if QT_VERSION < 0x050000
0097     tokens = getTokenUrl.queryItems();
0098 #else
0099     QUrlQuery query(getTokenUrl);
0100     tokens = query.queryItems();
0101 #endif
0102     QMap<QString, QString> queryParams;
0103     QPair<QString, QString> tokenPair;
0104     foreach (tokenPair, tokens) {
0105         // FIXME: We are decoding key and value again. This helps with Google OAuth, but is it mandated by the standard?
0106         QString key = QUrl::fromPercentEncoding(QByteArray().append(tokenPair.first.trimmed().toLatin1()));
0107         QString value = QUrl::fromPercentEncoding(QByteArray().append(tokenPair.second.trimmed().toLatin1()));
0108         queryParams.insert(key, value);
0109     }
0110     return queryParams;
0111 }
0112 
0113 void O2ReplyServer::closeServer(QTcpSocket *socket, bool hasparameters)
0114 {
0115   if (!isListening()) {
0116       return;
0117   }
0118 
0119   qDebug() << "O2ReplyServer::closeServer: Initiating";
0120   int port = serverPort();
0121 
0122   if (!socket && sender()) {
0123       QTimer *timer = qobject_cast<QTimer*>(sender());
0124       if (timer) {
0125           qWarning() << "O2ReplyServer::closeServer: Closing due to timeout";
0126           timer->stop();
0127           socket = qobject_cast<QTcpSocket *>(timer->parent());
0128           timer->deleteLater();
0129       }
0130   }
0131   if (socket) {
0132       QTimer *timer = socket->findChild<QTimer*>("timeoutTimer");
0133       if (timer) {
0134           qDebug() << "O2ReplyServer::closeServer: Stopping socket's timeout timer";
0135           timer->stop();
0136       }
0137       socket->disconnectFromHost();
0138   }
0139   close();
0140   qDebug() << "O2ReplyServer::closeServer: Closed, no longer listening on port" << port;
0141   Q_EMIT serverClosed(hasparameters);
0142 }
0143 
0144 QByteArray O2ReplyServer::replyContent() {
0145     return replyContent_;
0146 }
0147 
0148 void O2ReplyServer::setReplyContent(const QByteArray &value) {
0149   replyContent_ = value;
0150 }
0151 
0152 int O2ReplyServer::timeout()
0153 {
0154   return timeout_;
0155 }
0156 
0157 void O2ReplyServer::setTimeout(int timeout)
0158 {
0159   timeout_ = timeout;
0160 }
0161 
0162 int O2ReplyServer::callbackTries()
0163 {
0164   return maxtries_;
0165 }
0166 
0167 void O2ReplyServer::setCallbackTries(int maxtries)
0168 {
0169   maxtries_ = maxtries;
0170 }
0171 
0172 QString O2ReplyServer::uniqueState()
0173 {
0174     return uniqueState_;
0175 }
0176 
0177 void O2ReplyServer::setUniqueState(const QString &state)
0178 {
0179     uniqueState_ = state;
0180 }
0181 
0182 #include "moc_o2replyserver.cpp"