218 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			218 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| This file is part of Telegram Desktop,
 | |
| the official desktop version of Telegram messaging app, see https://telegram.org
 | |
| 
 | |
| Telegram Desktop is free software: you can redistribute it and/or modify
 | |
| it under the terms of the GNU General Public License as published by
 | |
| the Free Software Foundation, either version 3 of the License, or
 | |
| (at your option) any later version.
 | |
| 
 | |
| It is distributed in the hope that it will be useful,
 | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | |
| GNU General Public License for more details.
 | |
| 
 | |
| In addition, as a special exception, the copyright holders give permission
 | |
| to link the code of portions of this program with the OpenSSL library.
 | |
| 
 | |
| Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
 | |
| Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | |
| */
 | |
| #include "stdafx.h"
 | |
| 
 | |
| #include "mtproto/connection_http.h"
 | |
| 
 | |
| namespace MTP {
 | |
| namespace internal {
 | |
| 
 | |
| mtpBuffer HTTPConnection::handleResponse(QNetworkReply *reply) {
 | |
| 	QByteArray response = reply->readAll();
 | |
| 	TCP_LOG(("HTTP Info: read %1 bytes").arg(response.size()));
 | |
| 
 | |
| 	if (response.isEmpty()) return mtpBuffer();
 | |
| 
 | |
| 	if (response.size() & 0x03 || response.size() < 8) {
 | |
| 		LOG(("HTTP Error: bad response size %1").arg(response.size()));
 | |
| 		return mtpBuffer(1, -500);
 | |
| 	}
 | |
| 
 | |
| 	mtpBuffer data(response.size() >> 2);
 | |
| 	memcpy(data.data(), response.constData(), response.size());
 | |
| 
 | |
| 	return data;
 | |
| }
 | |
| 
 | |
| bool HTTPConnection::handleError(QNetworkReply *reply) { // returnes "maybe bad key"
 | |
| 	bool mayBeBadKey = false;
 | |
| 
 | |
| 	QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
 | |
| 	if (statusCode.isValid()) {
 | |
| 		int status = statusCode.toInt();
 | |
| 		mayBeBadKey = (status == 410);
 | |
| 		if (status == 429) {
 | |
| 			LOG(("Protocol Error: 429 flood code returned!"));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	switch (reply->error()) {
 | |
| 	case QNetworkReply::ConnectionRefusedError: LOG(("HTTP Error: connection refused - %1").arg(reply->errorString())); break;
 | |
| 	case QNetworkReply::RemoteHostClosedError: LOG(("HTTP Error: remote host closed - %1").arg(reply->errorString())); break;
 | |
| 	case QNetworkReply::HostNotFoundError: LOG(("HTTP Error: host not found - %2").arg(reply->error()).arg(reply->errorString())); break;
 | |
| 	case QNetworkReply::TimeoutError: LOG(("HTTP Error: timeout - %2").arg(reply->error()).arg(reply->errorString())); break;
 | |
| 	case QNetworkReply::OperationCanceledError: LOG(("HTTP Error: cancelled - %2").arg(reply->error()).arg(reply->errorString())); break;
 | |
| 	case QNetworkReply::SslHandshakeFailedError:
 | |
| 	case QNetworkReply::TemporaryNetworkFailureError:
 | |
| 	case QNetworkReply::NetworkSessionFailedError:
 | |
| 	case QNetworkReply::BackgroundRequestNotAllowedError:
 | |
| 	case QNetworkReply::UnknownNetworkError: LOG(("HTTP Error: network error %1 - %2").arg(reply->error()).arg(reply->errorString())); break;
 | |
| 
 | |
| 		// proxy errors (101-199):
 | |
| 	case QNetworkReply::ProxyConnectionRefusedError:
 | |
| 	case QNetworkReply::ProxyConnectionClosedError:
 | |
| 	case QNetworkReply::ProxyNotFoundError:
 | |
| 	case QNetworkReply::ProxyTimeoutError:
 | |
| 	case QNetworkReply::ProxyAuthenticationRequiredError:
 | |
| 	case QNetworkReply::UnknownProxyError:LOG(("HTTP Error: proxy error %1 - %2").arg(reply->error()).arg(reply->errorString())); break;
 | |
| 
 | |
| 		// content errors (201-299):
 | |
| 	case QNetworkReply::ContentAccessDenied:
 | |
| 	case QNetworkReply::ContentOperationNotPermittedError:
 | |
| 	case QNetworkReply::ContentNotFoundError:
 | |
| 	case QNetworkReply::AuthenticationRequiredError:
 | |
| 	case QNetworkReply::ContentReSendError:
 | |
| 	case QNetworkReply::UnknownContentError: LOG(("HTTP Error: content error %1 - %2").arg(reply->error()).arg(reply->errorString())); break;
 | |
| 
 | |
| 		// protocol errors
 | |
| 	case QNetworkReply::ProtocolUnknownError:
 | |
| 	case QNetworkReply::ProtocolInvalidOperationError:
 | |
| 	case QNetworkReply::ProtocolFailure: LOG(("HTTP Error: protocol error %1 - %2").arg(reply->error()).arg(reply->errorString())); break;
 | |
| 	};
 | |
| 	TCP_LOG(("HTTP Error %1, restarting! - %2").arg(reply->error()).arg(reply->errorString()));
 | |
| 
 | |
| 	return mayBeBadKey;
 | |
| }
 | |
| 
 | |
| HTTPConnection::HTTPConnection(QThread *thread) : AbstractConnection(thread)
 | |
| , status(WaitingHttp)
 | |
| , httpNonce(rand_value<MTPint128>())
 | |
| , _flags(0) {
 | |
| 	manager.moveToThread(thread);
 | |
| 	App::setProxySettings(manager);
 | |
| }
 | |
| 
 | |
| void HTTPConnection::sendData(mtpBuffer &buffer) {
 | |
| 	if (status == FinishedWork) return;
 | |
| 
 | |
| 	if (buffer.size() < 3) {
 | |
| 		LOG(("TCP Error: writing bad packet, len = %1").arg(buffer.size() * sizeof(mtpPrime)));
 | |
| 		TCP_LOG(("TCP Error: bad packet %1").arg(Logs::mb(&buffer[0], buffer.size() * sizeof(mtpPrime)).str()));
 | |
| 		emit error();
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	int32 requestSize = (buffer.size() - 3) * sizeof(mtpPrime);
 | |
| 
 | |
| 	QNetworkRequest request(address);
 | |
| 	request.setHeader(QNetworkRequest::ContentLengthHeader, QVariant(requestSize));
 | |
| 	request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(qsl("application/x-www-form-urlencoded")));
 | |
| 
 | |
| 	TCP_LOG(("HTTP Info: sending %1 len request %2").arg(requestSize).arg(Logs::mb(&buffer[2], requestSize).str()));
 | |
| 	requests.insert(manager.post(request, QByteArray((const char*)(&buffer[2]), requestSize)));
 | |
| }
 | |
| 
 | |
| void HTTPConnection::disconnectFromServer() {
 | |
| 	if (status == FinishedWork) return;
 | |
| 	status = FinishedWork;
 | |
| 
 | |
| 	Requests copy = requests;
 | |
| 	requests.clear();
 | |
| 	for (Requests::const_iterator i = copy.cbegin(), e = copy.cend(); i != e; ++i) {
 | |
| 		(*i)->abort();
 | |
| 		(*i)->deleteLater();
 | |
| 	}
 | |
| 
 | |
| 	disconnect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*)));
 | |
| 
 | |
| 	address = QUrl();
 | |
| }
 | |
| 
 | |
| void HTTPConnection::connectHttp(const QString &addr, int32 p, MTPDdcOption::Flags flags) {
 | |
| 	address = QUrl(((flags & MTPDdcOption::Flag::f_ipv6) ? qsl("http://[%1]:%2/api") : qsl("http://%1:%2/api")).arg(addr).arg(80));//not p - always 80 port for http transport
 | |
| 	TCP_LOG(("HTTP Info: address is %1").arg(address.toDisplayString()));
 | |
| 	connect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*)));
 | |
| 
 | |
| 	_flags = flags;
 | |
| 
 | |
| 	mtpBuffer buffer(preparePQFake(httpNonce));
 | |
| 
 | |
| 	DEBUG_LOG(("Connection Info: sending fake req_pq through HTTP/%1 transport").arg((flags & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4"));
 | |
| 
 | |
| 	sendData(buffer);
 | |
| }
 | |
| 
 | |
| bool HTTPConnection::isConnected() const {
 | |
| 	return (status == UsingHttp);
 | |
| }
 | |
| 
 | |
| void HTTPConnection::requestFinished(QNetworkReply *reply) {
 | |
| 	if (status == FinishedWork) return;
 | |
| 
 | |
| 	reply->deleteLater();
 | |
| 	if (reply->error() == QNetworkReply::NoError) {
 | |
| 		requests.remove(reply);
 | |
| 
 | |
| 		mtpBuffer data = handleResponse(reply);
 | |
| 		if (data.size() == 1) {
 | |
| 			emit error();
 | |
| 		} else if (!data.isEmpty()) {
 | |
| 			if (status == UsingHttp) {
 | |
| 				receivedQueue.push_back(data);
 | |
| 				emit receivedData();
 | |
| 			} else {
 | |
| 				try {
 | |
| 					MTPResPQ res_pq = readPQFakeReply(data);
 | |
| 					const MTPDresPQ &res_pq_data(res_pq.c_resPQ());
 | |
| 					if (res_pq_data.vnonce == httpNonce) {
 | |
| 						DEBUG_LOG(("Connection Info: HTTP/%1-transport connected by pq-response").arg((_flags & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4"));
 | |
| 						status = UsingHttp;
 | |
| 						emit connected();
 | |
| 					}
 | |
| 				} catch (Exception &e) {
 | |
| 					DEBUG_LOG(("Connection Error: exception in parsing HTTP fake pq-responce, %1").arg(e.what()));
 | |
| 					emit error();
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	} else {
 | |
| 		if (!requests.remove(reply)) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		bool mayBeBadKey = handleError(reply) && _sentEncrypted;
 | |
| 
 | |
| 		emit error(mayBeBadKey);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| bool HTTPConnection::usingHttpWait() {
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| bool HTTPConnection::needHttpWait() {
 | |
| 	return requests.isEmpty();
 | |
| }
 | |
| 
 | |
| int32 HTTPConnection::debugState() const {
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| QString HTTPConnection::transport() const {
 | |
| 	if (status == UsingHttp) {
 | |
| 		return qsl("HTTP");
 | |
| 	} else {
 | |
| 		return QString();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| } // namespace internal
 | |
| } // namespace MTP
 | 
