1//
2// This file is part of the aMule Project.
3//
4// Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5// Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
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.
10//
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.
15//
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.
20//
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
24//
25
26#include <wx/wx.h>
27
28#include "ED2KLink.h"			// Interface declarations.
29
30#include <wx/string.h>
31#include <wx/regex.h>			// Needed for wxRegEx
32#include <wx/tokenzr.h>			// Needed for wxStringTokenizer
33
34#include <protocol/ed2k/Constants.h>
35
36#include "MemFile.h"			// Needed for CMemFile
37#include "NetworkFunctions.h"	// Needed for Uint32toStringIP
38#include <common/Format.h>		// Needed for CFormat
39
40
41CED2KLink::CED2KLink( LinkType type )
42	: m_type( type )
43{
44}
45
46
47CED2KLink::~CED2KLink()
48{
49}
50
51
52CED2KLink::LinkType CED2KLink::GetKind() const
53{
54	return m_type;
55}
56
57
58CED2KLink* CED2KLink::CreateLinkFromUrl(const wxString& link)
59{
60	wxRegEx re_type(wxT("ed2k://\\|(file|server|serverlist)\\|.*/"), wxRE_ICASE | wxRE_DEFAULT);
61	{ wxCHECK(re_type.IsValid(), NULL); }
62
63	if (!re_type.Matches(link)) {
64		throw wxString(wxT("Not a valid ed2k-URI"));
65	}
66
67	wxString type = re_type.GetMatch(link, 1).MakeLower();
68	{ wxCHECK(type.Length(), NULL); }
69
70	if (type == wxT("file")) {
71		return new CED2KFileLink(link);
72	} else if (type == wxT("server")) {
73		return new CED2KServerLink(link);
74	} else if (type == wxT("serverlist")) {
75		return new CED2KServerListLink(link);
76	} else {
77		wxCHECK(false, NULL);
78	}
79}
80
81
82/////////////////////////////////////////////
83// CED2KServerListLink implementation
84/////////////////////////////////////////////
85CED2KServerListLink::CED2KServerListLink(const wxString& link)
86	: CED2KLink( kServerList )
87{
88	wxRegEx re(wxT("ed2k://\\|serverlist\\|(.*)\\|/"), wxRE_ICASE | wxRE_DEFAULT);
89	if (!re.Matches(link)) {
90		throw wxString(wxT("Not a valid server-list link."));
91	}
92
93	m_address = UnescapeHTML(re.GetMatch(link, 1));
94}
95
96
97wxString CED2KServerListLink::GetLink() const
98{
99	return wxT("ed2k://|serverlist|") + m_address + wxT("|/");
100}
101
102
103const wxString& CED2KServerListLink::GetAddress() const
104{
105	return m_address;
106}
107
108
109/////////////////////////////////////////////
110// CED2KServerLink implementation
111/////////////////////////////////////////////
112CED2KServerLink::CED2KServerLink(const wxString& link)
113	: CED2KLink( kServer )
114{
115	wxRegEx re(wxT("ed2k://\\|server\\|([^\\|]+)\\|([0-9]+)\\|/"), wxRE_ICASE | wxRE_DEFAULT);
116	if (!re.Matches(link)) {
117		throw wxString(wxT("Not a valid server link."));
118	}
119
120	wxString ip = UnescapeHTML(re.GetMatch(link, 1));
121	wxString port = re.GetMatch(link, 2);
122
123	unsigned long ul = StrToULong(port);
124	if (ul > 0xFFFF || ul == 0) {
125		throw wxString( wxT("Bad port number") );
126	}
127
128	m_port = static_cast<uint16>(ul);
129	m_ip = StringIPtoUint32(ip);
130}
131
132
133wxString CED2KServerLink::GetLink() const
134{
135	return wxString(wxT("ed2k://|server|")) << Uint32toStringIP(m_ip) << wxT("|") << m_port << wxT("|/");
136}
137
138
139uint32 CED2KServerLink::GetIP() const
140{
141	return m_ip;
142}
143
144
145uint16 CED2KServerLink::GetPort() const
146{
147	return m_port;
148}
149
150
151/////////////////////////////////////////////
152// CED2KFileLink implementation
153/////////////////////////////////////////////
154CED2KFileLink::CED2KFileLink(const wxString& link)
155	: CED2KLink( kFile ),
156	  m_hashset(NULL),
157	  m_size(0),
158	  m_bAICHHashValid(false)
159{
160	// Start tokenizing after the "ed2k:://|file|" part of the link
161	wxStringTokenizer tokens(link, wxT("|"), wxTOKEN_RET_EMPTY_ALL);
162
163	// Must at least be ed2k://|file|NAME|SIZE|HASH|/
164	if (tokens.CountTokens() < 5
165		|| tokens.GetNextToken() != wxT("ed2k://")
166		|| tokens.GetNextToken() != wxT("file")) {
167		throw wxString(wxT("Not a valid file link"));
168	}
169
170	m_name = UnescapeHTML(tokens.GetNextToken().Strip(wxString::both));
171	// We don't want a path in the name.
172	m_name.Replace(wxT("/"), wxT("_"));
173
174	// Note that StrToULong returns ULONG_MAX if the value is
175	// too large to be contained in a unsigned long, which means
176	// that this check is valid, as odd as it seems
177	wxString size = tokens.GetNextToken().Strip(wxString::both);
178	m_size = StrToULongLong(size);
179	if ((m_size == 0) || (m_size > MAX_FILE_SIZE)) {
180		throw wxString(CFormat(wxT("Invalid file size %i")) % m_size);
181	}
182
183	if (!m_hash.Decode(tokens.GetNextToken().Strip(wxString::both))) {
184		throw wxString(wxT("Invalid hash"));
185	}
186
187	// Check extra fields (sources, parthashes, masterhashes)
188	while (tokens.HasMoreTokens()) {
189		wxString field = tokens.GetNextToken().MakeLower().Strip(wxString::both);
190
191		if (field.StartsWith(wxT("sources,"))) {
192			wxStringTokenizer srcTokens(field, wxT(","));
193			// Skipping the first token ("sources").
194			wxString token = srcTokens.GetNextToken();
195			while (srcTokens.HasMoreTokens()) {
196				token = srcTokens.GetNextToken().Strip(wxString::both);
197
198				wxStringTokenizer sourceTokens(token, wxT(":"));
199				wxString addr = sourceTokens.GetNextToken();
200				if (addr.IsEmpty()) {
201					throw wxString( wxT("Empty address" ) );
202				}
203
204				wxString strport = sourceTokens.GetNextToken();
205				if (strport.IsEmpty()) {
206					throw wxString( wxT("Empty port" ) );
207				}
208
209				unsigned port = StrToULong(strport);
210
211				// Sanity checking
212				if ((port == 0) || (port > 0xFFFF)) {
213					throw wxString( wxT("Invalid Port" ) );
214				}
215
216				wxString sourcehash;
217				uint8 cryptoptions =0;
218				wxString strcryptoptions = sourceTokens.GetNextToken();
219				if (!strcryptoptions.IsEmpty()) {
220					cryptoptions = (uint8) StrToULong(strcryptoptions);
221					if ((cryptoptions & 0x80) > 0) {
222						// Source ready for encryption, hash included.
223						sourcehash = sourceTokens.GetNextToken();
224						if (sourcehash.IsEmpty()) {
225							throw wxString( wxT("Empty sourcehash conflicts with cryptoptions flag 0x80" ) );
226						}
227					}
228				}
229
230				SED2KLinkSource entry = { addr, port, sourcehash, cryptoptions };
231
232				m_sources.push_back(entry);
233			}
234		} else if (field.StartsWith(wxT("p="))) {
235			wxStringTokenizer hashTokens(field.AfterFirst(wxT('=')), wxT(":"), wxTOKEN_RET_EMPTY_ALL);
236
237			m_hashset = new CMemFile();
238			m_hashset->WriteHash(m_hash);
239			m_hashset->WriteUInt16(0);
240
241			while (hashTokens.HasMoreTokens()) {
242				CMD4Hash hash;
243				if (!hash.Decode(hashTokens.GetNextToken().Strip(wxString::both))) {
244					throw wxString(wxT("Invalid hash in part-hashes list"));
245				}
246
247				m_hashset->WriteHash(hash);
248			}
249
250			unsigned count = m_hashset->GetLength() / 16u - 1u;
251
252			if (count) {
253				m_hashset->Seek( 16, wxFromStart);
254				m_hashset->WriteUInt16( count );
255				m_hashset->Seek( 0, wxFromStart);
256			} else {
257				delete m_hashset;
258				m_hashset = NULL;
259			}
260		} else if (field.StartsWith(wxT("h="))) {
261			wxString hash = field.AfterFirst(wxT('=')).MakeUpper();
262
263			size_t decodedSize = DecodeBase32(hash, CAICHHash::GetHashSize(), m_AICHHash.GetRawHash());
264			if ((decodedSize != CAICHHash::GetHashSize()) || m_AICHHash.GetString() != hash) {
265				throw wxString(wxT("Invalid master-hash"));
266			}
267
268			m_bAICHHashValid = true;
269		}
270	}
271}
272
273
274CED2KFileLink::~CED2KFileLink()
275{
276	delete m_hashset;
277	m_hashset =  NULL;
278}
279
280
281wxString CED2KFileLink::GetLink() const
282{
283	return CFormat(wxT("ed2k://|file|%s|%u|%s|/")) % m_name % m_size % m_hash.Encode();
284}
285
286
287wxString CED2KFileLink::GetName() const
288{
289	return m_name;
290}
291
292
293uint64 CED2KFileLink::GetSize() const
294{
295	return m_size;
296}
297
298
299const CMD4Hash& CED2KFileLink::GetHashKey() const
300{
301	return m_hash;
302}
303
304
305bool CED2KFileLink::HasValidAICHHash() const
306{
307	return m_bAICHHashValid;
308}
309
310
311const CAICHHash& CED2KFileLink::GetAICHHash() const
312{
313	return m_AICHHash;
314}
315// File_checked_for_headers
316