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"