1 /* (PD) 2004 The Bitzi Corporation
2 * Please see file COPYING or http://bitzi.com/publicdomain
3 * for more info.
4 *
5 * $Id: main.c,v 1.49 2004/02/03 02:47:34 gojomo Exp $
6 */
7 /*------------------------------------------------------------------------- */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <ctype.h>
12
13 #include "bc_version.h"
14 #include "bitcollider.h"
15 #include "cache.h"
16 #include "bitprint.h"
17 #include "md5.h"
18 #include "ed2k_md4.h"
19 #include "ftuuhash.h"
20 #include "kztree.h"
21 #include "mp3.h"
22 #include "id3.h"
23 #include "plugin_man.h"
24 #include "tiger.h"
25 #include <sys/stat.h>
26 #ifndef _WIN32
27 #include "browser.h"
28 #include <errno.h>
29 #include <unistd.h>
30 #else
31 #include <windows.h>
32 #include <shellapi.h>
33 #endif
34
35 /*------------------------------------------------------------------------- */
36
37 #define BUFFER_LEN 4096
38 #define FIRST_N_HEX 20
39 #define FIRST_N_HEX_SIZE (FIRST_N_HEX * sizeof(char) * 2)
40 #define MAX_ATTR_STRING_LEN 1024
41 #define DEFAULT_NUM_ATTRS 16
42 #define GROW_NUM_ATTRS 16
43 #define MD5_SANITY_CHECK_FAILED "The MD5 hash function compiled into the bitcollider is faulty."
44 #define MD5_SANITY_CHECK_EMPTY "2QOYZWMPACZAJ2MABGMOZ6CCPY"
45 #define MD5_SANITY_CHECK_01234 "IEAMJVCNVELXER7EJJP4CVDHPA"
46
47 #ifdef _WIN32
48 #define DIR_SEP '\\'
49 #else
50 #define DIR_SEP '/'
51 #endif
52 #define DB printf("%s:%d\n", __FILE__, __LINE__);
53
54 /*------------------------------------------------------------------------- */
55 #define ERROR_FILENOTFOUND "File not found or permission denied."
56 #define ERROR_MALLOCFAILED "Failed to allocate memory."
57 #define ERROR_LAUNCHBROWSER "Cannot launch web browser."
58 #define ERROR_TEMPFILEERR "Cannot create a temorary file for the " \
59 "bitprint submission."
60 #define ERROR_HASHCHECK "The hash functions compiled into this version " \
61 "of the bitcollider utility are faulty!!!"
62 #define WARNING_NOTMP3 "This is not an MP3 file. " \
63 "Skipping mp3 information."
64
65 /*------------------------------------------------------------------------- */
66
67 b_bool calculate_hashes(BitcolliderSubmission *submission,
68 FILE *source,
69 char *bitprint,
70 char *crc32,
71 char *md5sum,
72 char *ed2kmd4sum,
73 char *ftuusum,
74 mp3_info *mp3Info,
75 PluginMethods *methods,
76 Attribute **attrList);
77 b_bool generate_first_n_hex(BitcolliderSubmission *submission,
78 FILE *source,
79 int n,
80 unsigned char *bitprint);
81 b_bool get_bitprint_data(BitcolliderSubmission *submission,
82 const char *fileName,
83 char *bitprint,
84 char *crc32,
85 char *md5sum,
86 char *ed2kmd4sum,
87 char *kzhashhex,
88 unsigned char *firstHex,
89 mp3_info *mp3Info,
90 PluginMethods *methods,
91 Attribute **attrList);
92 void convert_to_multiple_submission(BitcolliderSubmission *submission);
93 void set_error(BitcolliderSubmission *sub, const char *newError);
94 void set_warning(BitcolliderSubmission *sub, const char *newError);
95 b_bool check_md5_hash(void);
96 char *escape_form_value(char *form_value);
97 /*------------------------------------------------------------------------- */
98
bitcollider_init(b_bool printDebugInfo)99 Bitcollider *bitcollider_init(b_bool printDebugInfo)
100 {
101 Bitcollider *bc;
102 #ifdef WIN32
103 HKEY hKey;
104 DWORD type;
105 #endif
106 char path[MAX_PATH], cacheFile[MAX_PATH], *ptr;
107 int total = 0;
108
109 cacheFile[0] = 0;
110 bc = init_plugins();
111
112 #ifndef WIN32
113 /* Load the plugins from the build dir first if ./plugins exists */
114 if (printDebugInfo)
115 fprintf(stderr, "Loading plugins from ./plugins:\n");
116
117 total = load_plugins(bc, "./plugins", printDebugInfo);
118
119 ptr = getenv("HOME");
120 if (ptr)
121 {
122 sprintf(cacheFile, "%s/.bitcollider/cache.db", ptr);
123 sprintf(path, "%s/.bitcollider/plugins", ptr);
124 if (printDebugInfo)
125 fprintf(stderr, "Loading plugins from %s:\n", path);
126 total += load_plugins(bc, path, printDebugInfo);
127 }
128 else
129 {
130 if (printDebugInfo)
131 fprintf(stderr, "HOME env var not set. Cannot find home.\n");
132 }
133
134 ptr = PREFIX"/lib/bitcollider/plugins";
135 #else
136
137 path[0] = 0;
138 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Bitzi", 0,
139 KEY_READ, &hKey) != ERROR_SUCCESS)
140 {
141 /* No registry key set. Figure out where our plugin dir is and
142 set that key to the registry. */
143 set_plugin_dir(path);
144 /*printf("No reg key set. path: '%s'\n", path);*/
145 }
146 else
147 {
148 int ret;
149 DWORD size = MAX_PATH;
150
151 // Get the plugin dir, first
152 ret = RegQueryValueEx(hKey, "PluginDir", NULL, &type,
153 path, &size);
154
155 if (ret == ERROR_FILE_NOT_FOUND)
156 set_plugin_dir(path);
157 else
158 if (ret != ERROR_SUCCESS)
159 {
160 RegCloseKey(hKey);
161 return bc;
162 }
163
164 // Now try to get the cache file
165 size = MAX_PATH;
166 ret = RegQueryValueEx(hKey, "CacheFile", NULL, &type,
167 cacheFile, &size);
168 RegCloseKey(hKey);
169 }
170 ptr = path;
171 #endif
172
173 if (ptr)
174 {
175 if (printDebugInfo)
176 fprintf(stderr, "Loading plugins from %s:\n", ptr);
177 total += load_plugins(bc, ptr, printDebugInfo);
178 }
179 if (printDebugInfo)
180 fprintf(stderr, "Loaded %d plugins total.\n\n", total);
181
182 #if USE_BDB
183 bc->maxCacheSize = DEFAULT_MAX_CACHE_SIZE;
184 if (cacheFile[0] != 0)
185 {
186 bc->cache = init_cache();
187 if (bc->cache)
188 {
189 if (!open_cache(bc->cache, cacheFile))
190 {
191 if (printDebugInfo)
192 fprintf(stderr, "Failed to open cache file: %s. "
193 "Creating a new one.\n\n", cacheFile);
194 unlink(cacheFile);
195 if (!create_cache(bc->cache, cacheFile, bc->maxCacheSize))
196 {
197 if (printDebugInfo)
198 fprintf(stderr, "Failed to create cache file: %s.\n\n",
199 cacheFile);
200 close_cache(bc->cache);
201 bc->cache = NULL;
202 }
203 }
204 }
205 }
206 #endif
207
208 return bc;
209 }
210
bitcollider_shutdown(Bitcollider * bc)211 void bitcollider_shutdown(Bitcollider *bc)
212 {
213 #if USE_BDB
214 if (bc->cache)
215 close_cache(bc->cache);
216 #endif
217 unload_plugins(bc);
218 shutdown_plugins(bc);
219 }
220
create_submission(Bitcollider * bc)221 BitcolliderSubmission *create_submission(Bitcollider *bc)
222 {
223 BitcolliderSubmission *submission;
224
225 submission = (BitcolliderSubmission *)malloc(sizeof(BitcolliderSubmission));
226 if (submission == NULL)
227 return NULL;
228
229 memset(submission, 0, sizeof(BitcolliderSubmission));
230 submission->bc = bc;
231
232 return submission;
233 }
234
analyze_file(BitcolliderSubmission * submission,const char * fileName,b_bool matchingExtsOnly)235 b_bool analyze_file(BitcolliderSubmission *submission,
236 const char *fileName,
237 b_bool matchingExtsOnly)
238 {
239 char bitprint[BITPRINT_BASE32_LEN + 1];
240 char firstNHex[FIRST_N_HEX_SIZE + 1];
241 char temp[MAX_ATTR_STRING_LEN], *ext;
242 char crc32[10];
243 char md5[64];
244 char ed2kmd4[64];
245 char kzhashhex[128];
246 const char *baseFileName;
247 mp3_info *mp3Info = NULL;
248 PluginMethods *methods = NULL;
249 b_bool mp3Check = false;
250 Attribute *attrList = NULL, *attr;
251 const char *err;
252 #if USE_BDB
253 cache_entry centry;
254 struct stat fileInfo;
255 #endif
256
257 if (submission->bc->error)
258 {
259 free(submission->bc->error);
260 submission->bc->error = NULL;
261 }
262 if (submission->bc->warning)
263 {
264 free(submission->bc->warning);
265 submission->bc->warning = NULL;
266 }
267
268 if (submission->fileName)
269 {
270 free(submission->fileName);
271 submission->fileName = NULL;
272 }
273
274 if (!check_md5_hash())
275 {
276 set_error(submission, MD5_SANITY_CHECK_FAILED);
277 return false;
278 }
279
280 if (submission->bc->exitNow)
281 {
282 return false;
283 }
284
285 submission->fileName = strdup(fileName);
286 baseFileName = strrchr(fileName, DIR_SEP);
287 if (baseFileName)
288 baseFileName++;
289 else
290 baseFileName = fileName;
291
292 ext = strrchr(baseFileName, '.');
293 if (!submission->checkAsExt && ext && strcasecmp(ext, ".mp3") == 0)
294 {
295 mp3Check = true;
296 }
297
298 /* Load the plugin methods for this file */
299 ext = (submission->checkAsExt) ? submission->checkAsExt : ext;
300 if (ext)
301 methods = get_plugin(submission->bc, ext);
302
303 /* If we're only supposed to work on files with known extensions,
304 and we don't know this extension, bail. */
305 if (matchingExtsOnly && methods == NULL && mp3Check == false)
306 {
307 if (submission->bc->progressCallback && !submission->bc->preview)
308 submission->bc->progressCallback(0, submission->fileName,
309 "skipped.");
310 return false;
311 }
312
313 /* If we're in preview mode, return now */
314 if (submission->bc->preview)
315 return true;
316
317 #if USE_BDB
318 /* Check to see if this bitprint has already been bitprinted */
319 #ifdef WIN32
320 {
321 char *dummy;
322 if (GetFullPathName(fileName, MAX_PATH, centry.fileName, &dummy) == 0)
323 strcpy(centry.fileName, fileName);
324 }
325 #else
326 if (realpath(fileName, centry.fileName) == NULL)
327 strcpy(centry.fileName, fileName);
328 #endif
329
330 if (submission->bc->cache &&
331 get_cache_entry(submission->bc->cache, ¢ry))
332 {
333 if (_stat(submission->fileName, (struct _stat *)&fileInfo) == 0)
334 {
335 if (fileInfo.st_mtime == centry.lastModDate)
336 {
337 if (submission->bc->progressCallback &&
338 !submission->bc->preview)
339 submission->bc->progressCallback(0, submission->fileName,
340 "found in cache, skipped.");
341 return true;
342 }
343 }
344 /* Its out of date or something else is wrong. Remove the entry
345 from the cache */
346 remove_cache_entry(submission->bc->cache, ¢ry);
347 }
348 #endif
349
350 if (mp3Check)
351 mp3Info = malloc(sizeof(mp3_info));
352
353 if (!get_bitprint_data(submission, fileName, bitprint, crc32, md5, ed2kmd4, kzhashhex, firstNHex,
354 mp3Info, methods, &attrList))
355 {
356 if (mp3Info)
357 free(mp3Info);
358
359 return false;
360 }
361
362 /* If this is the first bit print, add a header to the attrs */
363 if (submission->numBitprints == 0)
364 {
365 get_agent_string(temp);
366 add_attribute(submission, "head.agent", temp);
367
368 sprintf(temp, "S%s", BC_SUBMITSPECVER);
369 add_attribute(submission, "head.version", temp);
370 }
371
372 /* If this is the second bitprint, convert the single submission
373 to a multiple submission */
374 if (submission->numBitprints == 1)
375 convert_to_multiple_submission(submission);
376
377 add_attribute(submission, "bitprint", bitprint);
378
379 sprintf(temp, "%lu", submission->fileSize);
380 add_attribute(submission, "tag.file.length", temp);
381 add_attribute(submission, "tag.file.first20", firstNHex);
382 add_attribute(submission, "tag.filename.filename", baseFileName);
383
384 if (submission->bc->calculateCRC32)
385 {
386 add_attribute(submission, "tag.crc32.crc32", crc32);
387 }
388 if (submission->bc->calculateMD5)
389 {
390 add_attribute(submission, "tag.md5.md5", md5);
391 }
392
393 add_attribute(submission, "tag.ed2k.ed2khash", ed2kmd4);
394 add_attribute(submission, "tag.kzhash.kzhash", kzhashhex);
395
396 /* Check to make sure that we carried out the mp3 check, and
397 make sure that an audioSha was generated. If not, then the
398 mp3 routines deemed that this was not a valid mp3 file
399 and we should skip the mp3 tag generation */
400 if (mp3Check && mp3Info->samplerate == 0)
401 {
402 set_warning(submission, WARNING_NOTMP3);
403 mp3Check = 0;
404 }
405 else
406 if (mp3Check)
407 {
408 char audioShaDigest[SHA_BASE32SIZE + 1];
409 ID3Info *info;
410
411 bitziEncodeBase32(mp3Info->audioSha, SHA_DIGESTSIZE, audioShaDigest);
412 sprintf(temp, "%d", mp3Info->duration);
413 add_attribute(submission, "tag.mp3.duration", temp);
414 if (mp3Info->bitrate == 0)
415 {
416 sprintf(temp, "%d", mp3Info->avgBitrate);
417 add_attribute(submission, "tag.mp3.bitrate", temp);
418 add_attribute(submission, "tag.mp3.vbr", "y");
419 }
420 else
421 {
422 sprintf(temp, "%d", mp3Info->bitrate);
423 add_attribute(submission, "tag.mp3.bitrate", temp);
424 }
425 sprintf(temp, "%d", mp3Info->samplerate);
426 add_attribute(submission, "tag.mp3.samplerate", temp);
427 add_attribute(submission, "tag.mp3.stereo",
428 mp3Info->stereo ? "y" : "n");
429 add_attribute(submission, "tag.mp3.audio_sha1", audioShaDigest);
430 free(mp3Info);
431
432 info = read_ID3_tag(fileName);
433 if (info)
434 {
435 if (info->encoder)
436 add_attribute(submission, "tag.mp3.encoder", info->encoder);
437 if (info->title)
438 add_attribute(submission, "tag.audiotrack.title", info->title);
439 if (info->artist)
440 add_attribute(submission, "tag.audiotrack.artist", info->artist);
441 if (info->album)
442 add_attribute(submission, "tag.audiotrack.album", info->album);
443 if (info->tracknumber)
444 add_attribute(submission, "tag.audiotrack.tracknumber",
445 info->tracknumber);
446 if (info->genre && atoi(info->genre) >= 0)
447 add_attribute(submission, "tag.id3genre.genre", info->genre);
448 if (info->year)
449 add_attribute(submission, "tag.audiotrack.year", info->year);
450 delete_ID3_tag(info);
451 }
452 }
453
454 /* If a plugin was selected, but no memory analyze functions were
455 provided, call the plugin's file analyze methods */
456 if (methods && methods->mem_analyze_init == NULL && !submission->bc->exitNow)
457 attrList = methods->file_analyze(fileName);
458
459 if (attrList)
460 {
461 for(attr = attrList; attr->key; attr++)
462 add_attribute(submission, attr->key, attr->value);
463
464 methods->free_attributes(attrList);
465 }
466
467 /* If we selected a plugin, but the no attributes were returned,
468 then check for an error from the plugin */
469 if (methods && !attrList)
470 {
471 /* An error from a plugin should be considered a warning
472 since its not fatal to the execution of the bitcollider */
473 err = methods->get_error();
474 if (err)
475 set_warning(submission, err);
476 }
477
478 if (submission->bc->progressCallback &&
479 !submission->bc->preview &&
480 !submission->bc->exitNow)
481 submission->bc->progressCallback(100, NULL, "ok.");
482
483 submission->numBitprints++;
484
485 #if USE_BDB
486 if (_stat(submission->fileName, (struct _stat *)&fileInfo) == 0)
487 {
488 centry.lastModDate = fileInfo.st_mtime;
489 if (submission->bc->cache &&
490 !add_cache_entry(submission->bc->cache, ¢ry, false))
491 {
492 printf("Failed to insert bitprint into cache.\n");
493 }
494 }
495 #endif
496
497 return true;
498 }
499
set_auto_submit(BitcolliderSubmission * tag,b_bool autoSubmit)500 void set_auto_submit(BitcolliderSubmission *tag,
501 b_bool autoSubmit)
502 {
503 tag->autoSubmit = autoSubmit;
504 }
505
set_check_as(BitcolliderSubmission * tag,const char * extension)506 void set_check_as(BitcolliderSubmission *tag,
507 const char *extension)
508 {
509 if (tag->checkAsExt)
510 free(tag->checkAsExt);
511
512 tag->checkAsExt = strdup(extension);
513 }
514
set_progress_callback(Bitcollider * bc,void (* callback)(int,const char *,const char *))515 void set_progress_callback(Bitcollider *bc,
516 void (*callback)(int, const char *, const char *))
517 {
518 bc->progressCallback = callback;
519 }
520
set_preview(Bitcollider * bc,b_bool preview)521 void set_preview(Bitcollider *bc, b_bool preview)
522 {
523 bc->preview = preview;
524 }
525
set_calculateCRC32(Bitcollider * bc,b_bool calculateCRC32)526 void set_calculateCRC32(Bitcollider *bc, b_bool calculateCRC32)
527 {
528 bc->calculateCRC32 = calculateCRC32;
529 }
530
set_calculateMD5(Bitcollider * bc,b_bool calculateMD5)531 void set_calculateMD5(Bitcollider *bc, b_bool calculateMD5)
532 {
533 bc->calculateMD5 = calculateMD5;
534 }
535
set_exit(Bitcollider * bc,b_bool exitNow)536 void set_exit(Bitcollider *bc, b_bool exitNow)
537 {
538 if (bc->progressCallback)
539 bc->progressCallback(-2, NULL, "operation cancelled.");
540 bc->exitNow = exitNow;
541 }
542
clear_bitprint_cache(Bitcollider * bc)543 void clear_bitprint_cache(Bitcollider *bc)
544 {
545 #if USE_BDB
546 if (bc->cache)
547 {
548 int maxCount;
549 char fileName[MAX_PATH];
550
551 strcpy(fileName, bc->cache->fileName);
552 maxCount = get_max_count(bc->cache);
553 close_cache(bc->cache);
554 unlink(fileName);
555
556 bc->cache = init_cache();
557 create_cache(bc->cache, fileName, maxCount);
558 }
559 #endif
560 }
561
562
get_num_bitprints(BitcolliderSubmission * sub)563 int get_num_bitprints(BitcolliderSubmission *sub)
564 {
565 return sub->numBitprints;
566 }
567
get_error(Bitcollider * bc)568 char *get_error(Bitcollider *bc)
569 {
570 return bc->error;
571 }
572
set_error(BitcolliderSubmission * sub,const char * newError)573 void set_error(BitcolliderSubmission *sub, const char *newError)
574 {
575 if (sub->bc->error)
576 free(sub->bc->error);
577 sub->bc->error = strdup(newError);
578 }
579
get_warning(Bitcollider * bc)580 char *get_warning(Bitcollider *bc)
581 {
582 return bc->warning;
583 }
584
set_warning(BitcolliderSubmission * sub,const char * newWarning)585 void set_warning(BitcolliderSubmission *sub, const char *newWarning)
586 {
587 if (sub->bc->warning)
588 free(sub->bc->warning);
589 sub->bc->warning = strdup(newWarning);
590 }
591
delete_submission(BitcolliderSubmission * submission)592 void delete_submission(BitcolliderSubmission *submission)
593 {
594 int i;
595
596 for(i = 0; i < submission->numItems; i++)
597 {
598 free(submission->attrList[i]->key);
599 free(submission->attrList[i]->value);
600 free(submission->attrList[i]);
601 }
602
603 if (submission->fileName)
604 free(submission->fileName);
605 free(submission->attrList);
606 free(submission);
607 }
608
convert_to_multiple_submission(BitcolliderSubmission * submission)609 void convert_to_multiple_submission(BitcolliderSubmission *submission)
610 {
611 int i;
612 char *temp;
613
614 for(i = 0; i < submission->numItems; i++)
615 {
616 if (strncmp("head.version", submission->attrList[i]->key, 12) == 0)
617 {
618 submission->attrList[i]->value[0] = 'M';
619 continue;
620 }
621 if (strncmp("head.", submission->attrList[i]->key, 5) == 0)
622 continue;
623
624 temp = malloc(strlen(submission->attrList[i]->key) + 3);
625 sprintf(temp, "0.%s", submission->attrList[i]->key);
626 free(submission->attrList[i]->key);
627 submission->attrList[i]->key = temp;
628 }
629 }
630
convert_to_hex(const unsigned char * buffer,int size,char * hexBuffer)631 void convert_to_hex(const unsigned char *buffer,
632 int size,
633 char *hexBuffer)
634 {
635 int i;
636
637 for(i = 0; i < size; i++)
638 {
639 sprintf(hexBuffer + (i * sizeof(char) * 2), "%02X", buffer[i] & 0xFF);
640 }
641 }
642
643
add_attribute(BitcolliderSubmission * submission,const char * key,const char * value)644 void add_attribute(BitcolliderSubmission *submission,
645 const char *key,
646 const char *value)
647 {
648 int i;
649 char *temp = NULL;
650
651 /* Check to see if the attr list already exists */
652 if (submission->attrList == NULL)
653 {
654 submission->attrList = (Attribute **)malloc(sizeof(Attribute *) *
655 DEFAULT_NUM_ATTRS);
656 memset(submission->attrList, 0, sizeof(submission->attrList));
657 submission->numItems = 0;
658 submission->numAllocated = DEFAULT_NUM_ATTRS;
659 }
660
661 /* Do we have enough space for another attr? If not, grow the array */
662 if (submission->numItems == submission->numAllocated)
663 {
664 submission->numAllocated += GROW_NUM_ATTRS;
665 submission->attrList = (Attribute **)realloc(submission->attrList,
666 sizeof(Attribute *) *
667 submission->numAllocated);
668 memset(&submission->attrList[submission->numItems],
669 0, sizeof(Attribute *) * GROW_NUM_ATTRS);
670 }
671
672 /* If this is a multiple tag submission, prepend the sequence number
673 to the key */
674 if (submission->numBitprints > 0)
675 {
676 temp = malloc(strlen(key) + 16);
677 sprintf(temp, "%d.%s", submission->numBitprints, key);
678 key = temp;
679 }
680
681 /* Check to make sure we don't already have this attr */
682 for(i = 0; i < submission->numItems; i++)
683 {
684 if (strcmp(key, submission->attrList[i]->key) == 0)
685 return;
686 }
687
688 submission->attrList[submission->numItems] =
689 (Attribute *)malloc(sizeof(Attribute));
690 submission->attrList[submission->numItems]->key = strdup(key);
691 submission->attrList[submission->numItems]->value = strdup(value);
692 submission->numItems++;
693
694 if (temp)
695 free(temp);
696 }
697
get_attribute(BitcolliderSubmission * submission,const char * key)698 const char *get_attribute(BitcolliderSubmission *submission,
699 const char *key)
700 {
701 int i;
702
703 for(i = 0; i < submission->numItems; i++)
704 {
705 if (strcmp(submission->attrList[i]->key, key) == 0)
706 return submission->attrList[i]->value;
707 }
708
709 return NULL;
710 }
711
get_bitprint_data(BitcolliderSubmission * submission,const char * fileName,char * bitprint,char * crc32hex,char * md5sum,char * ed2kmd4,char * kzhashhex,unsigned char * firstHex,mp3_info * mp3Info,PluginMethods * methods,Attribute ** attrList)712 b_bool get_bitprint_data(BitcolliderSubmission *submission,
713 const char *fileName,
714 char *bitprint,
715 char *crc32hex,
716 char *md5sum,
717 char *ed2kmd4,
718 char *kzhashhex,
719 unsigned char *firstHex,
720 mp3_info *mp3Info,
721 PluginMethods *methods,
722 Attribute **attrList)
723 {
724 FILE *source;
725 b_bool ret;
726
727 source = fopen(fileName, "rb");
728 if (source == NULL)
729 {
730 set_error(submission, ERROR_FILENOTFOUND);
731 return false;
732 }
733
734 fseek(source, 0, SEEK_END);
735 submission->fileSize = ftell(source);
736 fseek(source, 0, SEEK_SET);
737
738 ret = calculate_hashes(submission, source, bitprint, crc32hex, md5sum, ed2kmd4, kzhashhex,
739 mp3Info, methods, attrList);
740 if (ret)
741 ret = generate_first_n_hex(submission, source, FIRST_N_HEX, firstHex);
742
743 fclose(source);
744 return ret;
745 }
746
calculate_hashes(BitcolliderSubmission * submission,FILE * source,char * bitprint,char * crc32hex,char * md5sum,char * ed2kmd4sum,char * kzhashsum,mp3_info * mcontext,PluginMethods * methods,Attribute ** attrList)747 b_bool calculate_hashes(BitcolliderSubmission *submission,
748 FILE *source,
749 char *bitprint,
750 char *crc32hex,
751 char *md5sum,
752 char *ed2kmd4sum,
753 char *kzhashsum,
754 mp3_info *mcontext,
755 PluginMethods *methods,
756 Attribute **attrList)
757 {
758 BP_CONTEXT bcontext;
759 unsigned int crc32 = 0xffffffff;
760 struct MD5Context md5context;
761 ED2K_CTX ed2kmd4context;
762 FTUU_CTX ftuucontext;
763 KZTREE_CONTEXT kztreecontext;
764 unsigned char *buffer, bitprintRaw[BITPRINT_RAW_LEN], md5Digest[16], ed2kDigest[16], kzhash[36];
765 int bytes;
766 b_bool ret = true;
767 Context *context = NULL;
768
769 if (bitziBitprintInit(&bcontext) == -1)
770 {
771 set_error(submission, ERROR_HASHCHECK);
772 return false;
773 }
774
775 if (mcontext)
776 mp3_init(mcontext);
777 if (methods && methods->mem_analyze_init)
778 context = methods->mem_analyze_init();
779 if (submission->bc->calculateCRC32)
780 crc32 = 0xffffffff; //init
781 if (submission->bc->calculateMD5)
782 MD5Init(&md5context);
783 ED2KInit(&ed2kmd4context);
784 FTUUInit(&ftuucontext);
785 kztree_init(&kztreecontext);
786
787 buffer = (unsigned char*)malloc(BUFFER_LEN);
788 if (buffer == NULL)
789 {
790 set_error(submission, ERROR_MALLOCFAILED);
791 return false;
792 }
793
794 submission->percentComplete = 0;
795 if (submission->bc->progressCallback && !submission->bc->preview)
796 submission->bc->progressCallback(0, submission->fileName, NULL);
797
798 fseek(source, 0, SEEK_SET);
799 for(;;)
800 {
801 if (submission->bc->exitNow)
802 return false;
803
804 bytes = fread(buffer, 1, BUFFER_LEN, source);
805 if (bytes <= 0)
806 {
807 ret = feof(source);
808 break;
809 }
810
811 bitziBitprintUpdate(&bcontext, buffer, bytes);
812 if (mcontext)
813 mp3_update(mcontext, buffer, bytes);
814 if (methods && methods->mem_analyze_update)
815 methods->mem_analyze_update(context, buffer, bytes);
816
817 if (submission->bc->calculateCRC32)
818 crc32 = hashSmallHash(buffer, bytes , crc32); //crc32 by another name
819 if (submission->bc->calculateMD5)
820 MD5Update(&md5context, buffer, bytes);
821 ED2KUpdate(&ed2kmd4context, buffer, bytes);
822 FTUUUpdate(&ftuucontext, buffer, bytes);
823 kztree_update(&kztreecontext, buffer, bytes);
824
825 if (submission->bc->progressCallback && !submission->bc->preview)
826 {
827 int percentComplete;
828
829 percentComplete = (int)(((word64)ftell(source) * (word64)100) /
830 (word64)submission->fileSize);
831 if (percentComplete != submission->percentComplete)
832 {
833 submission->bc->progressCallback(percentComplete, NULL, NULL);
834 submission->percentComplete = percentComplete;
835 }
836 }
837 }
838 submission->percentComplete = 100;
839
840 free(buffer);
841
842 bitziBitprintFinal(&bcontext, bitprintRaw);
843 bitziBitprintToBase32(bitprintRaw, bitprint);
844
845 if (mcontext)
846 mp3_final(mcontext);
847 if (methods && methods->mem_analyze_final)
848 *attrList = methods->mem_analyze_final(context);
849
850 if (submission->bc->calculateCRC32)
851 {
852 crc32=~crc32;
853 sprintf(crc32hex,"%08X", crc32);
854 }
855
856 if (submission->bc->calculateMD5)
857 {
858 MD5Final(md5Digest, &md5context);
859 //bitziEncodeBase32(md5Digest, 16, md5sum);
860 sprintf(md5sum,"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
861 md5Digest[0],md5Digest[1],md5Digest[2],md5Digest[3],
862 md5Digest[4],md5Digest[5],md5Digest[6],md5Digest[7],
863 md5Digest[8],md5Digest[9],md5Digest[10],md5Digest[11],
864 md5Digest[12],md5Digest[13],md5Digest[14],md5Digest[15]);
865 }
866
867 ED2KFinal(ed2kDigest, &ed2kmd4context);
868 FTUUFinal(kzhash, &ftuucontext);
869 kztree_digest(&kztreecontext,kzhash+20);
870
871 sprintf(ed2kmd4sum,"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
872 ed2kDigest[0],ed2kDigest[1],ed2kDigest[2],ed2kDigest[3],
873 ed2kDigest[4],ed2kDigest[5],ed2kDigest[6],ed2kDigest[7],
874 ed2kDigest[8],ed2kDigest[9],ed2kDigest[10],ed2kDigest[11],
875 ed2kDigest[12],ed2kDigest[13],ed2kDigest[14],ed2kDigest[15]);
876
877 //bitziEncodeBase64(ftuuDigest,20,ftuusum);
878 sprintf(kzhashsum,"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
879 kzhash[0],kzhash[1],kzhash[2],kzhash[3],
880 kzhash[4],kzhash[5],kzhash[6],kzhash[7],
881 kzhash[8],kzhash[9],kzhash[10],kzhash[11],
882 kzhash[12],kzhash[13],kzhash[14],kzhash[15],
883 kzhash[16],kzhash[17],kzhash[18],kzhash[19],
884 kzhash[20],kzhash[21],kzhash[22],kzhash[23],
885 kzhash[24],kzhash[25],kzhash[26],kzhash[27],
886 kzhash[28],kzhash[29],kzhash[30],kzhash[31],
887 kzhash[32],kzhash[33],kzhash[34],kzhash[35]);
888
889 return ret;
890 }
891
generate_first_n_hex(BitcolliderSubmission * submission,FILE * source,int n,unsigned char * bits)892 b_bool generate_first_n_hex(BitcolliderSubmission *submission,
893 FILE *source,
894 int n,
895 unsigned char *bits)
896 {
897 unsigned char *buffer;
898
899 buffer = (unsigned char*)malloc(n);
900 if (buffer == NULL)
901 {
902 set_error(submission, ERROR_MALLOCFAILED);
903 return false;
904 }
905
906 fseek(source, 0, SEEK_SET);
907 n = fread(buffer, sizeof(unsigned char), n, source);
908 if (n < 0)
909 {
910 free(buffer);
911 return false;
912 }
913
914 bits[0] = 0;
915 convert_to_hex(buffer, n, bits);
916
917 free(buffer);
918
919 return true;
920 }
921
submit_submission(BitcolliderSubmission * submission,const char * url,BrowserEnum browser)922 b_bool submit_submission(BitcolliderSubmission *submission,
923 const char *url,
924 BrowserEnum browser)
925 {
926 FILE *output;
927 int i, last = -1;
928 char tempFile[MAX_PATH], *escaped;
929 b_bool ret;
930
931 if (submission->numBitprints == 0)
932 {
933 set_error(submission, "The submission contained no bitprints.");
934 return false;
935 }
936
937 #ifdef _WIN32
938 GetTempPath(MAX_PATH, tempFile);
939 strcat(tempFile, "bitprint.htm");
940 #else
941 strcpy(tempFile, "/tmp/bitprint.html");
942 #endif
943 output = fopen(tempFile, "wb");
944 if (output == NULL)
945 {
946 set_error(submission, ERROR_TEMPFILEERR);
947 return false;
948 }
949
950 fprintf(output,
951 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n");
952 fprintf(output, "<HTML><HEAD><TITLE>");
953
954 if (submission->numBitprints == 1)
955 fprintf(output, "Bitprint Submission %s\n", submission->fileName);
956 else
957 fprintf(output, "Multiple [%d] Bitprint Submission\n", submission->numBitprints);
958 fprintf(output, "</TITLE>\n</HEAD>\n");
959 if (submission->autoSubmit)
960 fprintf(output, "<BODY onLoad=\"document.forms[0].submit()\">\n");
961 else
962 fprintf(output, "<BODY>\n");
963
964 if (submission->numBitprints == 1)
965 fprintf(output, "<h3>Bitprint Submission %s</h3><p>\n",
966 submission->fileName);
967 else
968 fprintf(output, "<h3>Multiple [%d] Bitprint Submission</h3><p>\n",
969 submission->numBitprints);
970
971 fprintf(output,
972 "You are submitting the following bitprint and tag data to the web "
973 "location <i>%s</i>. For more information see <a "
974 "href=\"http://bitzi.com/bitcollider/websubmit\">the Bitzi website.</a>"
975 "<p>\nIf you are submitting more than a handful of files at once, it "
976 "may take a while for this page to load and submit.<p>\n"
977 "This submission should occur automatically. If it does not, you "
978 "may press the \"submit\" button which will appear at the bottom of "
979 "the page.<p><HR>\n", (url == NULL) ? SUBMIT_URL : url);
980
981 fprintf(output, "<FORM method=post action=\"%s\">\n",
982 (url == NULL) ? SUBMIT_URL : url);
983 fprintf(output, "<PRE>\n");
984
985 for(i = 0; i < submission->numItems; i++)
986 {
987 if (last != atoi(submission->attrList[i]->key) || i == 2)
988 {
989 last = atoi(submission->attrList[i]->key);
990 fprintf(output, "\n");
991 }
992 fprintf(output, "%s=<INPUT TYPE=\"hidden\" ",
993 submission->attrList[i]->key);
994 escaped = escape_form_value(submission->attrList[i]->value);
995 fprintf(output, "NAME=\"%s\" VALUE=\"%s\">%s\n",
996 submission->attrList[i]->key,
997 escaped,
998 submission->attrList[i]->value);
999 free(escaped);
1000 }
1001 fprintf(output, "\n<INPUT TYPE=\"submit\" NAME=\"Submit\" VALUE=\"Submit\">\n");
1002 fprintf(output, "</PRE>\n</FORM>\n</BODY>\n</HTML>\n");
1003 fclose(output);
1004
1005 #ifdef _WIN32
1006 {
1007 char url[MAX_PATH], *colon;
1008
1009
1010 colon = strchr(tempFile, ':');
1011 if (*colon)
1012 *colon = '|';
1013 sprintf(url, "file://%s", tempFile);
1014
1015 //Have Windows launch the default HTML viewer on the tempfile
1016 ret = (int)ShellExecute(NULL,"open",url,NULL,NULL,SW_SHOWNORMAL);
1017 ret = ret > 32;
1018 }
1019 #else
1020 ret = launch_browser(tempFile, browser);
1021 #endif
1022 if (!ret)
1023 set_error(submission, ERROR_LAUNCHBROWSER);
1024
1025 return ret;
1026 }
1027
escape_form_value(char * form_value)1028 char *escape_form_value(char *form_value)
1029 {
1030 int i, form_value_length, extra_length;
1031 char *escaped_value, *ptr;
1032
1033 form_value_length = strlen(form_value);
1034 for (i = 0, extra_length = 0; i < form_value_length; ++i)
1035 {
1036 switch(form_value[i])
1037 {
1038 case '"':
1039 extra_length += 5;
1040 break;
1041 case '&':
1042 extra_length += 4;
1043 break;
1044 case '<':
1045 case '>':
1046 extra_length += 3;
1047 break;
1048 }
1049 }
1050
1051 if (extra_length == 0)
1052 {
1053 // This is necessary since the caller must free the memory.
1054 return strdup(form_value);
1055 }
1056
1057 escaped_value = malloc(form_value_length + extra_length + 1);
1058 for (i = 0, ptr = escaped_value; i < form_value_length; ++i)
1059 {
1060 switch(form_value[i])
1061 {
1062 case '"':
1063 strcpy(ptr, """);
1064 ptr += 6;
1065 break;
1066 case '&':
1067 strcpy(ptr, "&");
1068 ptr += 5;
1069 break;
1070 case '<':
1071 strcpy(ptr, "<");
1072 ptr += 4;
1073 break;
1074 case '>':
1075 strcpy(ptr, ">");
1076 ptr += 4;
1077 break;
1078 default:
1079 *(ptr++) = form_value[i];
1080 }
1081 }
1082 *ptr = 0;
1083
1084 return escaped_value;
1085 }
1086
read_submission_from_file(Bitcollider * bc,char * fileName)1087 BitcolliderSubmission *read_submission_from_file(Bitcollider *bc, char *fileName)
1088 {
1089 BitcolliderSubmission *submission;
1090 char buf[BUFFER_LEN], last[BUFFER_LEN], temp[BUFFER_LEN], err[255];
1091 char *c, *t;
1092 int line, empty = 1;
1093 FILE *infile;
1094
1095 if (!strcmp(fileName, "-"))
1096 infile = stdin;
1097 else
1098 infile = fopen(fileName, "rb");
1099
1100 submission = (BitcolliderSubmission *)malloc(sizeof(BitcolliderSubmission));
1101 if (submission == NULL)
1102 {
1103 fclose(infile);
1104 return NULL;
1105 }
1106 memset(submission, 0, sizeof(BitcolliderSubmission));
1107 submission->bc = bc;
1108
1109 if (infile == NULL)
1110 {
1111 sprintf(err, "Can't open tag file: %s", strerror(errno));
1112 set_error(submission, err);
1113 return submission;
1114 }
1115
1116 last[0] = 0;
1117
1118 for( line = 1; fgets( buf, BUFFER_LEN, infile ) != NULL; ++line )
1119 {
1120 if( ! ( ( t = strchr( buf, '\r' ) ) || ( t = strchr( buf, '\n' ) ) ) )
1121 {
1122 if( strlen( buf ) == BUFFER_LEN - 1 )
1123 {
1124 sprintf(err, "Line %d exceeds length limit", line );
1125 set_error(submission, err);
1126 fclose(infile);
1127 submission->numBitprints = 0;
1128 return submission;
1129 }
1130 else
1131 {
1132 sprintf(err, "Line %d is truncated", line );
1133 set_error(submission, err);
1134 fclose(infile);
1135 submission->numBitprints = 0;
1136 return submission;
1137 }
1138 }
1139 *t = 0;
1140
1141 for( c = buf; *c == ' ' && *c == '\t'; ++c )
1142 ; /* <-- Empty for loop */
1143
1144 if( ! *c || *c == '#' )
1145 continue;
1146
1147 if( ! ( t = strchr( c, '=' ) ) )
1148 {
1149 sprintf(err, "Line %d does not appear to contain a tag", line );
1150 set_error(submission, err);
1151 fclose(infile);
1152 submission->numBitprints = 0;
1153 return submission;
1154 }
1155 *t = 0;
1156 if( ! strncmp( c, "head.", 5 ) )
1157 continue;
1158 if( isdigit( *c ) )
1159 {
1160 if( ! ( t = strchr( c, '.' ) ) )
1161 {
1162 sprintf(err, "Line %d does not appear to contain a tag", line);
1163 set_error(submission, err);
1164 fclose(infile);
1165 submission->numBitprints = 0;
1166 return submission;
1167 }
1168 *t = 0;
1169
1170 strcpy( last, c );
1171 c = t + 1;
1172 }
1173 if (empty)
1174 {
1175 empty = 0;
1176 get_agent_string(temp);
1177 add_attribute(submission, "head.agent", temp);
1178
1179 sprintf(temp, "S%s", BC_SUBMITSPECVER);
1180 add_attribute(submission, "head.version", temp);
1181 }
1182
1183 if(strncmp(c, "bitprint", 8) == 0)
1184 {
1185 if (submission->numBitprints == 1)
1186 convert_to_multiple_submission(submission);
1187
1188 submission->numBitprints++;
1189 }
1190 submission->numBitprints--;
1191 add_attribute( submission, c, c + strlen( c ) + 1 );
1192 submission->numBitprints++;
1193 }
1194
1195 fclose(infile);
1196
1197 return submission;
1198 }
1199
print_submission(BitcolliderSubmission * submission)1200 void print_submission(BitcolliderSubmission *submission)
1201 {
1202 int i;
1203
1204 for(i = 0; i < submission->numItems; i++)
1205 {
1206 printf("%s=%s\n",
1207 submission->attrList[i]->key,
1208 submission->attrList[i]->value);
1209 }
1210 }
1211
get_agent_string(char * agentString)1212 void get_agent_string(char *agentString)
1213 {
1214 sprintf(agentString, "%s/%s (%s)", BC_AGENTNAME, BC_VERSION,
1215 BC_AGENTBUILD);
1216 }
1217
1218 #ifdef _WIN32
1219 /* We get to write this function becuase win95 does not
1220 support this function! Thanks uncle Bill!!
1221 */
getLongPathName(const char * shortName,int len,char * longName)1222 void getLongPathName(const char *shortName, int len, char *longName)
1223 {
1224 WIN32_FIND_DATA FindFileData;
1225 HANDLE findHandle;
1226 char drive[10], path[MAX_PATH];
1227
1228 findHandle = FindFirstFile(shortName, &FindFileData);
1229 if (findHandle != INVALID_HANDLE_VALUE)
1230 {
1231 _splitpath(shortName, drive, path, NULL, NULL);
1232 _snprintf(longName, len, "%s%s%s", drive, path, FindFileData.cFileName);
1233 FindClose(findHandle);
1234 }
1235 else
1236 { /* Hmmm. I guess we're screwed. */
1237 strncpy(longName, shortName, len);
1238 }
1239 longName[len - 1] = 0;
1240 }
1241 #endif
1242
check_md5_hash(void)1243 b_bool check_md5_hash(void)
1244 {
1245 struct MD5Context md5context;
1246 unsigned char md5Digest[16];
1247 char md5Hash[33];
1248
1249 MD5Init(&md5context);
1250 MD5Final(md5Digest, &md5context);
1251 bitziEncodeBase32(md5Digest, 16, md5Hash);
1252
1253 if (strcmp(MD5_SANITY_CHECK_EMPTY, md5Hash))
1254 return false;
1255
1256 MD5Init(&md5context);
1257 MD5Update(&md5context, "01234", 5);
1258 MD5Final(md5Digest, &md5context);
1259 bitziEncodeBase32(md5Digest, 16, md5Hash);
1260
1261 if (strcmp(MD5_SANITY_CHECK_01234, md5Hash))
1262 return false;
1263
1264 return true;
1265 }
1266
1267