1
/* Time-stamp: <2006-05-17 22:10:35 jcs>
3
| Copyright (C) 2002 Corey Donohoe <atmos at atmos.org>
4
| Copyright (C) 2002-2005 Jorg Schuler <jcsjcs at users sourceforge net>
5
| Part of the gtkpod project.
7
| URL: http://www.gtkpod.org/
8
| URL: http://gtkpod.sourceforge.net/
10
| SHA1 routine: David Puckett <niekze at yahoo.com>
11
| SHA1 implemented from FIPS-160 standard
12
| <http://www.itl.nist.gov/fipspubs/fip180-1.htm>
14
| This program is free software; you can redistribute it and/or modify
15
| it under the terms of the GNU General Public License as published by
16
| the Free Software Foundation; either version 2 of the License, or
17
| (at your option) any later version.
19
| This program is distributed in the hope that it will be useful,
20
| but WITHOUT ANY WARRANTY; without even the implied warranty of
21
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
| GNU General Public License for more details.
24
| You should have received a copy of the GNU General Public License
25
| along with this program; if not, write to the Free Software
26
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28
| iTunes and iPod are trademarks of Apple
30
| This product is not supported/written/published by Apple!
32
| $Id: md5.c,v 1.44 2006/06/25 23:29:08 freethinkerjim Exp $
40
#include <sys/types.h>
48
#include "misc_track.h"
50
#include "display_itdb.h"
53
typedef guint32 chunk;
59
typedef union _block block;
66
typedef union _hblock hblock;
73
typedef struct _sha1 sha1;
75
static guint8 *sha1_hash(const guint8 * text, guint32 len);
76
static void process_block_sha1(sha1 * message);
78
#if BYTE_ORDER == LITTLE_ENDIAN
79
static void little_endian(hblock * stupidblock, int blocks);
83
* Create and manage a string hash for files on disk
88
* A seed of sorts for SHA1, if collisions occur increasing this value
89
* should give more unique data to SHA1 as more of the file is read
90
* This value is multiplied by PATH_MAX_MD5 to determine how many bytes are read
92
#define NR_PATH_MAX_BLOCKS 4
93
#define PATH_MAX_MD5 4096
95
/* Set up or destory the md5 hash table */
98
struct itdbs_head *itdbs_head;
100
g_return_if_fail (gtkpod_window);
101
itdbs_head = g_object_get_data (G_OBJECT (gtkpod_window),
104
/* gets called before itdbs are set up -> fail silently */
107
if (prefs_get_int("md5")) /* MD5 hashing turned on */
109
gp_md5_hash_tracks();
111
/* Display duplicates */
112
gp_duplicate_remove(NULL, NULL);
120
* get_filesize_for_file_descriptor - get the filesize on disk for the given
122
* @fp - the filepointer we want the filesize for
123
* Returns - the filesize in bytes
126
get_filesize_for_file_descriptor(FILE *fp)
129
struct stat stat_info;
130
int file_no = fileno(fp);
132
if((fstat(file_no, &stat_info) == 0)) /* returns 0 on success */
133
result = (int)stat_info.st_size;
134
return (guint32)result;
138
* md5_hash_on_file - read PATH_MAX_MD5 * NR_PATH_MAX_BLOCKS bytes
139
* from the file and ask sha1 for a hash of it, convert this hash to a
140
* string of hex output @fp - an open file descriptor to read from
141
* Returns - A Hash String - you handle memory returned
144
md5_hash_on_file(FILE * fp)
146
gchar *result = NULL;
151
int chunk_size = PATH_MAX_MD5 * NR_PATH_MAX_BLOCKS;
153
fsize = get_filesize_for_file_descriptor(fp);
154
if(fsize < chunk_size)
159
guint32 fsize_normal;
161
int bread = 0, x = 0, last = 0;
162
guchar file_chunk[chunk_size + sizeof(int)];
164
/* allocate the digest we're returning */
165
result = g_malloc0(sizeof(gchar) * 41);
167
/* put filesize in the first 32 bits */
168
fsize_normal = GINT32_TO_LE (fsize);
169
memcpy(file_chunk, &fsize_normal, sizeof(guint32));
171
/* read chunk_size from fp */
172
bread = fread(&file_chunk[sizeof(int)], sizeof(gchar),
175
/* create hash from our data */
176
hash = sha1_hash(file_chunk, (bread + sizeof(int)));
178
/* put it in a format we like */
179
for (x = 0; x < 20; x++)
180
last += snprintf(&result[last], 4, "%02x", hash[x]);
182
/* free the hash value sha1_hash gave us */
187
gtkpod_warning(_("Hashed file is 0 bytes long\n"));
194
* Generate a unique hash for the Track passed in
195
* @s - The Track data structure, we want to hash based on the file on disk
196
* Returns - an SHA1 hash in string format, is the hex output from the hash
199
md5_hash_track(Track * s)
202
gchar *result = NULL;
205
g_return_val_if_fail (s, NULL);
207
g_return_val_if_fail (etr, NULL);
209
if (etr->md5_hash != NULL)
211
result = g_strdup(etr->md5_hash);
215
filename = get_file_name_from_source (s, SOURCE_PREFER_LOCAL);
218
result = md5_hash_on_filename (filename, FALSE);
226
/* @silent: don't print any warning */
227
gchar *md5_hash_on_filename (gchar *name, gboolean silent)
229
gchar *result = NULL;
233
FILE *fpit = fopen (name, "r");
238
gchar *name_utf8=charset_to_utf8 (name);
240
_("Could not open '%s' to calculate MD5 checksum: %s\n"),
241
name_utf8, strerror(errno));
247
result = md5_hash_on_file (fpit);
256
* Free up the dynamically allocated memory in @itdb's hash table
258
void md5_free_eitdb (ExtraiTunesDBData *eitdb)
260
g_return_if_fail (eitdb);
264
g_hash_table_destroy (eitdb->md5hash);
265
eitdb->md5hash = NULL;
270
* Free up the dynamically allocated memory in @itdb's hash table
272
void md5_free (iTunesDB *itdb)
274
g_return_if_fail (itdb);
275
g_return_if_fail (itdb->userdata);
277
md5_free_eitdb (itdb->userdata);
281
* Check to see if a track has already been added to the ipod
282
* @s - the Track we want to know about. If the track does not exist, it
283
* is inserted into the hash.
284
* Returns a pointer to the duplicate track.
286
Track *md5_track_exists_insert (iTunesDB *itdb, Track * s)
288
ExtraiTunesDBData *eitdb;
293
g_return_val_if_fail (itdb, NULL);
294
eitdb = itdb->userdata;
295
g_return_val_if_fail (eitdb, NULL);
297
g_return_val_if_fail (s, NULL);
299
g_return_val_if_fail (etr, NULL);
301
if (prefs_get_int("md5"))
303
if (eitdb->md5hash == NULL)
305
eitdb->md5hash = g_hash_table_new_full(g_str_hash,
309
val = md5_hash_track (s);
312
track = g_hash_table_lookup (eitdb->md5hash, val);
318
{ /* if it doesn't exist we register it in the hash */
319
g_free (etr->md5_hash);
320
etr->md5_hash = g_strdup (val);
321
/* val is used in the next line -- we don't have to
323
g_hash_table_insert (eitdb->md5hash, val, s);
331
* Check to see if a track has already been added to the ipod
332
* @s - the Track we want to know about.
333
* Returns a pointer to the duplicate track.
335
Track *md5_track_exists (iTunesDB *itdb, Track *s)
337
ExtraiTunesDBData *eitdb;
340
g_return_val_if_fail (itdb, NULL);
341
eitdb = itdb->userdata;
342
g_return_val_if_fail (eitdb, NULL);
344
if (prefs_get_int("md5") && eitdb->md5hash)
346
gchar *val = md5_hash_track (s);
349
track = g_hash_table_lookup (eitdb->md5hash, val);
357
* Check to see if a track has already been added to the ipod
358
* @file - the Track we want to know about.
359
* Returns a pointer to the duplicate track using md5 for
360
* identification. If md5 checksums are off, NULL is returned.
362
Track *md5_file_exists (iTunesDB *itdb, gchar *file, gboolean silent)
364
ExtraiTunesDBData *eitdb;
367
g_return_val_if_fail (file, NULL);
368
g_return_val_if_fail (itdb, NULL);
369
eitdb = itdb->userdata;
370
g_return_val_if_fail (eitdb, NULL);
372
if (prefs_get_int("md5") && eitdb->md5hash)
374
gchar *val = md5_hash_on_filename (file, silent);
377
track = g_hash_table_lookup (eitdb->md5hash, val);
386
* Check to see if a track has already been added to the ipod
387
* @md5 - the md5 we want to know about.
388
* Returns a pointer to the duplicate track using md5 for
389
* identification. If md5 checksums are off, NULL is returned.
391
Track *md5_md5_exists (iTunesDB *itdb, gchar *md5)
393
ExtraiTunesDBData *eitdb;
396
g_return_val_if_fail (md5, NULL);
397
g_return_val_if_fail (itdb, NULL);
398
eitdb = itdb->userdata;
399
g_return_val_if_fail (eitdb, NULL);
401
if (prefs_get_int("md5") && eitdb->md5hash)
403
track = g_hash_table_lookup (eitdb->md5hash, md5);
409
* Free the specified track from the ipod's unique file hash
410
* @s - The Track that's being freed from the ipod
412
void md5_track_remove (Track *s)
414
ExtraiTunesDBData *eitdb;
416
g_return_if_fail (s);
417
g_return_if_fail (s->itdb);
418
eitdb = s->itdb->userdata;
419
g_return_if_fail (eitdb);
421
if (prefs_get_int("md5") && eitdb->md5hash)
423
gchar *val = md5_hash_track (s);
426
Track *track = g_hash_table_lookup (eitdb->md5hash, val);
429
if (track == s) /* only remove if it's the same track */
430
g_hash_table_remove (eitdb->md5hash, val);
437
/* sha1_hash - hash value the input data with a given size.
438
* @text - the data we're reading to seed sha1
439
* @len - the length of the data for our seed
440
* Returns a unique 20 char array.
443
sha1_hash(const guint8 * text, guint32 len)
446
chunk temp_len = len;
447
const guint8 *temp_text = text;
451
digest = g_malloc0(sizeof(guint8) * 21);
452
message = g_malloc0(sizeof(sha1));
453
message->blockdata = g_malloc0(sizeof(block));
454
message->H = g_malloc(sizeof(hblock));
456
message->H->chunkblock[0] = 0x67452301;
457
message->H->chunkblock[1] = 0xefcdab89;
458
message->H->chunkblock[2] = 0x98badcfe;
459
message->H->chunkblock[3] = 0x10325476;
460
message->H->chunkblock[4] = 0xc3d2e1f0;
461
while (temp_len >= 64)
463
for (x = 0; x < 64; x++)
464
message->blockdata->charblock[x] = temp_text[x];
465
#if BYTE_ORDER == LITTLE_ENDIAN
466
little_endian((hblock *) message->blockdata, 16);
468
process_block_sha1(message);
472
for (x = 0; x < temp_len; x++)
473
message->blockdata->charblock[x] = temp_text[x];
474
message->blockdata->charblock[temp_len] = 0x80;
475
for (x = temp_len + 1; x < 64; x++)
476
message->blockdata->charblock[x] = 0x00;
477
#if BYTE_ORDER == LITTLE_ENDIAN
478
little_endian((hblock *) message->blockdata, 16);
482
process_block_sha1(message);
483
for (x = 0; x < 60; x++)
484
message->blockdata->charblock[x] = 0x00;
486
message->blockdata->chunkblock[15] = len * 8;
487
process_block_sha1(message);
488
#if BYTE_ORDER == LITTLE_ENDIAN
489
little_endian(message->H, 5);
491
for (x = 0; x < 20; x++)
492
digest[x] = message->H->charblock[x];
494
g_free(message->blockdata);
501
* process_block_sha1 - process one 512-bit block of data
502
* @message - the sha1 struct we're doing working on
505
process_block_sha1(sha1 * message)
508
chunk w[80]; /* test block */
509
chunk A; /* A - E: used for hash calc */
514
chunk T; /* temp guint32 */
515
chunk K[] = { 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 };
517
for (x = 0; x < 16; x++)
518
w[x] = message->blockdata->chunkblock[x];
519
for (x = 16; x < 80; x++)
521
w[x] = w[x - 3] ^ w[x - 8] ^ w[x - 14] ^ w[x - 16];
522
w[x] = (w[x] << 1) | (w[x] >> 31);
524
A = message->H->chunkblock[0];
525
B = message->H->chunkblock[1];
526
C = message->H->chunkblock[2];
527
D = message->H->chunkblock[3];
528
E = message->H->chunkblock[4];
529
for (x = 0; x < 80; x++)
531
T = ((A << 5) | (A >> 27)) + E + w[x] + K[x / 20];
533
T += (B & C) | ((~B) & D);
534
else if (x < 40 || x >= 60)
537
T += ((C | D) & B) | (C & D);
540
C = (B << 30) | (B >> 2);
544
message->H->chunkblock[0] += A;
545
message->H->chunkblock[1] += B;
546
message->H->chunkblock[2] += C;
547
message->H->chunkblock[3] += D;
548
message->H->chunkblock[4] += E;
551
#if BYTE_ORDER == LITTLE_ENDIAN
553
* little_endian - swap the significants bits to cater to bigendian
554
* @stupidblock - the block of data we're swapping
555
* @blocks - the number of blocks we're swapping
558
little_endian(hblock * stupidblock, int blocks)
561
for (x = 0; x < blocks; x++)
563
stupidblock->chunkblock[x] = (stupidblock->charblock[x * 4] << 24 | stupidblock->charblock[x * 4 + 1] << 16 | stupidblock->charblock[x * 4 +2] << 8 | stupidblock->charblock[x * 4 + 3]);