2016-03-24 15:57:10 +03:00
/*
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 {
2016-04-08 14:44:35 +04:00
auto res_pq = readPQFakeReply ( data ) ;
const auto & res_pq_data ( res_pq . c_resPQ ( ) ) ;
2016-03-24 15:57:10 +03:00
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