[libgpod] Add hashDB support

Bastien Nocera hadess at fedoraproject.org
Thu Jul 14 17:02:51 UTC 2011


commit 569e6c52b2520136c9b742a9afcd1ba41159efb0
Author: Bastien Nocera <hadess at hadess.net>
Date:   Thu Jul 14 18:02:37 2011 +0100

    Add hashDB support

 0001-add-hashAB-support-via-external-module.patch  |  464 ++++++++++++++++++++
 ...s_cuid-mhsd-type-9-and-also-write-it-back.patch |  209 +++++++++
 0003-write-empty-mhsd-of-type-10.patch             |   67 +++
 libgpod.spec                                       |   19 +-
 4 files changed, 757 insertions(+), 2 deletions(-)
---
diff --git a/0001-add-hashAB-support-via-external-module.patch b/0001-add-hashAB-support-via-external-module.patch
new file mode 100644
index 0000000..e31f432
--- /dev/null
+++ b/0001-add-hashAB-support-via-external-module.patch
@@ -0,0 +1,464 @@
+From 631669274078916a601ae1a4d56f41cb5236d842 Mon Sep 17 00:00:00 2001
+From: Nikias Bassen <nikias at gmx.li>
+Date: Mon, 4 Oct 2010 01:15:39 +0200
+Subject: [PATCH 1/3] add hashAB support via external module
+
+---
+ configure.ac           |    9 ++
+ src/Makefile.am        |    1 +
+ src/db-itunes-parser.h |    2 +
+ src/itdb_device.c      |    1 +
+ src/itdb_hashAB.c      |  196 ++++++++++++++++++++++++++++++++++++++++++++++++
+ src/itdb_itunesdb.c    |   30 ++++++--
+ src/itdb_private.h     |    8 ++
+ src/itdb_sqlite.c      |   54 ++++++++++----
+ 8 files changed, 278 insertions(+), 23 deletions(-)
+ create mode 100644 src/itdb_hashAB.c
+
+diff --git a/configure.ac b/configure.ac
+index 370a192..5858c57 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -217,6 +217,15 @@ AC_SUBST(TMPMOUNTDIR)
+ AH_TEMPLATE([TMPMOUNTDIR], [Directory where HAL/udev will create a sub-directory to mount iPods])
+ AC_DEFINE_UNQUOTED(TMPMOUNTDIR, "$with_temp_mount_dir", [Directory where HAL/udev will create a sub-directory to mount iPods])
+ 
++dnl ***********************************************************************
++dnl * provide a $l{ibdir}/libgpod directory for external modules
++dnl * available via config.h as LIBGPOD_LIBDIR
++dnl ***********************************************************************
++
++test "x${exec_prefix}" = "xNONE" && exec_prefix='${prefix}'
++LIBGPOD_LIBDIR=$(eval "echo \"$(eval "echo \"${libdir}\"")\"")/libgpod
++AC_DEFINE_UNQUOTED(LIBGPOD_LIBDIR,"$LIBGPOD_LIBDIR", [libgpod libdir])
++
+ dnl **************************************************
+ dnl * TagLib is only used by test-rebuild-db 
+ dnl **************************************************
+diff --git a/src/Makefile.am b/src/Makefile.am
+index ce02831..1790d4f 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -11,6 +11,7 @@ libgpod_la_SOURCES = 		\
+ 	itdb_device.c  		\
+ 	itdb_hash58.c 		\
+ 	itdb_hash72.c		\
++	itdb_hashAB.c		\
+ 	itdb_iphone.c		\
+ 	itdb_itunesdb.c 	\
+ 	itdb_photoalbum.c 	\
+diff --git a/src/db-itunes-parser.h b/src/db-itunes-parser.h
+index cc71bcc..80d5213 100644
+--- a/src/db-itunes-parser.h
++++ b/src/db-itunes-parser.h
+@@ -102,6 +102,8 @@ struct _MhbdHeader {
+ 	guint16 unk_0xa4;
+ 	guint16 unk_0xa6;
+ 	guint16 unk_0xa8;
++	guchar align_0xa9;
++	guchar hashAB[57];
+ 	guchar  padding[];
+ } __attribute__((__packed__));
+ 
+diff --git a/src/itdb_device.c b/src/itdb_device.c
+index fd256b4..bdc3dc3 100644
+--- a/src/itdb_device.c
++++ b/src/itdb_device.c
+@@ -1920,6 +1920,7 @@ G_GNUC_INTERNAL gboolean itdb_device_write_checksum (Itdb_Device *device,
+ 	case ITDB_CHECKSUM_HASH72:
+ 	    return itdb_hash72_write_hash (device, itdb_data, itdb_len, error);
+         case ITDB_CHECKSUM_HASHAB:
++	    return itdb_hashAB_write_hash (device, itdb_data, itdb_len, error);
+ 	case ITDB_CHECKSUM_UNKNOWN:
+             g_set_error (error, 0, -1, "Unsupported checksum type");
+ 	    return FALSE;
+diff --git a/src/itdb_hashAB.c b/src/itdb_hashAB.c
+new file mode 100644
+index 0000000..6b64adb
+--- /dev/null
++++ b/src/itdb_hashAB.c
+@@ -0,0 +1,196 @@
++/*
++|  Copyright (c) 2010 Nikias Bassen <nikias at gmx.li>
++| 
++|  The code contained in this file is free software; you can redistribute
++|  it and/or modify it under the terms of the GNU Lesser General Public
++|  License as published by the Free Software Foundation; either version
++|  2.1 of the License, or (at your option) any later version.
++|
++|  This file is distributed in the hope that it will be useful,
++|  but WITHOUT ANY WARRANTY; without even the implied warranty of
++|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++|  Lesser General Public License for more details.
++|
++|  You should have received a copy of the GNU Lesser General Public
++|  License along with this code; if not, write to the Free Software
++|  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
++|  USA
++|
++|  iTunes and iPod are trademarks of Apple
++|
++|  This product is not supported/written/published by Apple!
++*/
++
++#if HAVE_CONFIG_H
++#include <config.h>
++#endif
++#include <stdio.h>
++#include <stdint.h>
++#include <string.h>
++#include "rijndael.h"
++
++#include <glib/gstdio.h>
++
++#include <gmodule.h>
++
++#include "itdb_device.h"
++#include "db-itunes-parser.h"
++#include "itdb_private.h"
++
++typedef void (*calcHashAB_t)(unsigned char target[57], const unsigned char sha1[20], const unsigned char uuid[20], const unsigned char rnd_bytes[23]);
++static calcHashAB_t calc_hashAB = NULL;
++
++static gboolean load_libhashab()
++{
++	gchar *path;
++	GModule *handle;
++
++	if (!g_module_supported()) {
++		return FALSE;
++	}
++	path = g_module_build_path(LIBGPOD_LIBDIR, "hashab");
++	handle = g_module_open(path, G_MODULE_BIND_LAZY);
++	if (!handle) {
++		g_free(path);
++		return FALSE;
++	}
++	g_free(path);
++
++	if (!g_module_symbol(handle, "calcHashAB", (void**)&calc_hashAB)) {
++		g_module_close(handle);
++		g_warning("symbol 'calcHashAB' not found");
++		return FALSE;
++	}
++	g_module_make_resident(handle);
++
++	printf("***** hashAB support successfully loaded *****\n");
++
++	return TRUE;
++}
++
++static void itdb_hashAB_compute_itunesdb_sha1 (unsigned char *itdb_data, 
++					       gsize itdb_len,
++					       unsigned char sha1[20])
++{
++    guchar backup18[8];
++    guchar backup32[20];
++    guchar hashAB[57];
++    gsize sha1_len;
++    GChecksum *checksum;
++    MhbdHeader *header;
++
++    g_assert (itdb_len >= 0x6c);
++
++    header = (MhbdHeader *)itdb_data;
++    g_assert (strncmp (header->header_id, "mhbd", strlen ("mhbd")) == 0);
++    memcpy (backup18, &header->db_id, sizeof (backup18));
++    memcpy (backup32, &header->unk_0x32, sizeof (backup32));
++    memcpy (hashAB, &header->hashAB, sizeof (hashAB));
++
++    /* Those fields must be zero'ed out for the sha1 calculation */
++    memset(&header->db_id, 0, sizeof (header->db_id));
++
++    memset(&header->hash58, 0, sizeof (header->hash58));
++    memset(&header->hash72, 0, sizeof (header->hash72));
++    memset(&header->hashAB, 0, sizeof (header->hashAB));
++
++    sha1_len = g_checksum_type_get_length (G_CHECKSUM_SHA1);
++    checksum = g_checksum_new (G_CHECKSUM_SHA1);
++    g_checksum_update (checksum, itdb_data, itdb_len);
++    g_checksum_get_digest (checksum, sha1, &sha1_len);
++    g_checksum_free (checksum);
++
++    memcpy (&header->db_id, backup18, sizeof (backup18));
++    memcpy (&header->unk_0x32, backup32, sizeof (backup32));
++}
++
++static int ord_from_hex_char(const char c)
++{
++  if ('0' <= c && c <= '9')
++    return c - '0';
++  else if ('a' <= c && c <= 'f')
++    return 10 + (c - 'a');
++  else if ('A' <= c && c <= 'F')
++    return 10 + (c - 'A');
++  else
++    return -1;
++}
++
++static int string_to_hex(unsigned char *dest, const int array_size,
++			 const char *s)
++{
++  /* skip optional '0x' prefix */
++  if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
++    s += 2;
++
++  if (strlen(s) > array_size*2)
++    return -8;
++
++  do {
++    int low, high;
++    if((high = ord_from_hex_char(s[0])) == -1 ||
++       (low = ord_from_hex_char(s[1])) == -1)
++      return -9;
++    *dest++ = high<<4 | low;
++  } while(*(s+=2));
++  return 0;
++}
++
++static gboolean get_uuid (const Itdb_Device *device, unsigned char uuid[20])
++{
++    const char *uuid_str;
++    int result;
++
++    uuid_str = itdb_device_get_uuid (device);
++    if (uuid_str == NULL) {
++	uuid_str = itdb_device_get_firewire_id (device);
++    }
++    if (uuid_str == NULL) {
++	return FALSE;
++    }
++    memset (uuid, 0, 20);
++    result = string_to_hex (uuid, 20, uuid_str);
++
++    return (result == 0);
++}
++
++gboolean itdb_hashAB_compute_hash_for_sha1 (const Itdb_Device *device, 
++					    const guchar sha1[20],
++					    guchar signature[57],
++					    GError **error)
++{
++    unsigned char uuid[20];
++    unsigned char rndpart[23] = "ABCDEFGHIJKLMNOPQRSTUVW";
++
++    if (calc_hashAB == NULL) {
++	if (!load_libhashab()) {
++	    g_set_error (error, 0, -1, "Unsupported checksum type");
++	    return FALSE;
++	}
++    }
++
++    if (!get_uuid(device, uuid)) return FALSE;
++
++    calc_hashAB(signature, sha1, uuid, rndpart);
++
++    return TRUE;
++}
++
++gboolean itdb_hashAB_write_hash (const Itdb_Device *device, 
++				 unsigned char *itdb_data, 
++				 gsize itdb_len,
++				 GError **error)
++{
++    guchar sha1[20];
++    MhbdHeader *header;
++
++    if (itdb_len < 0xF4) {
++	g_set_error (error, 0, -1, "iTunesDB file too small to write checksum");
++	return FALSE;
++    }
++
++    header = (MhbdHeader *)itdb_data;
++    header->hashing_scheme = GUINT16_FROM_LE (ITDB_CHECKSUM_HASHAB);
++    itdb_hashAB_compute_itunesdb_sha1 (itdb_data, itdb_len, sha1);
++    return itdb_hashAB_compute_hash_for_sha1 (device, sha1, header->hashAB, error);
++}
+diff --git a/src/itdb_itunesdb.c b/src/itdb_itunesdb.c
+index 18fc780..bc55f4a 100644
+--- a/src/itdb_itunesdb.c
++++ b/src/itdb_itunesdb.c
+@@ -3731,7 +3731,7 @@ static void mk_mhbd (FExport *fexp, guint32 children)
+   cts = fexp->wcontents;
+ 
+   put_header (cts, "mhbd");
+-  put32lint (cts, 188); /* header size */
++  put32lint (cts, 244); /* header size */
+   put32lint (cts, -1);  /* size of whole mhdb -- fill in later */
+   if (itdb_device_supports_compressed_itunesdb (fexp->itdb->device)) {
+       /* 2 on iPhone 3.0 and Nano 5G, 1 on iPod Color and iPod Classic
+@@ -3760,10 +3760,12 @@ static void mk_mhbd (FExport *fexp, guint32 children)
+                      0x19 = iTunes 7.4
+                      0x28 = iTunes 8.2.1
+ 		     0x2a = iTunes 9.0.1
++		     0x2e = iTunes 9.1
++		     0x30 = iTunes 9.2
+     Be aware that newer ipods won't work if the library version number is too 
+     old
+   */
+-  fexp->itdb->version = 0x2a;
++  fexp->itdb->version = 0x30;
+   put32lint (cts, fexp->itdb->version);
+   put32lint (cts, children);
+   put64lint (cts, fexp->itdb->id);
+@@ -3787,20 +3789,32 @@ static void mk_mhbd (FExport *fexp, guint32 children)
+                                                 /* seen: 0x01 for iPod Color       */
+   put32lint (cts, fexp->itdb->priv->unk_0x54);  /* unknown: seen: 0x4d for nano 3G */
+   				         	/* seen: 0x0f for iPod Color       */
+-  put32_n0 (cts, 5);    /* 20 bytes hash */
++  put32_n0 (cts, 5);    /* 20 bytes hash58 */
+   put32lint (cts, fexp->itdb->tzoffset);   /* timezone offset in seconds */
+   /* 0x70 */
+-  put16lint (cts, 2);   /* without it, iTunes thinks iPhone databases
+-			   are corrupted, 0 on iPod Color */
+-  put16lint (cts, 0);
+-  put32_n0 (cts, 11);   /* new hash */
++  switch (itdb_device_get_checksum_type(fexp->itdb->device)) {
++    case ITDB_CHECKSUM_HASHAB:
++      put16lint (cts, 4);   /* new on i4 */
++      break;
++    case ITDB_CHECKSUM_HASH72:
++      put16lint (cts, 2);
++      break;
++    default:
++      put16lint (cts, 0);
++      break;
++  }
++  put16lint (cts, 0);   /* hash72 */
++  put32_n0 (cts, 11);   /* hash72 */
+   /* 0xa0 */
+   put16lint (cts, fexp->itdb->priv->audio_language); /* audio_language */
+   put16lint (cts, fexp->itdb->priv->subtitle_language); /* subtitle_language */
+   put16lint (cts, fexp->itdb->priv->unk_0xa4); /* unknown */
+   put16lint (cts, fexp->itdb->priv->unk_0xa6); /* unknown */
+   put16lint (cts, fexp->itdb->priv->unk_0xa8); /* unknown */
+-  put16lint (cts, 0);
++  put8int (cts, 0);
++  /* 0xab */
++  put8int (cts, 0);   /* hashAB */
++  put32_n0 (cts, 14); /* hashAB */
+   put32_n0 (cts, 4); /* dummy space */
+ }
+ 
+diff --git a/src/itdb_private.h b/src/itdb_private.h
+index 7f78967..4eec250 100644
+--- a/src/itdb_private.h
++++ b/src/itdb_private.h
+@@ -229,6 +229,10 @@ G_GNUC_INTERNAL guint64 device_time_time_t_to_mac (Itdb_Device *device,
+ 						 time_t timet);
+ G_GNUC_INTERNAL gint itdb_musicdirs_number_by_mountpoint (const gchar *mountpoint);
+ G_GNUC_INTERNAL int itdb_sqlite_generate_itdbs(FExport *fexp);
++G_GNUC_INTERNAL gboolean itdb_hashAB_write_hash (const Itdb_Device *device, 
++						 unsigned char *itdb_data, 
++						 gsize itdb_len,
++						 GError **error);
+ G_GNUC_INTERNAL gboolean itdb_hash72_extract_hash_info(const Itdb_Device *device, 
+ 						       unsigned char *itdb_data, 
+ 						       gsize itdb_len);
+@@ -240,6 +244,10 @@ G_GNUC_INTERNAL gboolean itdb_hash58_write_hash (Itdb_Device *device,
+ 						 unsigned char *itdb_data, 
+ 						 gsize itdb_len,
+ 						 GError **error);
++G_GNUC_INTERNAL gboolean itdb_hashAB_compute_hash_for_sha1 (const Itdb_Device *device, 
++							    const guchar sha1[20],
++							    guchar signature[57],
++							    GError **error);
+ G_GNUC_INTERNAL gboolean itdb_hash72_compute_hash_for_sha1 (const Itdb_Device *device, 
+ 							    const guchar sha1[20],
+ 							    guchar signature[46],
+diff --git a/src/itdb_sqlite.c b/src/itdb_sqlite.c
+index b107e4e..974aa6c 100644
+--- a/src/itdb_sqlite.c
++++ b/src/itdb_sqlite.c
+@@ -1860,24 +1860,22 @@ error:
+     return FALSE;
+ }
+ 
+-static const guint CBK_HEADER_SIZE = 46;
+-
+-static void cbk_calc_sha1_of_sha1s(GArray *cbk)
++static void cbk_calc_sha1_of_sha1s(GArray *cbk, guint cbk_header_size)
+ {
+     GChecksum *checksum;
+     unsigned char* final_sha1;
+     unsigned char* sha1s;
+     gsize final_sha1_len;
+ 
+-    g_assert (cbk->len > CBK_HEADER_SIZE + 20);
++    g_assert (cbk->len > cbk_header_size + 20);
+ 
+-    final_sha1 = &g_array_index(cbk, guchar, CBK_HEADER_SIZE);
+-    sha1s = &g_array_index(cbk, guchar, CBK_HEADER_SIZE + 20);
++    final_sha1 = &g_array_index(cbk, guchar, cbk_header_size);
++    sha1s = &g_array_index(cbk, guchar, cbk_header_size + 20);
+     final_sha1_len = g_checksum_type_get_length(G_CHECKSUM_SHA1);
+     g_assert (final_sha1_len == 20);
+ 
+     checksum = g_checksum_new(G_CHECKSUM_SHA1);
+-    g_checksum_update(checksum, sha1s, cbk->len - (CBK_HEADER_SIZE + 20));
++    g_checksum_update(checksum, sha1s, cbk->len - (cbk_header_size + 20));
+     g_checksum_get_digest(checksum, final_sha1, &final_sha1_len);
+     g_checksum_free(checksum);
+ }
+@@ -1888,12 +1886,30 @@ static gboolean mk_Locations_cbk(Itdb_iTunesDB *itdb, const char *dirname)
+     char *cbk_filename;
+     GArray *cbk;
+     gboolean success;
+-    guchar *cbk_hash72;
++    guchar *cbk_hash;
+     guchar *final_sha1;
++    guint cbk_header_size = 0;
++    guint checksum_type;
++
++    checksum_type = itdb_device_get_checksum_type(itdb->device);
++    switch (checksum_type) {
++	case ITDB_CHECKSUM_HASHAB:
++	    cbk_header_size = 57;
++	    break;
++	case ITDB_CHECKSUM_HASH72:
++	    cbk_header_size = 46;
++	    break;
++	default:
++	    break;
++    }
++    if (cbk_header_size == 0) {
++	fprintf(stderr, "ERROR: Unsupported checksum type '%d' in cbk file generation!\n", checksum_type);
++	return FALSE;
++    }
+ 
+     cbk = g_array_sized_new(FALSE, TRUE, 1,
+-			    CBK_HEADER_SIZE + 20);
+-    g_array_set_size(cbk, CBK_HEADER_SIZE + 20);
++			    cbk_header_size + 20);
++    g_array_set_size(cbk, cbk_header_size + 20);
+ 
+     locations_filename = g_build_filename(dirname, "Locations.itdb", NULL);
+     success = cbk_calc_sha1s(locations_filename, cbk);
+@@ -1902,11 +1918,19 @@ static gboolean mk_Locations_cbk(Itdb_iTunesDB *itdb, const char *dirname)
+ 	g_array_free(cbk, TRUE);
+ 	return FALSE;
+     }
+-    cbk_calc_sha1_of_sha1s(cbk);
+-    final_sha1 = &g_array_index(cbk, guchar, CBK_HEADER_SIZE);
+-    cbk_hash72 = &g_array_index(cbk, guchar, 0);
+-    success = itdb_hash72_compute_hash_for_sha1 (itdb->device, final_sha1,
+-						 cbk_hash72, NULL);
++    cbk_calc_sha1_of_sha1s(cbk, cbk_header_size);
++    final_sha1 = &g_array_index(cbk, guchar, cbk_header_size);
++    cbk_hash = &g_array_index(cbk, guchar, 0);
++    switch (checksum_type) {
++	case ITDB_CHECKSUM_HASHAB:
++	    success = itdb_hashAB_compute_hash_for_sha1 (itdb->device, final_sha1, cbk_hash, NULL);
++	    break;
++	case ITDB_CHECKSUM_HASH72:
++	    success = itdb_hash72_compute_hash_for_sha1 (itdb->device, final_sha1, cbk_hash, NULL);
++	    break;
++	default:
++	    break;
++    }
+     if (!success) {
+ 	g_array_free(cbk, TRUE);
+ 	return FALSE;
+-- 
+1.7.3.2
+
diff --git a/0002-read-genius_cuid-mhsd-type-9-and-also-write-it-back.patch b/0002-read-genius_cuid-mhsd-type-9-and-also-write-it-back.patch
new file mode 100644
index 0000000..f645a8e
--- /dev/null
+++ b/0002-read-genius_cuid-mhsd-type-9-and-also-write-it-back.patch
@@ -0,0 +1,209 @@
+From 4e9548b131ed9fc8f535e6a6840544f69eb230c4 Mon Sep 17 00:00:00 2001
+From: Nikias Bassen <nikias at gmx.li>
+Date: Mon, 4 Oct 2010 01:56:52 +0200
+Subject: [PATCH 2/3] read genius_cuid (mhsd type 9) and also write it back
+
+This new mhsd of type 9 is new since iOS4. It just holds the genius cuid
+if genius is activated. Otherwise, this mhsd node is not written to the
+iTunesCDB file. Also updates the genius_cuid field in the db_info sqlite
+table if available.
+---
+ src/itdb_itunesdb.c |   94 +++++++++++++++++++++++++++++++++++++++++++++++++-
+ src/itdb_private.h  |    1 +
+ src/itdb_sqlite.c   |    7 +++-
+ 3 files changed, 98 insertions(+), 4 deletions(-)
+
+diff --git a/src/itdb_itunesdb.c b/src/itdb_itunesdb.c
+index bc55f4a..4e2debb 100644
+--- a/src/itdb_itunesdb.c
++++ b/src/itdb_itunesdb.c
+@@ -1335,6 +1335,7 @@ void itdb_free (Itdb_iTunesDB *itdb)
+ 	itdb_device_free (itdb->device);
+ 	if (itdb->userdata && itdb->userdata_destroy)
+ 	    (*itdb->userdata_destroy) (itdb->userdata);
++	g_free (itdb->priv->genius_cuid);
+ 	g_free (itdb->priv);
+ 	g_free (itdb);
+     }
+@@ -2988,6 +2989,47 @@ static gboolean parse_playlists (FImport *fimp, glong mhsd_seek)
+     return TRUE;
+ }
+ 
++static gboolean parse_genius_mhsd(FImport *fimp, glong mhsd_seek)
++{
++    FContents *cts;
++    guint32 hdrlen, mhsdlen;
++    gint32 len;
++    gchar *genius_cuid;
++
++    g_return_val_if_fail (fimp, FALSE);
++    g_return_val_if_fail (fimp->itdb, FALSE);
++    g_return_val_if_fail (fimp->fcontents, FALSE);
++    g_return_val_if_fail (fimp->fcontents->filename, FALSE);
++    g_return_val_if_fail (mhsd_seek >= 0, FALSE);
++
++    cts = fimp->fcontents;
++
++    g_return_val_if_fail (check_header_seek (cts, "mhsd", mhsd_seek),
++			  FALSE);
++
++    hdrlen = get32lint (cts, mhsd_seek+4);
++    mhsdlen = get32lint (cts, mhsd_seek+8);
++
++    len = mhsdlen - hdrlen;
++    if (len < 0) {
++	return FALSE;
++    }
++
++    if (len != 32) {
++	g_warning(_("%s: Unexpected length %d for genius_cuid!\n"), __func__, len);
++    }
++
++    genius_cuid = g_new0(gchar, len+1);
++    if (!seek_get_n_bytes (cts, genius_cuid, mhsd_seek+hdrlen, len)) {
++	g_free (genius_cuid);
++	return FALSE;
++    }
++
++    fimp->itdb->priv->genius_cuid = genius_cuid;
++
++    return TRUE;
++}
++
+ static gboolean looks_like_itunesdb (FImport *fimp)
+ {
+     FContents *cts;
+@@ -3019,7 +3061,7 @@ static gboolean parse_fimp (FImport *fimp, gboolean compressed)
+ {
+     glong seek=0;
+     FContents *cts;
+-    glong mhsd_1, mhsd_2, mhsd_3, mhsd_5;
++    glong mhsd_1, mhsd_2, mhsd_3, mhsd_5, mhsd_9;
+ 
+     g_return_val_if_fail (fimp, FALSE);
+     g_return_val_if_fail (fimp->itdb, FALSE);
+@@ -3053,6 +3095,10 @@ static gboolean parse_fimp (FImport *fimp, gboolean compressed)
+     mhsd_5 = find_mhsd (cts, 5);
+     CHECK_ERROR (fimp, FALSE);
+ 
++    /* read genius cuid mhsd */
++    mhsd_9 = find_mhsd (cts, 9);
++    CHECK_ERROR (fimp, FALSE);
++
+     fimp->itdb->version = get32lint (cts, seek+0x10);
+     CHECK_ERROR (fimp, FALSE);
+     fimp->itdb->id = get64lint (cts, seek+0x18);
+@@ -3130,6 +3176,10 @@ static gboolean parse_fimp (FImport *fimp, gboolean compressed)
+ 	parse_playlists (fimp, mhsd_5);
+     }
+ 
++    if (mhsd_9 != -1) {
++	parse_genius_mhsd (fimp, mhsd_9);
++    }
++
+     return TRUE;
+ }
+ 
+@@ -5585,6 +5635,26 @@ static gboolean write_mhsd_type6 (FExport *fexp)
+     return TRUE;
+ }
+ 
++static gboolean write_genius_mhsd (FExport *fexp)
++{
++    gulong mhsd_seek;
++    WContents *cts;
++
++    g_return_val_if_fail (fexp, FALSE);
++    g_return_val_if_fail (fexp->itdb, FALSE);
++    g_return_val_if_fail (fexp->wcontents, FALSE);
++
++    if (!fexp->itdb->priv->genius_cuid) {
++        return TRUE;
++    }
++    cts = fexp->wcontents;
++    mhsd_seek = cts->pos;      	/* get position of mhsd header */
++    mk_mhsd (fexp, 9); 		/* write header */
++    put_string (cts, fexp->itdb->priv->genius_cuid);
++    fix_header (cts, mhsd_seek);
++    return TRUE;
++}
++
+ /* create a WContents structure */
+ static WContents *wcontents_new (const gchar *filename)
+ {
+@@ -5836,6 +5906,7 @@ static gboolean itdb_write_file_internal (Itdb_iTunesDB *itdb,
+     gulong mhbd_seek = 0;
+     WContents *cts;
+     gboolean result = TRUE;
++    guint32 num_mhsds;
+ 
+     g_return_val_if_fail (itdb, FALSE);
+     g_return_val_if_fail (itdb->device, FALSE);
+@@ -5870,7 +5941,15 @@ static gboolean itdb_write_file_internal (Itdb_iTunesDB *itdb,
+     }
+ #endif
+ 
+-    mk_mhbd (fexp, 7);  /* seven mhsds */
++    /* default mhsd count */
++    num_mhsds = 7; /* seven mhsds */
++
++    /* if genius_cuid present, we have one more */
++    if (fexp->itdb->priv->genius_cuid) {
++	num_mhsds++;
++    }
++
++    mk_mhbd (fexp, num_mhsds);
+ 
+     /* write tracklist (mhsd type 1) */
+     if (!fexp->error && !write_mhsd_tracks (fexp)) {
+@@ -5933,6 +6012,17 @@ static gboolean itdb_write_file_internal (Itdb_iTunesDB *itdb,
+ 	goto err;
+     }
+ 
++    if (fexp->itdb->priv->genius_cuid) {
++	/* write genius cuid (mhsd type 9) */
++	if (!fexp->error && !write_genius_mhsd (fexp)) {
++	    g_set_error (&fexp->error,
++		    ITDB_FILE_ERROR,
++		    ITDB_FILE_ERROR_ITDB_CORRUPT,
++		    _("Error writing mhsd type 9"));
++	    goto err;
++    	}
++    }
++
+     fix_header (cts, mhbd_seek);
+ 
+     if (itdb_device_supports_compressed_itunesdb (itdb->device)) {
+diff --git a/src/itdb_private.h b/src/itdb_private.h
+index 4eec250..842250d 100644
+--- a/src/itdb_private.h
++++ b/src/itdb_private.h
+@@ -198,6 +198,7 @@ struct _Itdb_iTunesDB_Private
+     gint16 unk_0xa4;
+     gint16 unk_0xa6;
+     gint16 unk_0xa8;
++    gchar *genius_cuid;
+ };
+ 
+ /* private data for Itdb_Track */
+diff --git a/src/itdb_sqlite.c b/src/itdb_sqlite.c
+index 974aa6c..0f7272b 100644
+--- a/src/itdb_sqlite.c
++++ b/src/itdb_sqlite.c
+@@ -894,8 +894,11 @@ static int mk_Library(Itdb_iTunesDB *itdb,
+     /*  this is +0xA2 */
+     sqlite3_bind_int(stmt_db_info, ++idx, itdb->priv->subtitle_language);
+     /* genius cuid */
+-    /* TODO: unkown meaning, set to NULL */
+-    sqlite3_bind_null(stmt_db_info, ++idx);
++    if (itdb->priv->genius_cuid) {
++	sqlite3_bind_text(stmt_db_info, ++idx, itdb->priv->genius_cuid, -1, SQLITE_STATIC);
++    } else {
++	sqlite3_bind_null(stmt_db_info, ++idx);
++    }
+     /* bib */
+     /* TODO: unkown meaning, set to NULL */
+     sqlite3_bind_null(stmt_db_info, ++idx);
+-- 
+1.7.3.2
+
diff --git a/0003-write-empty-mhsd-of-type-10.patch b/0003-write-empty-mhsd-of-type-10.patch
new file mode 100644
index 0000000..c951cf8
--- /dev/null
+++ b/0003-write-empty-mhsd-of-type-10.patch
@@ -0,0 +1,67 @@
+From 157bddcb019f4cacc1808c4664d4f113d4b08f16 Mon Sep 17 00:00:00 2001
+From: Nikias Bassen <nikias at gmx.li>
+Date: Mon, 4 Oct 2010 02:00:47 +0200
+Subject: [PATCH 3/3] write empty mhsd of type 10
+
+Found since iOS4. We don't know yet what this is for -- this is for
+completeness.
+---
+ src/itdb_itunesdb.c |   28 +++++++++++++++++++++++++++-
+ 1 files changed, 27 insertions(+), 1 deletions(-)
+
+diff --git a/src/itdb_itunesdb.c b/src/itdb_itunesdb.c
+index 4e2debb..e08988f 100644
+--- a/src/itdb_itunesdb.c
++++ b/src/itdb_itunesdb.c
+@@ -5655,6 +5655,23 @@ static gboolean write_genius_mhsd (FExport *fexp)
+     return TRUE;
+ }
+ 
++static gboolean write_mhsd_type10 (FExport *fexp)
++{
++    gulong mhsd_seek;
++    WContents *cts;
++
++    g_return_val_if_fail (fexp, FALSE);
++    g_return_val_if_fail (fexp->itdb, FALSE);
++    g_return_val_if_fail (fexp->wcontents, FALSE);
++
++    cts = fexp->wcontents;
++    mhsd_seek = cts->pos;      	/* get position of mhsd header */
++    mk_mhsd (fexp, 10); 		/* write header */
++    mk_mhlt (fexp, 0);	/* for now, produce an empty set */
++    fix_header (cts, mhsd_seek);
++    return TRUE;
++}
++
+ /* create a WContents structure */
+ static WContents *wcontents_new (const gchar *filename)
+ {
+@@ -5942,7 +5959,7 @@ static gboolean itdb_write_file_internal (Itdb_iTunesDB *itdb,
+ #endif
+ 
+     /* default mhsd count */
+-    num_mhsds = 7; /* seven mhsds */
++    num_mhsds = 8; /* eight mhsds */
+ 
+     /* if genius_cuid present, we have one more */
+     if (fexp->itdb->priv->genius_cuid) {
+@@ -6003,6 +6020,15 @@ static gboolean itdb_write_file_internal (Itdb_iTunesDB *itdb,
+ 	goto err;
+     }
+ 
++    /* write empty mhsd type 10, whatever it is */
++    if (!fexp->error && !write_mhsd_type10 (fexp)) {
++	g_set_error (&fexp->error,
++		     ITDB_FILE_ERROR,
++		     ITDB_FILE_ERROR_ITDB_CORRUPT,
++		     _("Error writing mhsd type 10"));
++	goto err;
++    }
++
+     /* write mhsd5 playlists */
+     if (!fexp->error && !write_mhsd_playlists (fexp, 5)) {
+ 	g_set_error (&fexp->error,
+-- 
+1.7.3.2
+
diff --git a/libgpod.spec b/libgpod.spec
index ae3c568..b6f7b9b 100644
--- a/libgpod.spec
+++ b/libgpod.spec
@@ -9,13 +9,19 @@
 Summary: Library to access the contents of an iPod
 Name: libgpod
 Version: 0.8.0
-Release: 9%{?dist}
+Release: 10%{?dist}
 License: LGPLv2+
 Group: System Environment/Libraries
 URL: http://www.gtkpod.org/libgpod.html
 Source0: http://downloads.sourceforge.net/gtkpod/%{name}-%{version}.tar.gz
 # http://sourceforge.net/tracker/index.php?func=detail&aid=3059994&group_id=67873&atid=519273
 Patch0: libgpod-0.8.0-x86-32.patch
+# From http://gtkpod.git.sourceforge.net/git/gitweb.cgi?p=gtkpod/libgpod;a=summary
+Patch1: 0001-add-hashAB-support-via-external-module.patch
+Patch2: 0002-read-genius_cuid-mhsd-type-9-and-also-write-it-back.patch
+Patch3: 0003-write-empty-mhsd-of-type-10.patch
+BuildRequires: automake libtool
+
 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
 BuildRequires: docbook-style-xsl
 BuildRequires: glib2-devel
@@ -119,6 +125,10 @@ libgpod-sharp.
 %prep
 %setup -q
 %patch0 -p1 -b .x86-32
+%patch1 -p1 -b .hashAB
+%patch2 -p1 -b .genius
+%patch3 -p1 -b .mhsd
+autoreconf -f
 
 # remove execute perms on the python examples as they'll be installed in %%doc
 chmod -x bindings/python/examples/*.py
@@ -135,6 +145,8 @@ rm -rf %{buildroot}
 make DESTDIR=%{buildroot} install
 %find_lang %{name}
 
+mkdir -p %{buildroot}/%{_libdir}/libgpod
+
 # remove Makefiles from the python examples dir
 rm -rf bindings/python/examples/Makefile*
 
@@ -174,7 +186,7 @@ rm -rf %{buildroot}
 /lib/udev/iphone-set-info
 /lib/udev/ipod-set-info
 /lib/udev/rules.d/*.rules
-
+%{_libdir}/libgpod/
 
 %files devel
 %defattr(-, root, root, 0755)
@@ -209,6 +221,9 @@ rm -rf %{buildroot}
 %endif
 
 %changelog
+* Thu Jul 14 2011 Bastien Nocera <bnocera at redhat.com> 0.8.0-10
+- Add hashDB support
+
 * Wed May 25 2011 Todd Zullinger <tmz at pobox.com> - 0.8.0-9
 - Fix tmpfiles.d user/group for /var/run/libgpod (#707787)
 


More information about the scm-commits mailing list