Patch: support for deflated iTunesCDB from iPhone OS 3.0

View: New views
2 Messages — Rating Filter:   Alert me  

Patch: support for deflated iTunesCDB from iPhone OS 3.0

by Bugzilla from aumuell@reserv.at :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi,

please find attached a patch that
- makes libgpod prefer iTunesCDB over iTunesDB
- inflates everything after the 188 header on reading when the file is named
iTunesCDB
- deflates everything after the header in the same case
together with another patch that
- adds a simple test program that converts between iTunesCDB and iTunesDB.

Please let me know if I should change anything.

Regards,
Martin

[0001-uncompress-iTunesCDB-files-before-parsing-recompres.patch]

From 83b6a8ebd0a91c99efe533299cfd4fc3f23c39be Mon Sep 17 00:00:00 2001
From: Martin Aumueller <aumuell@...>
Date: Mon, 13 Jul 2009 07:19:21 +0200
Subject: [PATCH 1/2] uncompress iTunesCDB files before parsing, recompress before writing

iPhoneOS 3.0 with iTunes 8.2 seems to prefer a 'deflate' compressed version
of iTunesDB with the filename iTunesCDB over iTunesDB
---
 configure.ac        |   18 +++++
 src/Makefile.am     |    2 +-
 src/itdb.h          |    3 +-
 src/itdb_itunesdb.c |  210 +++++++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 226 insertions(+), 7 deletions(-)

diff --git a/configure.ac b/configure.ac
index a8cfad0..2318c62 100644
--- a/configure.ac
+++ b/configure.ac
@@ -93,6 +93,24 @@ fi
 AC_SUBST(SGUTILS_LIBS)
 AM_CONDITIONAL(HAVE_SGUTILS, test x"$have_sgutils" = xyes)
 
+dnl **************************************************
+dnl * zlib is neeeded for handling compressed iTunesCDB files
+dnl **************************************************
+AC_CHECK_LIB(z, inflate,
+             [Z_LIBS="-lz"; have_zlib=yes],
+             have_zlib=no)
+if test "x$have_zlib" != xyes; then
+    AC_CHECK_LIB(zlib, inflate,
+                 [Z_LIBS="-lzlib"; have_zlib=yes],
+                 have_zlib=no)
+fi
+if test x"$have_zlib" = xyes; then
+   AH_TEMPLATE([HAVE_ZLIB], [Whether zlib is installed, it's used for compressed iTunesCDB])
+   AC_DEFINE_UNQUOTED(HAVE_ZLIB, 1)
+fi
+AC_SUBST(Z_LIBS)
+AM_CONDITIONAL(HAVE_ZLIB, test x"$have_zlib" = xyes)
+
 dnl ***********************************************************************
 dnl * HAL is optional, but is required if you want things to "just work"
 dnl * when a recent iPod is plugged in
diff --git a/src/Makefile.am b/src/Makefile.am
index 962c591..9cfdbd4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -50,7 +50,7 @@ noinst_HEADERS = \
  sha1.h
 
 INCLUDES=$(LIBGPOD_CFLAGS)
-LIBS=$(LIBGPOD_LIBS) -lm
+LIBS=$(LIBGPOD_LIBS) $(Z_LIBS) -lm
 
 uninstall-hook:
  -rmdir --ignore-fail-on-non-empty $(DESTDIR)$(libgpodincludedir)
diff --git a/src/itdb.h b/src/itdb.h
index ddc0cde..8ecfe19 100644
--- a/src/itdb.h
+++ b/src/itdb.h
@@ -1674,7 +1674,8 @@ typedef enum
     ITDB_FILE_ERROR_CORRUPT,
     ITDB_FILE_ERROR_NOTFOUND,
     ITDB_FILE_ERROR_RENAME,
-    ITDB_FILE_ERROR_ITDB_CORRUPT
+    ITDB_FILE_ERROR_ITDB_CORRUPT,
+    ITDB_FILE_ERROR_NOTCOMPRESSED
 } ItdbFileError;
 
 
diff --git a/src/itdb_itunesdb.c b/src/itdb_itunesdb.c
index ed66e98..a87cf5e 100644
--- a/src/itdb_itunesdb.c
+++ b/src/itdb_itunesdb.c
@@ -120,6 +120,9 @@
 #include <string.h>
 #include <sys/types.h>
 #include <time.h>
+#ifdef HAVE_ZLIB
+#include <zlib.h>
+#endif
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
@@ -2924,9 +2927,142 @@ static void error_no_control_dir (const gchar *mp, GError **error)
 }
 #endif
 
+#ifdef HAVE_ZLIB
+/**
+ * itdb_inflate:
+ * @source:    data to be decompressed
+ * @sourcelen: length of input data
+ * @dest:      output data, will be resized using g_realloc
+ * @destlen:   output buffer size
+ * @destoff:   offset in output buffer
+ *
+ * decompress using inflate algorithm as implemented in zlib,
+ * based on inf function in the zlib example zpipe.c
+ *
+ * Returns: TRUE if successful
+ */
+static gboolean
+itdb_inflate(const gchar *source, gsize sourcelen, gchar **dest, gsize *destlen, gsize destoff)
+{
+    const int chunksize = 65536;
+    int ret;
+    z_stream strm;
+
+    if (sourcelen <= 0)
+        return TRUE;
+
+    /* allocate inflate state */
+    strm.zalloc = Z_NULL;
+    strm.zfree = Z_NULL;
+    strm.opaque = Z_NULL;
+    strm.avail_in = 0;
+    strm.next_in = Z_NULL;
+    ret = inflateInit(&strm);
+    if (ret != Z_OK)
+        return FALSE;
+
+    /* decompress until deflate stream ends or end of file */
+    strm.avail_in = sourcelen;
+
+    strm.next_in = (unsigned char *)source;
+
+    *destlen += chunksize;
+    *dest = g_realloc(*dest, *destlen);
+
+    /* run inflate() on input until output buffer not full */
+    do {
+        strm.avail_out = *destlen-destoff;
+        strm.next_out = (unsigned char *)(*dest+destoff);
+        ret = inflate(&strm, Z_NO_FLUSH);
+        g_assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
+        switch (ret) {
+            case Z_NEED_DICT:
+                ret = Z_DATA_ERROR;     /* and fall through */
+            case Z_DATA_ERROR:
+            case Z_MEM_ERROR:
+                (void)inflateEnd(&strm);
+                return FALSE;
+        }
+        destoff = *destlen - strm.avail_out;
+        if (strm.avail_out == 0)
+        {
+            *destlen += chunksize;
+            *dest = g_realloc(*dest, *destlen);
+        }
+    } while (strm.avail_out == 0);
+
+    /* clean up and return */
+    (void)inflateEnd(&strm);
+    return ret == Z_STREAM_END ? TRUE : FALSE;
+}
 
+/**
+ * itdb_deflate:
+ * @source:    data to be compressed
+ * @sourcelen: length of input data
+ * @dest:      output data, will be resized using g_realloc
+ * @destlen:   output buffer size
+ * @destoff:   offset in output buffer
+ *
+ * compress using deflate algorithm as implemented in zlib with compression level 1,
+ * based on inf function in the zlib example zpipe.c
+ *
+ * Returns: TRUE if successful
+ */
 static gboolean
-itdb_parse_internal (Itdb_iTunesDB *itdb, GError **error)
+itdb_deflate(const gchar *source, gsize sourcelen, gchar **dest, gsize *destlen, gsize destoff)
+{
+    const int level = 1;
+    const int chunksize = 65536;
+    int ret, flush;
+    z_stream strm;
+
+    /* allocate deflate state */
+    strm.zalloc = Z_NULL;
+    strm.zfree = Z_NULL;
+    strm.opaque = Z_NULL;
+    ret = deflateInit(&strm, level);
+    if (ret != Z_OK)
+        return FALSE;
+
+    strm.avail_in = sourcelen;
+    strm.next_in = (unsigned char *)source;
+
+    *destlen += chunksize;
+    *dest = g_realloc(*dest, *destlen);
+
+    flush = Z_FINISH;
+
+    /* run deflate() on input until output buffer not full, finish
+       compression if all of source has been read in */
+    do {
+        strm.avail_out = *destlen-destoff;
+        strm.next_out = (unsigned char *)(*dest+destoff);
+        ret = deflate(&strm, flush);    /* no bad return value */
+        g_assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
+        destoff = *destlen - strm.avail_out;
+        if (strm.avail_out == 0)
+        {
+            *destlen += chunksize;
+            *dest = g_realloc(*dest, *destlen);
+        }
+        else
+        {
+            *destlen -= strm.avail_out;
+            *dest = g_realloc(*dest, *destlen);
+        }
+    } while (strm.avail_out == 0);
+    g_assert(strm.avail_in == 0);     /* all input will be used */
+    g_assert(ret == Z_STREAM_END);        /* stream will be complete */
+
+    /* clean up and return */
+    (void)deflateEnd(&strm);
+    return TRUE;
+}
+#endif
+
+static gboolean
+itdb_parse_internal (Itdb_iTunesDB *itdb, gboolean compressed, GError **error)
 {
     FImport *fimp;
     gboolean success = FALSE;
@@ -2936,7 +3072,42 @@ itdb_parse_internal (Itdb_iTunesDB *itdb, GError **error)
     fimp = g_new0 (FImport, 1);
     fimp->itdb = itdb;
 
-    fimp->fcontents = fcontents_read (itdb->filename, error);
+    if (compressed)
+    {
+#ifdef HAVE_ZLIB
+        fimp->fcontents = fcontents_read (itdb->filename, error);
+        if (fimp->fcontents)
+        {
+            gchar *decompressed = NULL;
+            gsize length = 0;
+            FContents *cts = fimp->fcontents;
+            if (!itdb_inflate (cts->contents+188, cts->length-188, &decompressed, &length, 188))
+            {
+                g_set_error (error,
+                        ITDB_FILE_ERROR,
+                        ITDB_FILE_ERROR_NOTCOMPRESSED,
+                        _("File not in compressed format: '%s'."),
+     itdb->filename);
+            }
+            else
+            {
+                g_memmove (decompressed, cts->contents, 188);
+                g_free (cts->contents);
+                cts->contents = decompressed;
+                cts->length = length;
+            }
+        }
+#else
+        g_set_error (error,
+                ITDB_FILE_ERROR,
+                ITDB_FILE_ERROR_NOTCOMPRESSED,
+                _("Decompression not available."));
+#endif
+    }
+    else
+    {
+        fimp->fcontents = fcontents_read (itdb->filename, error);
+    }
 
     if (fimp->fcontents)
     {
@@ -2978,6 +3149,7 @@ Itdb_iTunesDB *itdb_parse (const gchar *mp, GError **error)
     gchar *itunes_dir;
     Itdb_iTunesDB *itdb = NULL;
     const gchar *db[] = {"iTunesDB", NULL};
+    const gchar *cdb[] = {"iTunesCDB", NULL};
 
 
     itunes_dir = itdb_get_itunes_dir (mp);
@@ -2988,7 +3160,12 @@ Itdb_iTunesDB *itdb_parse (const gchar *mp, GError **error)
  return NULL;
     }
 
-    filename = itdb_resolve_path (itunes_dir, db);
+    filename = itdb_resolve_path (itunes_dir, cdb);
+
+    if (!filename)
+    {
+        filename = itdb_resolve_path (itunes_dir, db);
+    }
 
     if (filename)
     {
@@ -3000,7 +3177,7 @@ Itdb_iTunesDB *itdb_parse (const gchar *mp, GError **error)
 
     itdb_set_mountpoint (itdb, mp);
     itdb->filename = filename;
-    success = itdb_parse_internal (itdb, error);
+    success = itdb_parse_internal (itdb, g_str_has_suffix (itdb->filename, "/iTunesCDB"), error);
     if (success)
     {
  /* We don't test the return value of ipod_parse_artwork_db
@@ -3069,7 +3246,7 @@ Itdb_iTunesDB *itdb_parse_file (const gchar *filename, GError **error)
     itdb = itdb_new ();
     itdb->filename = g_strdup (filename);
 
-    success = itdb_parse_internal (itdb, error);
+    success = itdb_parse_internal (itdb, g_str_has_suffix (itdb->filename, "/iTunesCDB"), error);
     if (!success)
     {
  itdb_free (itdb);
@@ -5315,6 +5492,29 @@ gboolean itdb_write_file (Itdb_iTunesDB *itdb, const gchar *filename,
     }
     if (!fexp->error)
     {
+#ifdef HAVE_ZLIB
+        if (g_str_has_suffix (itdb->filename, "/iTunesCDB"))
+        {
+            gchar *compressed = NULL;
+            gsize length = 0;
+            WContents *cts = fexp->wcontents;
+            if (!itdb_deflate (cts->contents+188, cts->total-188, &compressed, &length, 188))
+            {
+                g_set_error (error,
+                        ITDB_FILE_ERROR,
+                        ITDB_FILE_ERROR_NOTCOMPRESSED,
+                        _("Compression failure: '%s'."),
+                        itdb->filename);
+            }
+            else
+            {
+                g_memmove (compressed, cts->contents, 188);
+                g_free (cts->contents);
+                cts->contents = compressed;
+                cts->total = length;
+            }
+        }
+#endif
  if (!wcontents_write (cts))
     g_propagate_error (&fexp->error, cts->error);
     }
--
1.6.2.5



[0002-simple-iTunesCDB-test-convert-between-iTunesDB-and.patch]

From e55da9c80cc981609e34ed8fcd8bf4446aefd6ff Mon Sep 17 00:00:00 2001
From: Martin Aumueller <aumuell@...>
Date: Thu, 16 Jul 2009 09:18:20 +0200
Subject: [PATCH 2/2] simple iTunesCDB test: convert between iTunesDB and iTunesCDB

---
 tests/Makefile.am     |   12 +++++-
 tests/test-compress.c |  115 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 126 insertions(+), 1 deletions(-)
 create mode 100644 tests/test-compress.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index b4bbcc6..6ea0bd9 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -13,6 +13,15 @@ else
 TESTTHUMBS=
 endif
 
+if HAVE_ZLIB
+TESTCOMPRESS=test-compress
+
+test_compress_SOURCES = test-compress.c
+test_compress_LDADD =
+else
+TESTCOMPRESS=
+endif
+
 TESTMISC=test-init-ipod
 
 if HAVE_TAGLIB
@@ -30,6 +39,7 @@ test_itdb_LDADD =
 test_ls_SOURCES = test-ls.c
 test_ls_LDADD =
 
+
 test_init_ipod_SOURCES = test-init-ipod.c
 test_init_ipod_LDADD =
 
@@ -48,7 +58,7 @@ get_timezone_SOURCES = get-timezone.c
 
 noinst_PROGRAMS=test-itdb test-ls test-checksum test-firewire-id \
  test-sysinfo-extended-parsing \
-        $(TESTTHUMBS) $(TESTTAGLIB) $(TESTMISC)
+        $(TESTTHUMBS) $(TESTTAGLIB) $(TESTMISC) $(TESTCOMPRESS)
 
 INCLUDES=$(LIBGPOD_CFLAGS) -I$(top_srcdir)/src -DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\"
 LIBS=$(LIBGPOD_LIBS) $(top_builddir)/src/libgpod.la
diff --git a/tests/test-compress.c b/tests/test-compress.c
new file mode 100644
index 0000000..e693ff7
--- /dev/null
+++ b/tests/test-compress.c
@@ -0,0 +1,115 @@
+/*
+|   Copyright (C) 2002-2003 Jorg Schuler <jcsjcs at users.sourceforge.net>
+|   Copyright (C) 2006 Christophe Fergeau  <teuf@...>
+|
+|   This program is free software; you can redistribute it and/or modify
+|   it under the terms of the GNU General Public License as published by
+|   the Free Software Foundation; either version 2 of the License, or
+|   (at your option) any later version.
+|
+|   This program 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 General Public License for more details.
+|
+|   You should have received a copy of the GNU General Public License
+|   along with this program; 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!
+|
+*/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <libintl.h>
+
+#include "itdb.h"
+
+#define LOCALDB "/.gtkpod/local_0.itdb"
+
+int
+main (int argc, char *argv[])
+{
+  GError *error=NULL;
+  const gchar *homeenv="HOME";
+  Itdb_iTunesDB *itdb;
+  gchar *mountpoint = NULL, *playlistname = NULL;
+
+  if (argc >= 2)
+      mountpoint = argv[1];
+
+    if (argc >= 3)
+      playlistname = argv[2];
+
+  if (mountpoint == NULL)
+  {
+      g_print ("Usage: %s <mountpoint>|-l\n\n",
+                g_basename(argv[0]));
+      exit (0);
+  }
+
+  if (strcmp(mountpoint, "-l") == 0) {
+      mountpoint = g_build_filename(g_getenv(homeenv), LOCALDB, NULL);
+      itdb = itdb_parse_file (mountpoint, &error);
+  }
+  else
+      itdb = itdb_parse (mountpoint, &error);
+  
+  if (error)
+  {
+      if (error->message) {
+  g_print("%s\n", error->message);
+      }
+      g_error_free (error);
+      error = NULL;
+  }
+
+  if (itdb)
+  {
+      gchar *filename = itdb->filename;
+      if (g_str_has_suffix (filename, "/iTunesDB") )
+      {
+          gsize len = strlen (filename);
+          filename = g_strndup (filename, len+1);
+          ++len;
+          filename[len-3] = 'C';
+          filename[len-2] = 'D';
+          filename[len-1] = 'B';
+          filename[len] = '\0';
+      }
+      else if (g_str_has_suffix (filename, "/iTunesCDB") )
+      {
+          gsize len = strlen (filename);
+          filename = g_strdup (filename);
+          --len;
+          filename[len-2] = 'D';
+          filename[len-1] = 'B';
+          filename[len] = '\0';
+      }
+
+      itdb_write_file (itdb, filename, &error);
+
+      if (error)
+      {
+          if (error->message) {
+              g_print("%s\n", error->message);
+          }
+          g_error_free (error);
+          error = NULL;
+      }
+
+      g_free (filename);
+  }
+  itdb_free (itdb);
+
+  return 0;
+}
--
1.6.2.5



------------------------------------------------------------------------------
Enter the BlackBerry Developer Challenge  
This is your chance to win up to $100,000 in prizes! For a limited time,
vendors submitting new applications to BlackBerry App World(TM) will have
the opportunity to enter the BlackBerry Developer Challenge. See full prize  
details at: http://p.sf.net/sfu/Challenge
_______________________________________________
Gtkpod-devel mailing list
Gtkpod-devel@...
https://lists.sourceforge.net/lists/listinfo/gtkpod-devel

Re: Patch: support for deflated iTunesCDB from iPhone OS 3.0

by Bugzilla from aumuell@reserv.at :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

One more thing that I forgot to say in my previous mail: thank you to
Christophe Fergeau for providing the information about iTunesCDB!

On Friday 17 July 2009 00:09:27 Martin Aumueller wrote:

> Hi,
>
> please find attached a patch that
> - makes libgpod prefer iTunesCDB over iTunesDB
> - inflates everything after the 188 header on reading when the file is
> named iTunesCDB
> - deflates everything after the header in the same case
> together with another patch that
> - adds a simple test program that converts between iTunesCDB and iTunesDB.
>
> Please let me know if I should change anything.
>
> Regards,
> Martin

------------------------------------------------------------------------------
Enter the BlackBerry Developer Challenge  
This is your chance to win up to $100,000 in prizes! For a limited time,
vendors submitting new applications to BlackBerry App World(TM) will have
the opportunity to enter the BlackBerry Developer Challenge. See full prize  
details at: http://p.sf.net/sfu/Challenge
_______________________________________________
Gtkpod-devel mailing list
Gtkpod-devel@...
https://lists.sourceforge.net/lists/listinfo/gtkpod-devel