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 
CED2KLink(LinkType type)41 CED2KLink::CED2KLink( LinkType type )
42 	: m_type( type )
43 {
44 }
45 
46 
~CED2KLink()47 CED2KLink::~CED2KLink()
48 {
49 }
50 
51 
GetKind() const52 CED2KLink::LinkType CED2KLink::GetKind() const
53 {
54 	return m_type;
55 }
56 
57 
CreateLinkFromUrl(const wxString & link)58 CED2KLink* 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 /////////////////////////////////////////////
CED2KServerListLink(const wxString & link)85 CED2KServerListLink::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 
GetLink() const97 wxString CED2KServerListLink::GetLink() const
98 {
99 	return wxT("ed2k://|serverlist|") + m_address + wxT("|/");
100 }
101 
102 
GetAddress() const103 const wxString& CED2KServerListLink::GetAddress() const
104 {
105 	return m_address;
106 }
107 
108 
109 /////////////////////////////////////////////
110 // CED2KServerLink implementation
111 /////////////////////////////////////////////
CED2KServerLink(const wxString & link)112 CED2KServerLink::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 
GetLink() const133 wxString CED2KServerLink::GetLink() const
134 {
135 	return wxString(wxT("ed2k://|server|")) << Uint32toStringIP(m_ip) << wxT("|") << m_port << wxT("|/");
136 }
137 
138 
GetIP() const139 uint32 CED2KServerLink::GetIP() const
140 {
141 	return m_ip;
142 }
143 
144 
GetPort() const145 uint16 CED2KServerLink::GetPort() const
146 {
147 	return m_port;
148 }
149 
150 
151 /////////////////////////////////////////////
152 // CED2KFileLink implementation
153 /////////////////////////////////////////////
CED2KFileLink(const wxString & link)154 CED2KFileLink::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, (uint16) 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 
~CED2KFileLink()274 CED2KFileLink::~CED2KFileLink()
275 {
276 	delete m_hashset;
277 	m_hashset =  NULL;
278 }
279 
280 
GetLink() const281 wxString CED2KFileLink::GetLink() const
282 {
283 	return CFormat(wxT("ed2k://|file|%s|%u|%s|/")) % m_name % m_size % m_hash.Encode();
284 }
285 
286 
GetName() const287 wxString CED2KFileLink::GetName() const
288 {
289 	return m_name;
290 }
291 
292 
GetSize() const293 uint64 CED2KFileLink::GetSize() const
294 {
295 	return m_size;
296 }
297 
298 
GetHashKey() const299 const CMD4Hash& CED2KFileLink::GetHashKey() const
300 {
301 	return m_hash;
302 }
303 
304 
HasValidAICHHash() const305 bool CED2KFileLink::HasValidAICHHash() const
306 {
307 	return m_bAICHHashValid;
308 }
309 
310 
GetAICHHash() const311 const CAICHHash& CED2KFileLink::GetAICHHash() const
312 {
313 	return m_AICHHash;
314 }
315 // File_checked_for_headers
316