Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support for itunes 10? #10

Closed
josephw opened this issue Mar 13, 2015 · 7 comments
Closed

support for itunes 10? #10

josephw opened this issue Mar 13, 2015 · 7 comments

Comments

@josephw
Copy link
Owner

josephw commented Mar 13, 2015

Original issue 10 created by josephw on 2010-11-02T11:53:08.000Z:

will support for itunes 10 be added?

@josephw
Copy link
Owner Author

josephw commented Mar 13, 2015

Comment #1 originally posted by josephw on 2010-11-05T10:01:15.000Z:

I'd like to add it.

I've seen one problem (unexpected type) with an empty library, which is now fixed. I've also seen a more serious problem with decryption not appearing to work; I've tried to improve the exception here.

Could you post a specific exception?

@josephw
Copy link
Owner Author

josephw commented Mar 13, 2015

Comment #2 originally posted by josephw on 2010-12-07T14:07:18.000Z:

As you asked, the specific exception : "Library format not understood; bad decryption (unhandled type: x�̽)".

For your information, I started a personal project a few months ago (the sources can be found at http://code.google.com/p/mosart/) first it was just to generate a mosaic wallpaper from the cover artworks. Then I planned to add some features like cloud presentation of your music collection based on play count etc.
To do that I need to be able to link a particular artwork to its track. So I integrated your sources to parse the .itl file (first I used the pseudo xml file, but the info is insufficient to achieve what I want).

Cordially

@josephw
Copy link
Owner Author

josephw commented Mar 13, 2015

Comment #3 originally posted by josephw on 2011-01-03T11:55:29.000Z:

It's the decompression stage that's failing. I've now changed it so it throws a better exception (that "unhandled type" is compressed data), but it still fails.

It looks like, around 100kB in, the decryption -> decompression process starts to produce nonsense. I'm not sure what's changing at that point yet.

@josephw
Copy link
Owner Author

josephw commented Mar 13, 2015

Comment #4 originally posted by josephw on 2011-01-18T10:39:09.000Z:

I face the same issue with my iTunes itl. Has any progress been made on this?

@josephw
Copy link
Owner Author

josephw commented Mar 13, 2015

Comment #5 originally posted by josephw on 2011-02-14T20:05:29.000Z:

It appears the first 100kb (+/-) is encrypted with the original key. After that, either the key changes or it isn't encrypted (but still zlib compressed). Anyone know the exact boundary when things stop working? I'm getting a zlib error-- invalid distance code, when decompressing the fully decrpyted file. Will keep exploring.

@josephw
Copy link
Owner Author

josephw commented Mar 13, 2015

Comment #6 originally posted by josephw on 2011-02-14T22:29:34.000Z:

Here are the fixes for iTunes 10:
in Hdfm.java, decrpyt only the first 100K of data (exactly).
The rest is unencrpyted zlib'd data.

// Byte Length Comment
// -----------------------
// 0 4 'hdfm'
// 4 4 L = header length
// 8 4 file length ?
// 12 4 ?
// 13 1 N = length of version string
// 14 N application version string
// 14+N L-N-17 ?
public static Hdfm read(DataInput di, long fileLength) throws IOException, ItlException
{
    int hdr = di.readInt();
    assertEquals("hdfm", Util.toString(hdr));

    int hl = di.readInt();

    int fl = di.readInt();
    if (fileLength != fl)
    {
        throw new IOException("Disk file is " + fileLength + " but header claims " + fl);
    }

    int unknown = di.readInt();

    int vsl = di.readUnsignedByte();
    byte[] avs = new byte[vsl];
    di.readFully(avs);

    String version = new String(avs, "us-ascii");

    int consumed = vsl + 17;

    byte[] headerRemainder = new byte[hl - consumed];
    di.readFully(headerRemainder);

    consumed += headerRemainder.length;

    if (hl != consumed)
    {
        throw new IOException("Header claims to be " + hl + " bytes but read " + consumed);
    }

    // START of changes by supersync
    int majVers = 0;
    try
    {
        majVers = Integer.parseInt(version.substring(0, version.indexOf(".")));
    } catch (Throwable t)
    {
    }

    byte decrypted[];

    if (majVers >= 10)
    {
        // version 10.x of iTunes only encrpyts first 100K of compressed data.
        byte buf[] = new byte[100 * 1024];
        di.readFully(buf);
        consumed += buf.length;
        buf = crypt(buf, Cipher.DECRYPT_MODE);
        byte[] restOfFile = new byte[(int) fileLength - consumed];
        di.readFully(restOfFile);
        decrypted = new byte[buf.length + restOfFile.length];

        System.arraycopy(buf, 0, decrypted, 0, buf.length); 
        System.arraycopy(restOfFile, 0, decrypted, buf.length, restOfFile.length);
    } else
    {
        byte[] restOfFile = new byte[(int) fileLength - consumed];
        di.readFully(restOfFile);
        /* Decrypt */
        decrypted = crypt(restOfFile, Cipher.DECRYPT_MODE);
    }

    /* Unzip (aka inflate, decompress...) */
    byte[] inflated = inflate(decrypted);

    /*
     * If inflate() returned the exact same array, that means the unzip
     * failed, so we should assume that the compression shouldn't be used
     * for this ITL file.
     */
    boolean useCompression = !Arrays.equals(decrypted, inflated);

    return new Hdfm(version, unknown, headerRemainder, inflated, useCompression);
}

In ParseLibrary.java, add a case for 0x2b:

            /* Unknown, but seen */
            case 0x2b:
            case 0x68:
            case 0x69:

...

In HohmPodcast.parse, there is a problem... getting \0\0\0\0 instead of "link" as well as a bad length before it. Util.assertEquals("link", type);

So in ParseLibrary I add code to wrap the call to HohmPodcast.parse in a try/catch and ignore the problem..

            case 0x67: // Podcast info?
                byte[] pcInf = new byte[recLength - consumed];
                // arrayDumpBytes(di, pcInf.length);
                // System.exit(0);
                di.readFully(pcInf);
                // System.out.println(pcInf.length);
                try
                {
                    currentPlaylist.setHohmPodcast(HohmPodcast.parse(new DataInputStream(new ByteArrayInputStream(pcInf)), pcInf.length));
                } catch (Throwable th)
                {
                    th.printStackTrace();
                }
                consumed = recLength;
                break;

Brad
http://supersync.com

@josephw
Copy link
Owner Author

josephw commented Mar 13, 2015

Comment #7 originally posted by josephw on 2011-02-15T13:24:46.000Z:

Thanks!

Good work figuring out the decryption; it's a relief that the change ends up being simple.

@josephw josephw closed this as completed Mar 13, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant