Bug Summary

File:rootdir/src/ED2KLinkParser.cpp
Warning:line 117, column 9
The parameter must not be null

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name ED2KLinkParser.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/rootdir/src -fcoverage-compilation-dir=/rootdir/src -resource-dir /usr/lib/llvm-19/lib/clang/19 -D HAVE_CONFIG_H -I . -I .. -D USE_WX_EXTENSIONS -D USE_STD_STRING -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-register -fdeprecated-macro -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcxx-exceptions -fexceptions -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /rootdir/html-report/2025-01-17-081402-14897-1 -x c++ ED2KLinkParser.cpp
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) 2003-2011 Alo Sarv ( madcat_@users.sourceforge.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
26const int versionMajor = 1;
27const int versionMinor = 5;
28const int versionRevision = 1;
29
30#include <cstdlib>
31#include <sstream>
32#include <iostream>
33#include <fstream>
34
35#ifdef __APPLE__
36 #include <CoreServices/CoreServices.h>
37#elif defined(_WIN32)
38 #include <winerror.h>
39 #include <shlobj.h>
40 #include <shlwapi.h>
41#endif
42
43#include "FileLock.h"
44#include "MagnetURI.h"
45#include "MuleCollection.h"
46
47using std::string;
48
49static string GetLinksFilePath(const string& configDir)
50{
51 if (!configDir.empty()) {
18
Assuming the condition is false
19
Taking false branch
52#ifdef _WIN32
53 char buffer[MAX_PATH + 1];
54 configDir.copy(buffer, MAX_PATH);
55 if (PathAppendA(buffer, "ED2KLinks")) {
56 string strDir;
57 strDir.assign(buffer);
58 return strDir;
59 }
60#else
61 string strDir = configDir;
62 if (strDir.at(strDir.length() - 1) != '/') {
63 strDir += '/';
64 }
65 return strDir + "ED2KLinks";
66#endif
67 }
68
69
70#ifdef __APPLE__
71
72 std::string strDir;
73
74 FSRef fsRef;
75 if (FSFindFolder(kUserDomain, kApplicationSupportFolderType, kCreateFolder, &fsRef) == noErr) {
76 CFURLRef urlRef = CFURLCreateFromFSRef(NULL__null, &fsRef);
77 if (urlRef != NULL__null) {
78 UInt8 buffer[PATH_MAX + 1];
79 if (CFURLGetFileSystemRepresentation(urlRef, true, buffer, sizeof(buffer))) {
80 strDir.assign((char*) buffer);
81 }
82 CFRelease(urlRef) ;
83 }
84 }
85
86 return strDir + "/aMule/ED2KLinks";
87
88#elif defined(_WIN32)
89
90 std::string strDir;
91 LPITEMIDLIST pidl;
92
93 HRESULT hr = SHGetSpecialFolderLocation(NULL__null, CSIDL_APPDATA, &pidl);
94
95 if (SUCCEEDED(hr)) {
96 char buffer[MAX_PATH + 1];
97 if (SHGetPathFromIDListA(pidl, buffer)) {
98 if (PathAppendA(buffer, "aMule\\ED2KLinks")) {
99 strDir.assign(buffer);
100 }
101 }
102 }
103
104 if (pidl) {
105 LPMALLOC pMalloc;
106 SHGetMalloc(&pMalloc);
107 if (pMalloc) {
108 pMalloc->Free(pidl);
109 pMalloc->Release();
110 }
111 }
112
113 return strDir;
114
115#else
116
117 return string( getenv("HOME") ) + "/.aMule/ED2KLinks";
20
The parameter must not be null
118
119#endif
120}
121
122/**
123 * Converts a hexadecimal number to a char.
124 *
125 * @param hex The hex-number, must be at most 2 digits long.
126 * @return The resulting char or \0 if conversion failed.
127 */
128static char HexToDec( const string& hex )
129{
130 char result = 0;
131
132 for ( size_t i = 0; i < hex.length(); ++i ) {
133 char cur = toupper( hex.at(i) );
134 result *= 16;
135
136 if ( isdigit( cur ) ) {
137 result += cur - '0';
138 } else if ( cur >= 'A' && cur <= 'F' ) {
139 result += cur - 'A' + 10;
140 } else {
141 return '\0';
142 }
143 }
144
145 return result;
146}
147
148
149/**
150 * This function converts all valid HTML escape-codes to their corresponding chars.
151 *
152 * @param str The string to unescape.
153 * @return The unescaped version of the input string.
154 */
155static string Unescape( const string& str )
156{
157 string result;
158 result.reserve( str.length() );
159
160 for ( size_t i = 0; i < str.length(); ++i ) {
161 if ( str.at(i) == '%' && ( i + 2 < str.length() ) ) {
162 char unesc = HexToDec( str.substr( i + 1, 2 ) );
163
164 if ( unesc ) {
165 i += 2;
166
167 result += unesc;
168 } else {
169 // If conversion failed, then we just add the escape-code
170 // and continue past it like nothing happened.
171 result += str.at(i);
172 }
173 } else {
174 result += str.at(i);
175 }
176 }
177
178 return result;
179}
180
181
182/**
183 * Returns the string with whitespace stripped from both ends.
184 */
185static string strip( const string& str )
186{
187 size_t first = 0;
188 size_t last = str.length() - 1;
189
190 // A simple but no very optimized way to narrow down the
191 // usable text within the string.
192 while ( first <= last ) {
193 if ( isspace( str.at(first) ) ) {
194 first++;
195 } else if ( isspace( str.at(last) ) ) {
196 last--;
197 } else {
198 break;
199 }
200 };
201
202 return str.substr( first, 1 + last - first );
203}
204
205
206/**
207 * Returns true if the string is a valid number.
208 */
209static bool isNumber( const string& str )
210{
211 for ( size_t i = 0; i < str.length(); i++ ) {
212 if ( !isdigit( str.at(i) ) ) {
213 return false;
214 }
215 }
216
217 return str.length() > 0;
218}
219
220
221/**
222 * Returns true if the string is a valid Base16 representation of a MD4 Hash.
223 */
224static bool isMD4Hash( const string& str )
225{
226 for ( size_t i = 0; i < str.length(); i++ ) {
227 const char c = toupper( str.at(i) );
228
229 if ( !isdigit( c ) && ( c < 'A' || c > 'F' ) ) {
230 return false;
231 }
232 }
233
234 return str.length() == 32;
235}
236
237
238/**
239 * Returns a description of the current version of "ed2k".
240 */
241static string getVersion()
242{
243 std::ostringstream v;
244
245 v << "aMule ED2k link parser v"
246 << versionMajor << "."
247 << versionMinor << "."
248 << versionRevision;
249
250 return v.str();
251}
252
253
254/**
255 * Helper-function for printing link-errors.
256 */
257static void badLink( const string& type, const string& err, const string& uri )
258{
259 std::cout << "Invalid " << type << "-link, " + err << ":\n"
260 << "\t" << uri << std::endl;
261}
262
263
264/**
265 * Writes a string to the ED2KLinks file.
266 *
267 * If errors are detected, it will terminate the program.
268 */
269static void writeLink( const string& uri, const string& config_dir )
270{
271 // Attempt to lock the ED2KLinks file
272 static CFileLock lock(GetLinksFilePath(config_dir));
17
Calling 'GetLinksFilePath'
273 static std::ofstream file;
274
275 if (!file.is_open()) {
276 string path = GetLinksFilePath(config_dir);
277 file.open( path.c_str(), std::ofstream::out | std::ofstream::app );
278
279 if (!file.is_open()) {
280 std::cout << "ERROR! Failed to open " << path << " for writing!" << std::endl;
281 exit(1);
282 }
283 }
284
285 file << uri << std::endl;
286
287 std::cout << "Link successfully queued." << std::endl;
288}
289
290
291/**
292 * Writes the the specified URI to the ED2KLinks file if it is a valid file-link.
293 *
294 * @param uri The URI to check.
295 * @return True if the URI was written, false otherwise.
296 */
297static bool checkFileLink( const string& uri )
298{
299 if ( uri.substr( 0, 13 ) == "ed2k://|file|" ) {
300 size_t base_off = 12;
301 size_t name_off = uri.find( '|', base_off + 1 );
302 size_t size_off = uri.find( '|', name_off + 1 );
303 size_t hash_off = uri.find( '|', size_off + 1 );
304
305 bool valid = true;
306 valid &= ( base_off < name_off );
307 valid &= ( name_off < size_off );
308 valid &= ( size_off < hash_off );
309 valid &= ( hash_off != string::npos );
310
311 if ( !valid ) {
312 badLink( "file", "invalid link format", uri );
313 return false;
314 }
315
316 string name = uri.substr( base_off + 1, name_off - base_off - 1 );
317 string size = uri.substr( name_off + 1, size_off - name_off - 1 );
318 string hash = uri.substr( size_off + 1, hash_off - size_off - 1 );
319
320 if ( name.empty() ) {
321 badLink( "file", "no name specified", uri );
322 return false;
323 }
324
325 if ( !isNumber( size ) ) {
326 badLink( "file", "invalid size", uri );
327 return false;
328 }
329
330 if ( !isMD4Hash( hash ) ) {
331 badLink( "file", "invalid MD4 hash", uri );
332 return false;
333 }
334
335 return true;
336 }
337
338 return false;
339}
340
341
342/**
343 * Writes the the specified URI to the ED2KLinks file if it is a valid server-link.
344 *
345 * @param uri The URI to check.
346 * @return True if the URI was written, false otherwise.
347 */
348static bool checkServerLink( const string& uri )
349{
350 if ( uri.substr( 0, 15 ) == "ed2k://|server|" ) {
351 size_t base_off = 14;
352 size_t host_off = uri.find( '|', base_off + 1 );
353 size_t port_off = uri.find( '|', host_off + 1 );
354
355 bool valid = true;
356 valid &= ( base_off < host_off );
357 valid &= ( host_off < port_off );
358 valid &= ( port_off != string::npos );
359
360 if ( !valid || uri.at( port_off + 1 ) != '/' ) {
361 badLink( "server", "invalid link format", uri );
362 return false;
363 }
364
365 string host = uri.substr( base_off + 1, host_off - base_off - 1 );
366 string port = uri.substr( host_off + 1, port_off - host_off - 1 );
367
368 if ( host.empty() ) {
369 badLink( "server", "no hostname specified", uri );
370 return false;
371 }
372
373 if ( !isNumber( port ) ) {
374 badLink( "server", "invalid port", uri );
375 return false;
376 }
377
378 return true;
379 }
380
381 return false;
382}
383
384
385/**
386 * Writes the the specified URI to the ED2KLinks file if it is a valid serverlist-link.
387 *
388 * @param uri The URI to check.
389 * @return True if the URI was written, false otherwise.
390 */
391static bool checkServerListLink( const string& uri )
392{
393 if ( uri.substr( 0, 19 ) == "ed2k://|serverlist|" ) {
394 size_t base_off = 19;
395 size_t path_off = uri.find( '|', base_off + 1 );
396
397 bool valid = true;
398 valid &= ( base_off < path_off );
399 valid &= ( path_off != string::npos );
400
401 if ( !valid ) {
402 badLink( "serverlist", "invalid link format", uri );
403 return false;
404 }
405
406 string path = uri.substr( base_off + 1, path_off - base_off - 1 );
407
408 if ( path.empty() ) {
409 badLink( "serverlist", "no hostname specified", uri );
410 return false;
411 }
412
413 return true;
414 }
415
416 return false;
417}
418
419
420int main(int argc, char *argv[])
421{
422 bool errors = false;
423 string config_path;
424 string category = "";
425 for ( int i = 1; i < argc; i++ ) {
1
Assuming 'i' is < 'argc'
2
Loop condition is true. Entering loop body
8
Assuming 'i' is < 'argc'
9
Loop condition is true. Entering loop body
426 string arg = strip( Unescape( string( argv[i] ) ) );
427
428 if ( arg.compare(0, 7, "magnet:" ) == 0 ) {
3
Assuming the condition is false
4
Taking false branch
10
Assuming the condition is false
11
Taking false branch
429 string ed2k = CMagnetED2KConverter(arg);
430 if ( ed2k.empty() ) {
431 std::cerr << "Cannot convert magnet URI to ed2k:\n\t" << arg << std::endl;
432 errors = true;
433 continue;
434 } else {
435 arg = ed2k;
436 }
437 }
438
439 if ( arg.substr( 0, 8 ) == "ed2k://|" ) {
12
Taking true branch
440 // Ensure the URI is valid
441 if ( arg.at( arg.length() - 1 ) != '/' ) {
13
Assuming the condition is true
14
Taking true branch
442 arg += '/';
443 }
444
445 string type = arg.substr( 8, arg.find( '|', 9 ) - 8 );
446
447 if ( (type == "file") && checkFileLink( arg ) ) {
15
Taking true branch
448 arg += category;
449 writeLink( arg, config_path );
16
Calling 'writeLink'
450 } else if ( (type == "server") && checkServerLink( arg ) ) {
451 writeLink( arg, config_path );
452 } else if ( (type == "serverlist") && checkServerListLink( arg ) ) {
453 writeLink( arg, config_path );
454 } else {
455 std::cout << "Unknown or invalid link-type:\n\t" << arg << std::endl;
456 errors = true;
457 }
458 } else if (arg == "-c" || arg == "--config-dir") {
5
Taking false branch
459 if (i < argc - 1) {
460 config_path = argv[++i];
461 } else {
462 std::cerr << "Missing mandatory argument for " << arg << std::endl;
463 errors = true;
464 }
465 } else if (arg.substr(0, 2) == "-c") {
6
Taking false branch
466 config_path = arg.substr(2);
467 } else if (arg.substr(0, 13) == "--config-dir=") {
468 config_path = arg.substr(13);
469 } else if (arg == "-h" || arg == "--help") {
470 std::cout << getVersion()
471 << "\n\n"
472 << "Usage:\n"
473 << " --help, -h Prints this help.\n"
474 << " --config-dir, -c Specifies the aMule configuration directory.\n"
475 << " --version, -v Displays version info.\n\n"
476 << " --category, -t Add Link to category number.\n"
477 << " magnet:? Causes the file to be queued for download.\n"
478 << " ed2k://|file| Causes the file to be queued for download.\n"
479 << " ed2k://|server| Causes the server to be listed or updated.\n"
480 << " ed2k://|serverlist| Causes aMule to update the current serverlist.\n\n"
481 << " --list, -l Show all links of an emulecollection\n"
482 << " --emulecollection, -e Loads all links of an emulecollection\n\n"
483 << "*** NOTE: Option order is important! ***\n"
484 << std::endl;
485
486 } else if (arg == "-v" || arg == "--version") {
487 std::cout << getVersion() << std::endl;
488 } else if (arg == "-t" || arg == "--category") {
489 if (i < argc - 1) {
490 if ((category == "" ) && (0 != atoi(argv[++i]))) {
491 category = ':';
492 category += argv[i];
493 }
494 } else {
495 std::cerr << "Missing mandatory argument for " << arg << std::endl;
496 errors = true;
497 }
498 } else if (arg == "-e" || arg == "--emulecollection" || arg == "-l" || arg == "--list") {
7
Taking false branch
499 bool listOnly = (arg == "-l" || arg == "--list");
500 if (i < argc - 1) {
501 CMuleCollection my_collection;
502 if (my_collection.Open( /* emulecollection file */ argv[++i] ))
503 {
504 for(size_t e = 0; e < my_collection.size(); e++)
505 if (listOnly)
506 std::cout << my_collection[e] << std::endl;
507 else
508 writeLink( my_collection[e], config_path );
509 } else {
510 std::cerr << "Invalid emulecollection file: " << argv[i] << std::endl;
511 errors = true;
512 }
513 } else {
514 std::cerr << "Missing mandatory argument for " << arg << std::endl;
515 errors = true;
516 }
517 } else {
518 std::cerr << "Bad parameter value:\n\t" << arg << "\n" << std::endl;
519 errors = true;
520 }
521 }
522
523 return ( errors ? 1 : 0 );
524}
525
526// File_checked_for_headers