Whitespace fixes
[amule.git] / src / libs / ec / cpp / RemoteConnect.cpp
blob772aa45cad89421cee6105e40830f0c4b0cf9801
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2004-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2004-2011 Angel Vidal ( kry@amule.org )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "RemoteConnect.h"
28 #include <common/MD5Sum.h>
29 #include <common/Format.h>
31 #include <wx/intl.h>
33 using std::auto_ptr;
35 DEFINE_LOCAL_EVENT_TYPE(wxEVT_EC_CONNECTION)
37 CECLoginPacket::CECLoginPacket(const wxString& client, const wxString& version,
38 bool canZLIB, bool canUTF8numbers, bool canNotify)
40 CECPacket(EC_OP_AUTH_REQ)
42 AddTag(CECTag(EC_TAG_CLIENT_NAME, client));
43 AddTag(CECTag(EC_TAG_CLIENT_VERSION, version));
44 AddTag(CECTag(EC_TAG_PROTOCOL_VERSION, (uint64)EC_CURRENT_PROTOCOL_VERSION));
46 #ifdef EC_VERSION_ID
47 CMD4Hash versionhash;
48 wxCHECK2(versionhash.Decode(wxT(EC_VERSION_ID)), /* Do nothing. */);
49 AddTag(CECTag(EC_TAG_VERSION_ID, versionhash));
50 #endif
52 // Send capabilities:
53 // support ZLIB compression
54 if (canZLIB) AddTag(CECEmptyTag(EC_TAG_CAN_ZLIB));
55 // support encoding of integers as UTF-8
56 if (canUTF8numbers) AddTag(CECEmptyTag(EC_TAG_CAN_UTF8_NUMBERS));
57 // client accepts push messages
58 if (canNotify) AddTag(CECEmptyTag(EC_TAG_CAN_NOTIFY));
61 CECAuthPacket::CECAuthPacket(const wxString& pass)
63 CECPacket(EC_OP_AUTH_PASSWD)
65 CMD4Hash passhash;
66 wxCHECK2(passhash.Decode(pass), /* Do nothing. */);
67 AddTag(CECTag(EC_TAG_PASSWD_HASH, passhash));
70 /*!
71 * Connection to remote core
75 CRemoteConnect::CRemoteConnect(wxEvtHandler* evt_handler)
77 CECMuleSocket(evt_handler != 0),
78 m_ec_state(EC_INIT),
79 m_req_fifo(),
80 // Give application some indication about how fast requests are served
81 // When request fifo contain more that certain number of entries, it may
82 // indicate that either core or network is slowing us down
83 m_req_count(0),
84 // This is not mean to be absolute limit, because we can't drop requests
85 // out of calling context; it is just signal to application to slow down
86 m_req_fifo_thr(20),
87 m_notifier(evt_handler),
88 m_canZLIB(false),
89 m_canUTF8numbers(false),
90 m_canNotify(false)
94 void CRemoteConnect::SetCapabilities(bool canZLIB, bool canUTF8numbers, bool canNotify)
96 m_canZLIB = canZLIB;
97 if (canZLIB) {
98 m_my_flags |= EC_FLAG_ZLIB;
100 m_canUTF8numbers = canUTF8numbers;
101 if (canUTF8numbers) {
102 m_my_flags |= EC_FLAG_UTF8_NUMBERS;
104 m_canNotify = canNotify;
107 bool CRemoteConnect::ConnectToCore(const wxString &host, int port,
108 const wxString &WXUNUSED(login), const wxString &pass,
109 const wxString& client, const wxString& version)
111 m_connectionPassword = pass;
113 m_client = client;
114 m_version = version;
116 // don't even try to connect without a valid password
117 if (m_connectionPassword.IsEmpty() || m_connectionPassword == wxT("d41d8cd98f00b204e9800998ecf8427e")) {
118 m_server_reply = _("You must specify a non-empty password.");
119 return false;
120 } else {
121 CMD4Hash hash;
122 if (!hash.Decode(m_connectionPassword)) {
123 m_server_reply = _("Invalid password, not a MD5 hash!");
124 return false;
125 } else if (hash.IsEmpty()) {
126 m_server_reply = _("You must specify a non-empty password.");
127 return false;
131 wxIPV4address addr;
133 addr.Hostname(host);
134 addr.Service(port);
136 if (ConnectSocket(addr)) {
137 CECLoginPacket login_req(m_client, m_version, m_canZLIB, m_canUTF8numbers, m_canNotify);
139 std::auto_ptr<const CECPacket> getSalt(SendRecvPacket(&login_req));
140 m_ec_state = EC_REQ_SENT;
142 ProcessAuthPacket(getSalt.get());
144 CECAuthPacket passwdPacket(m_connectionPassword);
146 std::auto_ptr<const CECPacket> reply(SendRecvPacket(&passwdPacket));
147 m_ec_state = EC_PASSWD_SENT;
149 return ProcessAuthPacket(reply.get());
150 } else if (m_notifier) {
151 m_ec_state = EC_CONNECT_SENT;
152 } else {
153 return false;
156 return true;
159 bool CRemoteConnect::IsConnectedToLocalHost()
161 wxIPV4address addr;
162 return GetPeer(addr) ? addr.IsLocalHost() : false;
165 void CRemoteConnect::WriteDoneAndQueueEmpty()
169 void CRemoteConnect::OnConnect() {
170 if (m_notifier) {
171 wxASSERT(m_ec_state == EC_CONNECT_SENT);
172 CECLoginPacket login_req(m_client, m_version, m_canZLIB, m_canUTF8numbers, m_canNotify);
173 CECSocket::SendPacket(&login_req);
175 m_ec_state = EC_REQ_SENT;
176 } else {
177 // do nothing, calling code will take from here
181 void CRemoteConnect::OnLost() {
182 if (m_notifier) {
183 // Notify app of failure
184 wxECSocketEvent event(wxEVT_EC_CONNECTION,false,_("Connection failure"));
185 m_notifier->AddPendingEvent(event);
189 const CECPacket *CRemoteConnect::OnPacketReceived(const CECPacket *packet, uint32 trueSize)
191 CECPacket *next_packet = 0;
192 m_req_count--;
193 packet->DebugPrint(true, trueSize);
194 switch(m_ec_state) {
195 case EC_REQ_SENT:
196 if (ProcessAuthPacket(packet)) {
197 CECAuthPacket passwdPacket(m_connectionPassword);
198 CECSocket::SendPacket(&passwdPacket);
199 m_ec_state = EC_PASSWD_SENT;
201 break;
202 case EC_PASSWD_SENT:
203 ProcessAuthPacket(packet);
204 break;
205 case EC_OK:
206 if ( !m_req_fifo.empty() ) {
207 CECPacketHandlerBase *handler = m_req_fifo.front();
208 m_req_fifo.pop_front();
209 if ( handler ) {
210 handler->HandlePacket(packet);
212 } else {
213 printf("EC error - packet received, but request fifo is empty\n");
215 break;
216 default:
217 break;
220 // no reply by default
221 return next_packet;
225 * Our requests are served by core in FCFS order. And core always replies. So, even
226 * if we're not interested in reply, we preserve place in request fifo.
228 void CRemoteConnect::SendRequest(CECPacketHandlerBase *handler, const CECPacket *request)
230 m_req_count++;
231 m_req_fifo.push_back(handler);
232 CECSocket::SendPacket(request);
235 void CRemoteConnect::SendPacket(const CECPacket *request)
237 SendRequest(0, request);
240 bool CRemoteConnect::ProcessAuthPacket(const CECPacket *reply) {
241 bool result = false;
243 if (!reply) {
244 m_server_reply = _("EC connection failed. Empty reply.");
245 CloseSocket();
246 } else {
247 if ((m_ec_state == EC_REQ_SENT) && (reply->GetOpCode() == EC_OP_AUTH_SALT)) {
248 const CECTag *passwordSalt = reply->GetTagByName(EC_TAG_PASSWD_SALT);
249 if ( NULL != passwordSalt) {
250 wxString saltHash = MD5Sum(CFormat(wxT("%lX")) % passwordSalt->GetInt()).GetHash();
251 m_connectionPassword = MD5Sum(m_connectionPassword.Lower() + saltHash).GetHash();
252 m_ec_state = EC_SALT_RECEIVED;
253 return true;
254 } else {
255 m_server_reply = _("External Connection: Bad reply, handshake failed. Connection closed.");
256 m_ec_state = EC_FAIL;
257 CloseSocket();
259 } else if ((m_ec_state == EC_PASSWD_SENT) && (reply->GetOpCode() == EC_OP_AUTH_OK)) {
260 m_ec_state = EC_OK;
261 result = true;
262 if (reply->GetTagByName(EC_TAG_SERVER_VERSION)) {
263 m_server_reply = _("Succeeded! Connection established to aMule ") +
264 reply->GetTagByName(EC_TAG_SERVER_VERSION)->GetStringData();
265 } else {
266 m_server_reply = _("Succeeded! Connection established.");
268 }else {
269 m_ec_state = EC_FAIL;
270 const CECTag *reason = reply->GetTagByName(EC_TAG_STRING);
271 if (reason != NULL) {
272 m_server_reply = wxString(_("External Connection: Access denied because: ")) +
273 wxGetTranslation(reason->GetStringData());
274 } else {
275 m_server_reply = _("External Connection: Handshake failed.");
277 CloseSocket();
280 if ( m_notifier ) {
281 wxECSocketEvent event(wxEVT_EC_CONNECTION, result, m_server_reply);
282 m_notifier->AddPendingEvent(event);
284 return result;
287 /******************** EC API ***********************/
289 void CRemoteConnect::StartKad() {
290 CECPacket req(EC_OP_KAD_START);
291 SendPacket(&req);
294 void CRemoteConnect::StopKad() {
295 CECPacket req(EC_OP_KAD_STOP);
296 SendPacket(&req);
299 void CRemoteConnect::ConnectED2K(uint32 ip, uint16 port) {
300 CECPacket req(EC_OP_SERVER_CONNECT);
301 if (ip && port) {
302 req.AddTag(CECTag(EC_TAG_SERVER, EC_IPv4_t(ip, port)));
304 SendPacket(&req);
307 void CRemoteConnect::DisconnectED2K() {
308 CECPacket req(EC_OP_SERVER_DISCONNECT);
309 SendPacket(&req);
312 void CRemoteConnect::RemoveServer(uint32 ip, uint16 port) {
313 CECPacket req(EC_OP_SERVER_REMOVE);
314 if (ip && port) {
315 req.AddTag(CECTag(EC_TAG_SERVER, EC_IPv4_t(ip, port)));
317 SendPacket(&req);
319 // File_checked_for_headers