1 /*
2 | Copyright (C) 2002 Corey Donohoe <atmos at atmos.org>
3 | Copyright (C) 2002-2007 Jorg Schuler <jcsjcs at users sourceforge net>
4 | Part of the gtkpod project.
5 |
6 | URL: http://www.gtkpod.org/
7 | URL: http://gtkpod.sourceforge.net/
8 |
9 | SHA1 routine: David Puckett <niekze at yahoo.com>
10 | SHA1 implemented from FIPS-160 standard
11 | <http://www.itl.nist.gov/fipspubs/fip180-1.htm>
12 |
13 | This program is free software; you can redistribute it and/or modify
14 | it under the terms of the GNU General Public License as published by
15 | the Free Software Foundation; either version 2 of the License, or
16 | (at your option) any later version.
17 |
18 | This program is distributed in the hope that it will be useful,
19 | but WITHOUT ANY WARRANTY; without even the implied warranty of
20 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 | GNU General Public License for more details.
22 |
23 | You should have received a copy of the GNU General Public License
24 | along with this program; if not, write to the Free Software
25 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 |
27 | iTunes and iPod are trademarks of Apple
28 |
29 | This product is not supported/written/published by Apple!
30 |
31 | $Id$
32 */
33
34 #include <errno.h>
35 #include <limits.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <sys/stat.h>
39 #include <sys/types.h>
40 #include <unistd.h>
41 #include <glib.h>
42 #include "charset.h"
43 #include "sha1.h"
44 #include "file.h"
45 #include "prefs.h"
46 #include "misc.h"
47 #include "misc_track.h"
48 #include "display.h"
49 #include "display_itdb.h"
50
51
52 typedef guint32 chunk;
53 union _block
54 {
55 guint8 charblock[64];
56 chunk chunkblock[16];
57 };
58 typedef union _block block;
59
60 union _hblock
61 {
62 guint8 charblock[20];
63 chunk chunkblock[5];
64 };
65 typedef union _hblock hblock;
66
67 struct _sha1
68 {
69 block *blockdata;
70 hblock *H;
71 };
72 typedef struct _sha1 sha1;
73
74 static guint8 *sha1_hash(const guint8 * text, guint32 len);
75 static void process_block_sha1(sha1 * message);
76
77 #if BYTE_ORDER == LITTLE_ENDIAN
78 static void little_endian(hblock * stupidblock, int blocks);
79 #endif
80
81 /**
82 * Create and manage a string hash for files on disk
83 */
84
85 /**
86 * NR_PATH_MAX_BLOCKS
87 * A seed of sorts for SHA1, if collisions occur increasing this value
88 * should give more unique data to SHA1 as more of the file is read
89 * This value is multiplied by PATH_MAX_MD5 to determine how many bytes are read
90 */
91 #define NR_PATH_MAX_BLOCKS 4
92 #define PATH_MAX_SHA1 4096
93
94 /* Set up or destory the sha1 hash table */
setup_sha1()95 void setup_sha1()
96 {
97 struct itdbs_head *itdbs_head;
98
99 g_return_if_fail (gtkpod_window);
100 itdbs_head = g_object_get_data (G_OBJECT (gtkpod_window),
101 "itdbs_head");
102
103 /* gets called before itdbs are set up -> fail silently */
104 if (itdbs_head)
105 {
106 if (prefs_get_int("sha1")) /* SHA1 hashing turned on */
107 {
108 gp_sha1_hash_tracks();
109
110 /* Display duplicates */
111 gp_duplicate_remove(NULL, NULL);
112 }
113 else
114 gp_sha1_free_hash();
115 }
116 }
117
118 /**
119 * get_filesize_for_file_descriptor - get the filesize on disk for the given
120 * file descriptor
121 * @fp - the filepointer we want the filesize for
122 * Returns - the filesize in bytes
123 */
124 static guint32
get_filesize_for_file_descriptor(FILE * fp)125 get_filesize_for_file_descriptor(FILE *fp)
126 {
127 off_t result = 0;
128 struct stat stat_info;
129 int file_no = fileno(fp);
130
131 if((fstat(file_no, &stat_info) == 0)) /* returns 0 on success */
132 result = (int)stat_info.st_size;
133 return (guint32)result;
134 }
135
136 /**
137 * sha1_hash_on_file - read PATH_MAX_SHA1 * NR_PATH_MAX_BLOCKS bytes
138 * from the file and ask sha1 for a hash of it, convert this hash to a
139 * string of hex output @fp - an open file descriptor to read from
140 * Returns - A Hash String - you handle memory returned
141 */
142 static gchar *
sha1_hash_on_file(FILE * fp)143 sha1_hash_on_file(FILE * fp)
144 {
145 gchar *result = NULL;
146
147 if (fp)
148 {
149 int fsize = 0;
150 int chunk_size = PATH_MAX_SHA1 * NR_PATH_MAX_BLOCKS;
151
152 fsize = get_filesize_for_file_descriptor(fp);
153 if(fsize < chunk_size)
154 chunk_size = fsize;
155
156 if(fsize > 0)
157 {
158 guint32 fsize_normal;
159 guint8 *hash = NULL;
160 int bread = 0, x = 0, last = 0;
161 guchar file_chunk[chunk_size + sizeof(int)];
162
163 /* allocate the digest we're returning */
164 result = g_malloc0(sizeof(gchar) * 41);
165
166 /* put filesize in the first 32 bits */
167 fsize_normal = GINT32_TO_LE (fsize);
168 memcpy(file_chunk, &fsize_normal, sizeof(guint32));
169
170 /* read chunk_size from fp */
171 bread = fread(&file_chunk[sizeof(int)], sizeof(gchar),
172 chunk_size, fp);
173
174 /* create hash from our data */
175 hash = sha1_hash(file_chunk, (bread + sizeof(int)));
176
177 /* put it in a format we like */
178 for (x = 0; x < 20; x++)
179 last += snprintf(&result[last], 4, "%02x", hash[x]);
180
181 /* free the hash value sha1_hash gave us */
182 g_free(hash);
183 }
184 else
185 {
186 gtkpod_warning(_("Hashed file is 0 bytes long\n"));
187 }
188 }
189 return (result);
190 }
191
192 /**
193 * Generate a unique hash for the Track passed in
194 * @s - The Track data structure, we want to hash based on the file on disk
195 * Returns - an SHA1 hash in string format, is the hex output from the hash
196 */
197 static gchar *
sha1_hash_track(Track * s)198 sha1_hash_track(Track * s)
199 {
200 ExtraTrackData *etr;
201 gchar *result = NULL;
202 gchar *filename;
203
204 g_return_val_if_fail (s, NULL);
205 etr = s->userdata;
206 g_return_val_if_fail (etr, NULL);
207
208 if (etr->sha1_hash != NULL)
209 {
210 result = g_strdup(etr->sha1_hash);
211 }
212 else
213 {
214 filename = get_file_name_from_source (s, SOURCE_PREFER_LOCAL);
215 if (filename)
216 {
217 result = sha1_hash_on_filename (filename, FALSE);
218 g_free(filename);
219 }
220 }
221 return (result);
222 }
223
224
225 /* @silent: don't print any warning */
sha1_hash_on_filename(gchar * name,gboolean silent)226 gchar *sha1_hash_on_filename (gchar *name, gboolean silent)
227 {
228 gchar *result = NULL;
229
230 if (name)
231 {
232 FILE *fpit = fopen (name, "r");
233 if (!fpit)
234 {
235 if (!silent)
236 {
237 gchar *name_utf8=charset_to_utf8 (name);
238 gtkpod_warning (
239 _("Could not open '%s' to calculate SHA1 checksum: %s\n"),
240 name_utf8, strerror(errno));
241 g_free (name_utf8);
242 }
243 }
244 else
245 {
246 result = sha1_hash_on_file (fpit);
247 fclose (fpit);
248 }
249 }
250 return result;
251 }
252
253
254 /**
255 * Free up the dynamically allocated memory in @itdb's hash table
256 */
sha1_free_eitdb(ExtraiTunesDBData * eitdb)257 void sha1_free_eitdb (ExtraiTunesDBData *eitdb)
258 {
259 g_return_if_fail (eitdb);
260
261 if (eitdb->sha1hash)
262 {
263 g_hash_table_destroy (eitdb->sha1hash);
264 eitdb->sha1hash = NULL;
265 }
266 }
267
268 /**
269 * Free up the dynamically allocated memory in @itdb's hash table
270 */
sha1_free(iTunesDB * itdb)271 void sha1_free (iTunesDB *itdb)
272 {
273 g_return_if_fail (itdb);
274 g_return_if_fail (itdb->userdata);
275
276 sha1_free_eitdb (itdb->userdata);
277 }
278
279 /**
280 * Check to see if a track has already been added to the ipod
281 * @s - the Track we want to know about. If the track does not exist, it
282 * is inserted into the hash.
283 * Returns a pointer to the duplicate track.
284 */
sha1_track_exists_insert(iTunesDB * itdb,Track * s)285 Track *sha1_track_exists_insert (iTunesDB *itdb, Track * s)
286 {
287 ExtraiTunesDBData *eitdb;
288 ExtraTrackData *etr;
289 gchar *val = NULL;
290 Track *track = NULL;
291
292 g_return_val_if_fail (itdb, NULL);
293 eitdb = itdb->userdata;
294 g_return_val_if_fail (eitdb, NULL);
295
296 g_return_val_if_fail (s, NULL);
297 etr = s->userdata;
298 g_return_val_if_fail (etr, NULL);
299
300 if (prefs_get_int("sha1"))
301 {
302 if (eitdb->sha1hash == NULL)
303 {
304 eitdb->sha1hash = g_hash_table_new_full(g_str_hash,
305 g_str_equal,
306 g_free, NULL);
307 }
308 val = sha1_hash_track (s);
309 if (val != NULL)
310 {
311 track = g_hash_table_lookup (eitdb->sha1hash, val);
312 if (track)
313 {
314 g_free(val);
315 }
316 else
317 { /* if it doesn't exist we register it in the hash */
318 g_free (etr->sha1_hash);
319 etr->sha1_hash = g_strdup (val);
320 /* val is used in the next line -- we don't have to
321 * g_free() it */
322 g_hash_table_insert (eitdb->sha1hash, val, s);
323 }
324 }
325 }
326 return track;
327 }
328
329 /**
330 * Check to see if a track has already been added to the ipod
331 * @s - the Track we want to know about.
332 * Returns a pointer to the duplicate track.
333 */
sha1_track_exists(iTunesDB * itdb,Track * s)334 Track *sha1_track_exists (iTunesDB *itdb, Track *s)
335 {
336 ExtraiTunesDBData *eitdb;
337 Track *track = NULL;
338
339 g_return_val_if_fail (itdb, NULL);
340 eitdb = itdb->userdata;
341 g_return_val_if_fail (eitdb, NULL);
342
343 if (prefs_get_int("sha1") && eitdb->sha1hash)
344 {
345 gchar *val = sha1_hash_track (s);
346 if (val)
347 {
348 track = g_hash_table_lookup (eitdb->sha1hash, val);
349 g_free (val);
350 }
351 }
352 return track;
353 }
354
355 /**
356 * Check to see if a track has already been added to the ipod
357 * @file - the Track we want to know about.
358 * Returns a pointer to the duplicate track using sha1 for
359 * identification. If sha1 checksums are off, NULL is returned.
360 */
sha1_file_exists(iTunesDB * itdb,gchar * file,gboolean silent)361 Track *sha1_file_exists (iTunesDB *itdb, gchar *file, gboolean silent)
362 {
363 ExtraiTunesDBData *eitdb;
364 Track *track = NULL;
365
366 g_return_val_if_fail (file, NULL);
367 g_return_val_if_fail (itdb, NULL);
368 eitdb = itdb->userdata;
369 g_return_val_if_fail (eitdb, NULL);
370
371 if (prefs_get_int("sha1") && eitdb->sha1hash)
372 {
373 gchar *val = sha1_hash_on_filename (file, silent);
374 if (val)
375 {
376 track = g_hash_table_lookup (eitdb->sha1hash, val);
377 g_free (val);
378 }
379 }
380 return track;
381 }
382
383
384 /**
385 * Check to see if a track has already been added to the ipod
386 * @sha1 - the sha1 we want to know about.
387 * Returns a pointer to the duplicate track using sha1 for
388 * identification. If sha1 checksums are off, NULL is returned.
389 */
sha1_sha1_exists(iTunesDB * itdb,gchar * sha1)390 Track *sha1_sha1_exists (iTunesDB *itdb, gchar *sha1)
391 {
392 ExtraiTunesDBData *eitdb;
393 Track *track = NULL;
394
395 g_return_val_if_fail (sha1, NULL);
396 g_return_val_if_fail (itdb, NULL);
397 eitdb = itdb->userdata;
398 g_return_val_if_fail (eitdb, NULL);
399
400 if (prefs_get_int("sha1") && eitdb->sha1hash)
401 {
402 track = g_hash_table_lookup (eitdb->sha1hash, sha1);
403 }
404 return track;
405 }
406
407 /**
408 * Free the specified track from the ipod's unique file hash
409 * @s - The Track that's being freed from the ipod
410 */
sha1_track_remove(Track * s)411 void sha1_track_remove (Track *s)
412 {
413 ExtraiTunesDBData *eitdb;
414
415 g_return_if_fail (s);
416 g_return_if_fail (s->itdb);
417 eitdb = s->itdb->userdata;
418 g_return_if_fail (eitdb);
419
420 if (prefs_get_int("sha1") && eitdb->sha1hash)
421 {
422 gchar *val = sha1_hash_track (s);
423 if (val)
424 {
425 Track *track = g_hash_table_lookup (eitdb->sha1hash, val);
426 if (track)
427 {
428 if (track == s) /* only remove if it's the same track */
429 g_hash_table_remove (eitdb->sha1hash, val);
430 }
431 g_free(val);
432 }
433 }
434 }
435
436 /* sha1_hash - hash value the input data with a given size.
437 * @text - the data we're reading to seed sha1
438 * @len - the length of the data for our seed
439 * Returns a unique 20 char array.
440 */
441 static guint8 *
sha1_hash(const guint8 * text,guint32 len)442 sha1_hash(const guint8 * text, guint32 len)
443 {
444 chunk x;
445 chunk temp_len = len;
446 const guint8 *temp_text = text;
447 guint8 *digest;
448 sha1 *message;
449
450 digest = g_malloc0(sizeof(guint8) * 21);
451 message = g_malloc0(sizeof(sha1));
452 message->blockdata = g_malloc0(sizeof(block));
453 message->H = g_malloc(sizeof(hblock));
454
455 message->H->chunkblock[0] = 0x67452301;
456 message->H->chunkblock[1] = 0xefcdab89;
457 message->H->chunkblock[2] = 0x98badcfe;
458 message->H->chunkblock[3] = 0x10325476;
459 message->H->chunkblock[4] = 0xc3d2e1f0;
460 while (temp_len >= 64)
461 {
462 for (x = 0; x < 64; x++)
463 message->blockdata->charblock[x] = temp_text[x];
464 #if BYTE_ORDER == LITTLE_ENDIAN
465 little_endian((hblock *) message->blockdata, 16);
466 #endif
467 process_block_sha1(message);
468 temp_len -= 64;
469 temp_text += 64;
470 }
471 for (x = 0; x < temp_len; x++)
472 message->blockdata->charblock[x] = temp_text[x];
473 message->blockdata->charblock[temp_len] = 0x80;
474 for (x = temp_len + 1; x < 64; x++)
475 message->blockdata->charblock[x] = 0x00;
476 #if BYTE_ORDER == LITTLE_ENDIAN
477 little_endian((hblock *) message->blockdata, 16);
478 #endif
479 if (temp_len > 54)
480 {
481 process_block_sha1(message);
482 for (x = 0; x < 60; x++)
483 message->blockdata->charblock[x] = 0x00;
484 }
485 message->blockdata->chunkblock[15] = len * 8;
486 process_block_sha1(message);
487 #if BYTE_ORDER == LITTLE_ENDIAN
488 little_endian(message->H, 5);
489 #endif
490 for (x = 0; x < 20; x++)
491 digest[x] = message->H->charblock[x];
492 digest[20] = 0x00;
493 g_free(message->blockdata);
494 g_free(message->H);
495 g_free(message);
496 return (digest);
497 }
498
499 /*
500 * process_block_sha1 - process one 512-bit block of data
501 * @message - the sha1 struct we're doing working on
502 */
503 static void
process_block_sha1(sha1 * message)504 process_block_sha1(sha1 * message)
505 {
506 chunk x;
507 chunk w[80]; /* test block */
508 chunk A; /* A - E: used for hash calc */
509 chunk B;
510 chunk C;
511 chunk D;
512 chunk E;
513 chunk T; /* temp guint32 */
514 chunk K[] = { 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 };
515
516 for (x = 0; x < 16; x++)
517 w[x] = message->blockdata->chunkblock[x];
518 for (x = 16; x < 80; x++)
519 {
520 w[x] = w[x - 3] ^ w[x - 8] ^ w[x - 14] ^ w[x - 16];
521 w[x] = (w[x] << 1) | (w[x] >> 31);
522 }
523 A = message->H->chunkblock[0];
524 B = message->H->chunkblock[1];
525 C = message->H->chunkblock[2];
526 D = message->H->chunkblock[3];
527 E = message->H->chunkblock[4];
528 for (x = 0; x < 80; x++)
529 {
530 T = ((A << 5) | (A >> 27)) + E + w[x] + K[x / 20];
531 if (x < 20)
532 T += (B & C) | ((~B) & D);
533 else if (x < 40 || x >= 60)
534 T += B ^ C ^ D;
535 else
536 T += ((C | D) & B) | (C & D);
537 E = D;
538 D = C;
539 C = (B << 30) | (B >> 2);
540 B = A;
541 A = T;
542 }
543 message->H->chunkblock[0] += A;
544 message->H->chunkblock[1] += B;
545 message->H->chunkblock[2] += C;
546 message->H->chunkblock[3] += D;
547 message->H->chunkblock[4] += E;
548 }
549
550 #if BYTE_ORDER == LITTLE_ENDIAN
551 /*
552 * little_endian - swap the significants bits to cater to bigendian
553 * @stupidblock - the block of data we're swapping
554 * @blocks - the number of blocks we're swapping
555 */
556 static void
little_endian(hblock * stupidblock,int blocks)557 little_endian(hblock * stupidblock, int blocks)
558 {
559 int x;
560 for (x = 0; x < blocks; x++)
561 {
562 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]);
563 }
564 }
565 #endif
566
567
568