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, &centry))
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, &centry);
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, &centry, 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, "&quot;");
1064                 ptr += 6;
1065                 break;
1066             case '&':
1067                 strcpy(ptr, "&amp;");
1068                 ptr += 5;
1069                 break;
1070             case '<':
1071                 strcpy(ptr, "&lt;");
1072                 ptr += 4;
1073                 break;
1074             case '>':
1075                 strcpy(ptr, "&gt;");
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